Class: ActiveCipherStorage::Adapters::ActiveStorageService
- Inherits:
-
Object
- Object
- ActiveCipherStorage::Adapters::ActiveStorageService
- Defined in:
- lib/active_cipher_storage/adapters/active_storage_service.rb
Overview
Active Storage service that transparently encrypts uploads and decrypts downloads. Configure in config/storage.yml:
encrypted_s3:
service: ActiveCipherStorage
wrapped_service: s3
Backward compatibility ───────────────────────Blobs uploaded before encryption was enabled are detected via the “ACSx01” magic header. If the magic is absent the raw bytes are returned as-is, so the service is safe to enable on a bucket with existing plaintext objects.
Range requests (download_chunk) must decrypt the full blob first because GCM authentication requires the complete ciphertext before any plaintext can be safely released.
Defined Under Namespace
Classes: BlobRef
Instance Attribute Summary collapse
-
#inner ⇒ Object
readonly
Returns the value of attribute inner.
Class Method Summary collapse
Instance Method Summary collapse
- #delete(key) ⇒ Object
- #delete_prefixed(pfx) ⇒ Object
- #download(key, &block) ⇒ Object
- #download_chunk(key, range) ⇒ Object
-
#download_raw(key) ⇒ Object
Used by KeyRotation to fetch raw ciphertext without decrypting.
- #exist?(key) ⇒ Boolean
- #headers_for_direct_upload ⇒ Object
-
#initialize(wrapped_service:, **_kwargs) ⇒ ActiveStorageService
constructor
A new instance of ActiveStorageService.
-
#rekey(key, old_provider:, new_provider:) ⇒ Object
Re-wraps the DEK in a single blob’s header under new_provider without decrypting or re-encrypting the file body.
- #upload(key, io, checksum: nil, content_type: nil, filename: nil, disposition: nil, custom_metadata: {}) ⇒ Object
-
#upload_raw(key, io) ⇒ Object
Used by KeyRotation to overwrite a blob’s bytes without re-encrypting.
- #url(key, expires_in:, filename:, content_type:, disposition:) ⇒ Object
- #url_for_direct_upload ⇒ Object
Constructor Details
#initialize(wrapped_service:, **_kwargs) ⇒ ActiveStorageService
Returns a new instance of ActiveStorageService.
28 29 30 31 32 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 28 def initialize(wrapped_service:, **_kwargs) @inner = wrapped_service @cipher = Cipher.new @stream_cipher = StreamCipher.new end |
Instance Attribute Details
#inner ⇒ Object (readonly)
Returns the value of attribute inner.
22 23 24 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 22 def inner @inner end |
Class Method Details
.build(configurator:, wrapped_service:, **kwargs) ⇒ Object
24 25 26 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 24 def self.build(configurator:, wrapped_service:, **kwargs) new(wrapped_service: configurator.build(wrapped_service), **kwargs) end |
Instance Method Details
#delete(key) ⇒ Object
81 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 81 def delete(key) = @inner.delete(key) |
#delete_prefixed(pfx) ⇒ Object
82 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 82 def delete_prefixed(pfx) = @inner.delete_prefixed(pfx) |
#download(key, &block) ⇒ Object
46 47 48 49 50 51 52 53 54 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 46 def download(key, &block) raw = collect_download(key) # Legacy plaintext blob — no magic header present. return (block ? yield(raw) : raw) unless cipher_payload?(raw) plaintext = decrypt_raw(raw) block ? yield(plaintext) : plaintext end |
#download_chunk(key, range) ⇒ Object
56 57 58 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 56 def download_chunk(key, range) download(key).b[range] end |
#download_raw(key) ⇒ Object
Used by KeyRotation to fetch raw ciphertext without decrypting.
61 62 63 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 61 def download_raw(key) collect_download(key) end |
#exist?(key) ⇒ Boolean
83 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 83 def exist?(key) = @inner.exist?(key) |
#headers_for_direct_upload ⇒ Object
95 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 95 def headers_for_direct_upload(*) = {} |
#rekey(key, old_provider:, new_provider:) ⇒ Object
Re-wraps the DEK in a single blob’s header under new_provider without decrypting or re-encrypting the file body.
72 73 74 75 76 77 78 79 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 72 def rekey(key, old_provider:, new_provider:) KeyRotation.rotate_blob( BlobRef.new(key), old_provider: old_provider, new_provider: new_provider, service: self ) end |
#upload(key, io, checksum: nil, content_type: nil, filename: nil, disposition: nil, custom_metadata: {}) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 34 def upload(key, io, checksum: nil, content_type: nil, filename: nil, disposition: nil, custom_metadata: {}) @inner.upload(key, encrypt_io(io), checksum: nil, # checksum is over plaintext; skip for ciphertext content_type: "application/octet-stream", filename: filename, disposition: disposition, custom_metadata: ) BlobMetadata.write(key, ActiveCipherStorage.configuration.provider) end |
#upload_raw(key, io) ⇒ Object
Used by KeyRotation to overwrite a blob’s bytes without re-encrypting.
66 67 68 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 66 def upload_raw(key, io) @inner.upload(key, io, content_type: "application/octet-stream") end |
#url(key, expires_in:, filename:, content_type:, disposition:) ⇒ Object
85 86 87 88 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 85 def url(key, expires_in:, filename:, content_type:, disposition:, **) @inner.url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition) end |
#url_for_direct_upload ⇒ Object
90 91 92 93 |
# File 'lib/active_cipher_storage/adapters/active_storage_service.rb', line 90 def url_for_direct_upload(*) raise Errors::UnsupportedOperation, "Direct uploads bypass encryption — use server-side upload instead" end |