Class: Service::ActiveCipherStorageService

Inherits:
Service
  • Object
show all
Defined in:
lib/active_storage/service/active_cipher_storage_service.rb

Overview

Encrypting wrapper around another Active Storage service (e.g. S3). Configure in config/storage.yml with service: ActiveCipherStorage and wrapped_service: pointing at a named service.

See the gem README for behavior and caveats.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wrapped_service:, public: nil, chunk_size: ActiveCipherStorage::Configuration::DEFAULT_CHUNK_SIZE) ⇒ ActiveCipherStorageService

Returns a new instance of ActiveCipherStorageService.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 27

def initialize(wrapped_service:, public: nil,
               chunk_size: ActiveCipherStorage::Configuration::DEFAULT_CHUNK_SIZE, **)
  @inner = wrapped_service
  @chunk_size    = Integer(chunk_size)
  @cipher        = ActiveCipherStorage::Cipher.new
  @stream_cipher = ActiveCipherStorage::StreamCipher.new(chunk_size: @chunk_size)
  @public =
    if public.nil?
      wrapped_service.respond_to?(:public?) ? wrapped_service.public? : false
    else
      public
    end
end

Instance Attribute Details

#innerObject (readonly)

Returns the value of attribute inner.



19
20
21
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 19

def inner
  @inner
end

Class Method Details

.build(configurator:, name:, wrapped_service:, service: nil, **service_config) ⇒ Object



21
22
23
24
25
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 21

def self.build(configurator:, name:, wrapped_service:, service: nil, **service_config)
  new(wrapped_service: configurator.build(wrapped_service), **service_config).tap do |instance|
    instance.name = name.to_s
  end
end

Instance Method Details

#compose(source_keys, destination_key, **options) ⇒ Object



102
103
104
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 102

def compose(source_keys, destination_key, **options)
  inner.compose(source_keys, destination_key, **options)
end

#delete(key) ⇒ Object



78
79
80
81
82
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 78

def delete(key)
  instrument :delete, key: key do
    inner.delete(key)
  end
end

#delete_prefixed(prefix) ⇒ Object



84
85
86
87
88
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 84

def delete_prefixed(prefix)
  instrument :delete_prefixed, prefix: prefix do
    inner.delete_prefixed(prefix)
  end
end

#download(key, &block) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 47

def download(key, &block)
  if block_given?
    instrument :streaming_download, key: key do
      plain = download_without_block(key)
      yield plain
    end
  else
    instrument :download, key: key do
      download_without_block(key)
    end
  end
end

#download_chunk(key, range) ⇒ Object



60
61
62
63
64
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 60

def download_chunk(key, range)
  instrument :download_chunk, key: key, range: range do
    download_without_block(key).b[range]
  end
end

#download_raw(key) ⇒ Object

Returns stored ciphertext bytes (for custom tooling, backups, or migrations).



67
68
69
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 67

def download_raw(key)
  collect_download(key)
end

#exist?(key) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
96
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 90

def exist?(key)
  instrument :exist, key: key do |payload|
    answer = inner.exist?(key)
    payload[:exist] = answer
    answer
  end
end

#headers_for_direct_uploadObject



121
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 121

def headers_for_direct_upload(*) = {}

#path_for(key) ⇒ Object



106
107
108
109
110
111
112
113
114
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 106

def path_for(key)
  unless inner.respond_to?(:path_for)
    raise NotImplementedError,
          "#{inner.class.name} does not implement path_for — use a disk-backed " \
          "inner service if you need local paths"
  end

  inner.path_for(key)
end

#update_metadata(key, **metadata) ⇒ Object



98
99
100
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 98

def (key, **)
  inner.(key, **)
end

#upload(key, io, checksum: nil, **options) ⇒ Object



41
42
43
44
45
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 41

def upload(key, io, checksum: nil, **options)
  instrument :upload, key: key, checksum: checksum do
    upload_without_instrument(key, io, checksum: checksum, **options)
  end
end

#upload_raw(key, io) ⇒ Object

Writes raw ciphertext bytes without encrypting (for advanced use only).



72
73
74
75
76
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 72

def upload_raw(key, io)
  instrument :upload, key: key, checksum: nil do
    inner.upload(key, io, content_type: "application/octet-stream")
  end
end

#url_for_direct_uploadObject



116
117
118
119
# File 'lib/active_storage/service/active_cipher_storage_service.rb', line 116

def url_for_direct_upload(*)
  raise ActiveCipherStorage::Errors::UnsupportedOperation,
        "Direct uploads bypass encryption — use server-side upload instead"
end