active_storage_encryption 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Appraisals +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +236 -0
- data/Rakefile +17 -0
- data/bin/rails +26 -0
- data/bin/rubocop +8 -0
- data/config/initializers/active_storage_encryption.rb +9 -0
- data/config/routes.rb +7 -0
- data/gemfiles/rails_7.gemfile +7 -0
- data/gemfiles/rails_7.gemfile.lock +276 -0
- data/gemfiles/rails_8.gemfile +7 -0
- data/gemfiles/rails_8.gemfile.lock +276 -0
- data/lib/active_storage/service/encrypted_disk_service.rb +10 -0
- data/lib/active_storage/service/encrypted_mirror_service.rb +10 -0
- data/lib/active_storage/service/encrypted_s3_service.rb +10 -0
- data/lib/active_storage_encryption/encrypted_blobs_controller.rb +163 -0
- data/lib/active_storage_encryption/encrypted_disk_service/v1_scheme.rb +28 -0
- data/lib/active_storage_encryption/encrypted_disk_service/v2_scheme.rb +51 -0
- data/lib/active_storage_encryption/encrypted_disk_service.rb +186 -0
- data/lib/active_storage_encryption/encrypted_mirror_service.rb +76 -0
- data/lib/active_storage_encryption/encrypted_s3_service.rb +236 -0
- data/lib/active_storage_encryption/engine.rb +7 -0
- data/lib/active_storage_encryption/overrides.rb +201 -0
- data/lib/active_storage_encryption/private_url_policy.rb +53 -0
- data/lib/active_storage_encryption/resumable_gcs_upload.rb +194 -0
- data/lib/active_storage_encryption/version.rb +5 -0
- data/lib/active_storage_encryption.rb +79 -0
- data/lib/tasks/active_storage_encryption_tasks.rake +6 -0
- data/test/active_storage_encryption_test.rb +9 -0
- data/test/dummy/Rakefile +8 -0
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/helpers/application_helper.rb +4 -0
- data/test/dummy/app/models/application_record.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +22 -0
- data/test/dummy/app/views/pwa/manifest.json.erb +22 -0
- data/test/dummy/app/views/pwa/service-worker.js +26 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +37 -0
- data/test/dummy/config/application.rb +43 -0
- data/test/dummy/config/boot.rb +7 -0
- data/test/dummy/config/credentials.yml.enc +1 -0
- data/test/dummy/config/database.yml +32 -0
- data/test/dummy/config/environment.rb +7 -0
- data/test/dummy/config/environments/development.rb +59 -0
- data/test/dummy/config/environments/production.rb +81 -0
- data/test/dummy/config/environments/test.rb +53 -0
- data/test/dummy/config/initializers/content_security_policy.rb +27 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +10 -0
- data/test/dummy/config/initializers/inflections.rb +18 -0
- data/test/dummy/config/initializers/permissions_policy.rb +15 -0
- data/test/dummy/config/locales/en.yml +31 -0
- data/test/dummy/config/master.key +1 -0
- data/test/dummy/config/puma.rb +36 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/storage.yml +21 -0
- data/test/dummy/config.ru +8 -0
- data/test/dummy/db/migrate/20250304023851_create_active_storage_tables.active_storage.rb +60 -0
- data/test/dummy/db/migrate/20250304023853_add_blob_encryption_key_column.rb +7 -0
- data/test/dummy/db/schema.rb +47 -0
- data/test/dummy/log/test.log +1022 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/406-unsupported-browser.html +66 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/icon.png +0 -0
- data/test/dummy/public/icon.svg +3 -0
- data/test/dummy/storage/test.sqlite3 +0 -0
- data/test/dummy/storage/x6/pl/x6plznfuhrsyjn9pox2a6xgmcs3x +0 -0
- data/test/dummy/storage/yq/sv/yqsvw5a72b3fv719zq8a6yb7lv0j +0 -0
- data/test/integration/encrypted_blobs_controller_test.rb +400 -0
- data/test/lib/encrypted_disk_service_test.rb +387 -0
- data/test/lib/encrypted_mirror_service_test.rb +159 -0
- data/test/lib/encrypted_s3_service_test.rb +293 -0
- data/test/test_helper.rb +19 -0
- metadata +264 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
class ActiveStorageEncryption::EncryptedS3ServiceTest < ActiveSupport::TestCase
|
7
|
+
def config
|
8
|
+
{
|
9
|
+
access_key_id: ENV.fetch("AWS_ACCESS_KEY_ID"),
|
10
|
+
secret_access_key: ENV.fetch("AWS_SECRET_ACCESS_KEY"),
|
11
|
+
region: "eu-central-1",
|
12
|
+
bucket: "active-storage-encryption-test-bucket"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
setup do
|
17
|
+
if ENV["AWS_ACCESS_KEY_ID"].blank? || ENV["AWS_SECRET_ACCESS_KEY"].blank?
|
18
|
+
skip "You need AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY set in your env to test the EncryptedS3Service"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
setup do
|
23
|
+
require "active_storage/service/s3_service"
|
24
|
+
@service = ActiveStorageEncryption::EncryptedS3Service.new(**config)
|
25
|
+
@service.name = "amazing_encrypting_s3_service" # Needed for the DiskController and service lookup
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_id
|
29
|
+
# We use a shared S3 bucket, and multiple runs of the test suite may write into it at the same time.
|
30
|
+
# To prevent clobbering and conflicts, assign a "test run ID" and mix it into the object keys. Keep that
|
31
|
+
# value stable across the test suite.
|
32
|
+
@test_suite_run_id ||= SecureRandom.base36(10)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_encrypted_question_method
|
36
|
+
assert @service.encrypted?
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_forbids_private_urls_with_disabled_policy
|
40
|
+
@service.private_url_policy = :disable
|
41
|
+
|
42
|
+
rng = Random.new(Minitest.seed)
|
43
|
+
key = "#{run_id}-streamed-key-#{rng.hex(4)}"
|
44
|
+
k = Random.bytes(68)
|
45
|
+
plaintext_upload_bytes = rng.bytes(425)
|
46
|
+
@service.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key: k)
|
47
|
+
|
48
|
+
# ActiveStorage wraps the passed filename in a wrapper thingy
|
49
|
+
filename_with_sanitization = ActiveStorage::Filename.new("temp.bin")
|
50
|
+
|
51
|
+
assert_raises(ActiveStorageEncryption::StreamingDisabled) do
|
52
|
+
@service.url(key, filename: filename_with_sanitization, content_type: "binary/octet-stream", disposition: "inline", encryption_key: k, expires_in: 10.seconds)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_generates_private_streaming_urls_with_streaming_policy
|
57
|
+
@service.private_url_policy = :stream
|
58
|
+
|
59
|
+
rng = Random.new(Minitest.seed)
|
60
|
+
key = "#{run_id}-streamed-key-#{rng.hex(4)}"
|
61
|
+
k = Random.bytes(68)
|
62
|
+
plaintext_upload_bytes = rng.bytes(425)
|
63
|
+
@service.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key: k)
|
64
|
+
|
65
|
+
# The streaming URL generation uses Rails routing, so it needs
|
66
|
+
# ActiveStorage::Current.url_options to be set
|
67
|
+
# We need to use a hostname for ActiveStorage which is in the Rails authorized hosts.
|
68
|
+
# see https://stackoverflow.com/a/60573259/153886
|
69
|
+
ActiveStorage::Current.url_options = {
|
70
|
+
host: "www.example.com",
|
71
|
+
protocol: "https"
|
72
|
+
}
|
73
|
+
|
74
|
+
# ActiveStorage wraps the passed filename in a wrapper thingy
|
75
|
+
filename_with_sanitization = ActiveStorage::Filename.new("temp.bin")
|
76
|
+
url = @service.url(key, filename: filename_with_sanitization, content_type: "binary/octet-stream", disposition: "inline", encryption_key: k, expires_in: 10.seconds)
|
77
|
+
assert url.include?("/active-storage-encryption/blob/")
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_generates_private_urls_with_require_headers_policy
|
81
|
+
@service.private_url_policy = :require_headers
|
82
|
+
|
83
|
+
rng = Random.new(Minitest.seed)
|
84
|
+
key = "#{run_id}-streamed-key-#{rng.hex(4)}"
|
85
|
+
k = Random.bytes(68)
|
86
|
+
plaintext_upload_bytes = rng.bytes(425)
|
87
|
+
@service.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key: k)
|
88
|
+
|
89
|
+
# ActiveStorage wraps the passed filename in a wrapper thingy
|
90
|
+
filename_with_sanitization = ActiveStorage::Filename.new("temp.bin")
|
91
|
+
url = @service.url(key, filename: filename_with_sanitization, content_type: "binary/octet-stream", disposition: "inline", encryption_key: k, expires_in: 240.seconds)
|
92
|
+
|
93
|
+
assert url.include?("x-amz-server-side-encryption-customer-algorithm")
|
94
|
+
refute url.include?("x-amz-server-side-encryption-customer-key=") # The key should not be in the URL
|
95
|
+
|
96
|
+
uri = URI(url)
|
97
|
+
req = Net::HTTP::Get.new(uri)
|
98
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http|
|
99
|
+
http.request(req)
|
100
|
+
}
|
101
|
+
assert_equal "400", res.code
|
102
|
+
|
103
|
+
headers = @service.headers_for_private_download(key, encryption_key: k)
|
104
|
+
headers.each_pair do |h, v|
|
105
|
+
req[h] = v
|
106
|
+
end
|
107
|
+
|
108
|
+
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http|
|
109
|
+
http.request(req)
|
110
|
+
}
|
111
|
+
assert_equal "200", res.code
|
112
|
+
assert_equal plaintext_upload_bytes, res.body
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_s3_config_sane_and_works_with_stock_service
|
116
|
+
# maybe remove later
|
117
|
+
stock_s3_service = ActiveStorage::Service::S3Service.new(**config)
|
118
|
+
rng = Random.new(Minitest.seed)
|
119
|
+
key = "#{run_id}-unencrypted-key-#{rng.hex(4)}"
|
120
|
+
plaintext_upload_bytes = rng.bytes(1024)
|
121
|
+
assert_nothing_raised do
|
122
|
+
stock_s3_service.upload(key, StringIO.new(plaintext_upload_bytes))
|
123
|
+
end
|
124
|
+
readback = stock_s3_service.download(key)
|
125
|
+
assert_equal readback, plaintext_upload_bytes
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_exists
|
129
|
+
rng = Random.new(Minitest.seed)
|
130
|
+
|
131
|
+
key = "#{run_id}-encrypted-exists-key-#{rng.hex(4)}"
|
132
|
+
encryption_key = rng.bytes(47) # Make it bigger than required, to ensure the service truncates it
|
133
|
+
plaintext_upload_bytes = rng.bytes(1024)
|
134
|
+
|
135
|
+
assert_nothing_raised { @service.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key:) }
|
136
|
+
refute @service.exist?(key + "-definitely-not-present")
|
137
|
+
assert @service.exist?(key)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_basic_s3_readback
|
141
|
+
rng = Random.new(Minitest.seed)
|
142
|
+
|
143
|
+
key = "#{run_id}-encrypted-key-#{rng.hex(4)}"
|
144
|
+
encryption_key = rng.bytes(47) # Make it bigger than required, to ensure the service truncates it
|
145
|
+
plaintext_upload_bytes = rng.bytes(1024)
|
146
|
+
|
147
|
+
assert_nothing_raised do
|
148
|
+
@service.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key:)
|
149
|
+
end
|
150
|
+
readback = @service.download(key, encryption_key:)
|
151
|
+
assert_equal readback, plaintext_upload_bytes
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_s3_upload_requiring_multipart
|
155
|
+
rng = Random.new(Minitest.seed)
|
156
|
+
encryption_key = rng.bytes(47) # Make it bigger than required, to ensure the service truncates it
|
157
|
+
|
158
|
+
# The minimum multipart part size is 5MB
|
159
|
+
multipart_threshold = 1024 * 1024 * 5
|
160
|
+
total_size = multipart_threshold + 3
|
161
|
+
plaintext_upload_bytes = rng.bytes(total_size)
|
162
|
+
|
163
|
+
key = "#{run_id}-encrypted-key-#{rng.hex(4)}"
|
164
|
+
service_with_smaller_part_size = ActiveStorageEncryption::EncryptedS3Service.new(**config, upload: {multipart_threshold:})
|
165
|
+
assert_nothing_raised do
|
166
|
+
service_with_smaller_part_size.upload(key, StringIO.new(plaintext_upload_bytes), encryption_key:)
|
167
|
+
end
|
168
|
+
|
169
|
+
readback = service_with_smaller_part_size.download(key, encryption_key:)
|
170
|
+
assert_equal total_size, readback.bytesize
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_accepts_direct_upload_with_signature_and_headers
|
174
|
+
rng = Random.new(Minitest.seed)
|
175
|
+
|
176
|
+
key = "#{run_id}-encrypted-key-direct-upload-#{rng.hex(4)}"
|
177
|
+
encryption_key = rng.bytes(47) # Make it bigger than required, to ensure the service truncates it
|
178
|
+
plaintext_upload_bytes = rng.bytes(1024)
|
179
|
+
|
180
|
+
url = @service.url_for_direct_upload(key,
|
181
|
+
encryption_key:,
|
182
|
+
expires_in: 1.minute,
|
183
|
+
content_type: "binary/octet-stream",
|
184
|
+
content_length: plaintext_upload_bytes.bytesize,
|
185
|
+
checksum: Digest::MD5.base64digest(plaintext_upload_bytes))
|
186
|
+
headers = @service.headers_for_direct_upload(key,
|
187
|
+
encryption_key:,
|
188
|
+
content_type: "binary/octet-stream",
|
189
|
+
content_length: plaintext_upload_bytes.bytesize,
|
190
|
+
checksum: Digest::MD5.base64digest(plaintext_upload_bytes))
|
191
|
+
|
192
|
+
refute url.include?("x-amz-server-side-encryption-customer-key=") # The key should not be in the URL
|
193
|
+
assert url.include?("x-amz-server-side-encryption-customer-key-md5=") # The checksum must be in the URL
|
194
|
+
|
195
|
+
res = Net::HTTP.put(URI(url), plaintext_upload_bytes, headers)
|
196
|
+
assert_equal "200", res.code
|
197
|
+
|
198
|
+
assert_equal plaintext_upload_bytes, @service.download(key, encryption_key:)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_rejects_direct_upload_if_client_manipulates_the_encryption_key
|
202
|
+
skip "Currently does not work, investigate"
|
203
|
+
|
204
|
+
rng = Random.new(Minitest.seed)
|
205
|
+
|
206
|
+
key = "#{run_id}-encrypted-key-direct-upload-#{rng.hex(4)}"
|
207
|
+
encryption_key = rng.bytes(47) # Make it bigger than required, to ensure the service truncates it
|
208
|
+
plaintext_upload_bytes = rng.bytes(1024)
|
209
|
+
|
210
|
+
url = @service.url_for_direct_upload(key,
|
211
|
+
encryption_key:,
|
212
|
+
expires_in: 1.minute,
|
213
|
+
content_type: "binary/octet-stream",
|
214
|
+
content_length: plaintext_upload_bytes.bytesize,
|
215
|
+
checksum: Digest::MD5.base64digest(plaintext_upload_bytes))
|
216
|
+
headers = @service.headers_for_direct_upload(key,
|
217
|
+
encryption_key:,
|
218
|
+
content_type: "binary/octet-stream",
|
219
|
+
content_length: plaintext_upload_bytes.bytesize,
|
220
|
+
checksum: Digest::MD5.base64digest(plaintext_upload_bytes))
|
221
|
+
|
222
|
+
# Replace the key and its checksum
|
223
|
+
other_key = Random.bytes(32)
|
224
|
+
fake_headers = headers.merge({
|
225
|
+
"x-amz-server-side-encryption-customer-key" => Base64.strict_encode64(other_key),
|
226
|
+
"x-amz-server-side-encryption-customer-key-MD5" => Digest::MD5.base64digest(other_key)
|
227
|
+
})
|
228
|
+
res = Net::HTTP.put(URI(url), plaintext_upload_bytes, fake_headers)
|
229
|
+
refute_equal "200", res.code
|
230
|
+
end
|
231
|
+
|
232
|
+
# Read the objects from something slow, so that threads may switch between one another
|
233
|
+
class SnoozyStringIO < StringIO
|
234
|
+
def read(n = nil, outbuf = nil)
|
235
|
+
sleep(rand((0.1..0.2)))
|
236
|
+
super
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_uploads_correctly_across_multiple_threads
|
241
|
+
# Due to a hack that we are applying to reuse most of the stock S3Service, we
|
242
|
+
# temporarily override @upload_options on the service when an upload is in progress.
|
243
|
+
# This must be done in a thread-local manner, otherwise some uploads may, potentially,
|
244
|
+
# get uploaded with the wrong encryption key - belonging to an upload from a different
|
245
|
+
# thread. While a test like this is by no means exhaustive, it should reveal this
|
246
|
+
# race condition if it occurs.
|
247
|
+
rng = Random.new(Minitest.seed)
|
248
|
+
objects = 12.times.map do |n|
|
249
|
+
key = "#{run_id}-threaded-upload-#{n}-#{rng.hex(4)}"
|
250
|
+
encryption_key = rng.bytes(32)
|
251
|
+
bytes = rng.bytes(512)
|
252
|
+
{key:, encryption_key:, io: SnoozyStringIO.new(bytes)}
|
253
|
+
end
|
254
|
+
|
255
|
+
threads = objects.map do |o|
|
256
|
+
Thread.new do
|
257
|
+
@service.upload(o.fetch(:key), o.fetch(:io), encryption_key: o.fetch(:encryption_key))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
threads.map(&:join)
|
261
|
+
|
262
|
+
objects.each do |o|
|
263
|
+
readback = @service.download(o.fetch(:key), encryption_key: o.fetch(:encryption_key))
|
264
|
+
assert_equal o.fetch(:io).string, readback
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_composes_objects
|
269
|
+
rng = Random.new(Minitest.seed)
|
270
|
+
|
271
|
+
key1 = "#{run_id}-to-compose-key-1-#{rng.hex(4)}"
|
272
|
+
k1 = rng.bytes(68)
|
273
|
+
buf1 = rng.bytes(1024 * 7)
|
274
|
+
|
275
|
+
key2 = "#{run_id}-to-compose-key-2-#{rng.hex(4)}"
|
276
|
+
k2 = rng.bytes(68)
|
277
|
+
buf2 = rng.bytes(1024 * 3)
|
278
|
+
|
279
|
+
assert_nothing_raised do
|
280
|
+
@service.upload(key1, StringIO.new(buf1), encryption_key: k1)
|
281
|
+
@service.upload(key2, StringIO.new(buf2), encryption_key: k2)
|
282
|
+
end
|
283
|
+
|
284
|
+
composed_key = "#{run_id}-composed-key-3-#{rng.hex(4)}"
|
285
|
+
k3 = Random.bytes(68)
|
286
|
+
assert_nothing_raised do
|
287
|
+
@service.compose([key1, key2], composed_key, source_encryption_keys: [k1, k2], encryption_key: k3, content_type: "binary/octet-stream")
|
288
|
+
end
|
289
|
+
|
290
|
+
readback_composed_bytes = @service.download(composed_key, encryption_key: k3)
|
291
|
+
assert_equal Digest::SHA256.hexdigest(buf1 + buf2), Digest::SHA256.hexdigest(readback_composed_bytes)
|
292
|
+
end
|
293
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configure Rails Environment
|
4
|
+
ENV["RAILS_ENV"] = "test"
|
5
|
+
|
6
|
+
require_relative "../test/dummy/config/environment"
|
7
|
+
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
|
8
|
+
ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__)
|
9
|
+
require "rails/test_help"
|
10
|
+
|
11
|
+
require "minitest/mock"
|
12
|
+
|
13
|
+
# Load fixtures from the engine
|
14
|
+
if ActiveSupport::TestCase.respond_to?(:fixture_paths=)
|
15
|
+
ActiveSupport::TestCase.fixture_paths = [File.expand_path("fixtures", __dir__)]
|
16
|
+
ActionDispatch::IntegrationTest.fixture_paths = ActiveSupport::TestCase.fixture_paths
|
17
|
+
ActiveSupport::TestCase.file_fixture_path = File.expand_path("fixtures", __dir__) + "/files"
|
18
|
+
ActiveSupport::TestCase.fixtures :all
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_storage_encryption
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julik Tarkhanov
|
8
|
+
- Sebastian van Hesteren
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2025-03-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 7.2.2.1
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 7.2.2.1
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: block_cipher_kit
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.0.4
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.0.4
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: activestorage
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: aws-sdk-s3
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: net-http
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: sqlite3
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: standard
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.35.1
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.35.1
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: appraisal
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: magic_frozen_string_literal
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: rake
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
description: Adds customer-supplied encryption keys to storage services.
|
155
|
+
email:
|
156
|
+
- me@julik.nl
|
157
|
+
executables: []
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- Appraisals
|
162
|
+
- MIT-LICENSE
|
163
|
+
- README.md
|
164
|
+
- Rakefile
|
165
|
+
- bin/rails
|
166
|
+
- bin/rubocop
|
167
|
+
- config/initializers/active_storage_encryption.rb
|
168
|
+
- config/routes.rb
|
169
|
+
- gemfiles/rails_7.gemfile
|
170
|
+
- gemfiles/rails_7.gemfile.lock
|
171
|
+
- gemfiles/rails_8.gemfile
|
172
|
+
- gemfiles/rails_8.gemfile.lock
|
173
|
+
- lib/active_storage/service/encrypted_disk_service.rb
|
174
|
+
- lib/active_storage/service/encrypted_mirror_service.rb
|
175
|
+
- lib/active_storage/service/encrypted_s3_service.rb
|
176
|
+
- lib/active_storage_encryption.rb
|
177
|
+
- lib/active_storage_encryption/encrypted_blobs_controller.rb
|
178
|
+
- lib/active_storage_encryption/encrypted_disk_service.rb
|
179
|
+
- lib/active_storage_encryption/encrypted_disk_service/v1_scheme.rb
|
180
|
+
- lib/active_storage_encryption/encrypted_disk_service/v2_scheme.rb
|
181
|
+
- lib/active_storage_encryption/encrypted_mirror_service.rb
|
182
|
+
- lib/active_storage_encryption/encrypted_s3_service.rb
|
183
|
+
- lib/active_storage_encryption/engine.rb
|
184
|
+
- lib/active_storage_encryption/overrides.rb
|
185
|
+
- lib/active_storage_encryption/private_url_policy.rb
|
186
|
+
- lib/active_storage_encryption/resumable_gcs_upload.rb
|
187
|
+
- lib/active_storage_encryption/version.rb
|
188
|
+
- lib/tasks/active_storage_encryption_tasks.rake
|
189
|
+
- test/active_storage_encryption_test.rb
|
190
|
+
- test/dummy/Rakefile
|
191
|
+
- test/dummy/app/assets/stylesheets/application.css
|
192
|
+
- test/dummy/app/controllers/application_controller.rb
|
193
|
+
- test/dummy/app/helpers/application_helper.rb
|
194
|
+
- test/dummy/app/models/application_record.rb
|
195
|
+
- test/dummy/app/views/layouts/application.html.erb
|
196
|
+
- test/dummy/app/views/pwa/manifest.json.erb
|
197
|
+
- test/dummy/app/views/pwa/service-worker.js
|
198
|
+
- test/dummy/bin/rails
|
199
|
+
- test/dummy/bin/rake
|
200
|
+
- test/dummy/bin/setup
|
201
|
+
- test/dummy/config.ru
|
202
|
+
- test/dummy/config/application.rb
|
203
|
+
- test/dummy/config/boot.rb
|
204
|
+
- test/dummy/config/credentials.yml.enc
|
205
|
+
- test/dummy/config/database.yml
|
206
|
+
- test/dummy/config/environment.rb
|
207
|
+
- test/dummy/config/environments/development.rb
|
208
|
+
- test/dummy/config/environments/production.rb
|
209
|
+
- test/dummy/config/environments/test.rb
|
210
|
+
- test/dummy/config/initializers/content_security_policy.rb
|
211
|
+
- test/dummy/config/initializers/filter_parameter_logging.rb
|
212
|
+
- test/dummy/config/initializers/inflections.rb
|
213
|
+
- test/dummy/config/initializers/permissions_policy.rb
|
214
|
+
- test/dummy/config/locales/en.yml
|
215
|
+
- test/dummy/config/master.key
|
216
|
+
- test/dummy/config/puma.rb
|
217
|
+
- test/dummy/config/routes.rb
|
218
|
+
- test/dummy/config/storage.yml
|
219
|
+
- test/dummy/db/migrate/20250304023851_create_active_storage_tables.active_storage.rb
|
220
|
+
- test/dummy/db/migrate/20250304023853_add_blob_encryption_key_column.rb
|
221
|
+
- test/dummy/db/schema.rb
|
222
|
+
- test/dummy/log/test.log
|
223
|
+
- test/dummy/public/404.html
|
224
|
+
- test/dummy/public/406-unsupported-browser.html
|
225
|
+
- test/dummy/public/422.html
|
226
|
+
- test/dummy/public/500.html
|
227
|
+
- test/dummy/public/icon.png
|
228
|
+
- test/dummy/public/icon.svg
|
229
|
+
- test/dummy/storage/test.sqlite3
|
230
|
+
- test/dummy/storage/x6/pl/x6plznfuhrsyjn9pox2a6xgmcs3x
|
231
|
+
- test/dummy/storage/yq/sv/yqsvw5a72b3fv719zq8a6yb7lv0j
|
232
|
+
- test/integration/encrypted_blobs_controller_test.rb
|
233
|
+
- test/lib/encrypted_disk_service_test.rb
|
234
|
+
- test/lib/encrypted_mirror_service_test.rb
|
235
|
+
- test/lib/encrypted_s3_service_test.rb
|
236
|
+
- test/test_helper.rb
|
237
|
+
homepage: https://github.com/cheddar-me/active_storage_encryption
|
238
|
+
licenses:
|
239
|
+
- MIT
|
240
|
+
metadata:
|
241
|
+
allowed_push_host: https://rubygems.org
|
242
|
+
homepage_uri: https://github.com/cheddar-me/active_storage_encryption
|
243
|
+
source_code_uri: https://github.com/cheddar-me/active_storage_encryption
|
244
|
+
changelog_uri: https://github.com/cheddar-me/active_storage_encryption/blob/main/CHANGELOG.md
|
245
|
+
post_install_message:
|
246
|
+
rdoc_options: []
|
247
|
+
require_paths:
|
248
|
+
- lib
|
249
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
250
|
+
requirements:
|
251
|
+
- - ">="
|
252
|
+
- !ruby/object:Gem::Version
|
253
|
+
version: 3.1.0
|
254
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
255
|
+
requirements:
|
256
|
+
- - ">="
|
257
|
+
- !ruby/object:Gem::Version
|
258
|
+
version: '0'
|
259
|
+
requirements: []
|
260
|
+
rubygems_version: 3.4.10
|
261
|
+
signing_key:
|
262
|
+
specification_version: 4
|
263
|
+
summary: Customer-supplied encryption key support for ActiveStorage blobs.
|
264
|
+
test_files: []
|