active_storage_encryption 0.2.0 → 0.3.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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -0
  3. data/.github/workflows/ci.yml +75 -0
  4. data/.gitignore +14 -0
  5. data/.ruby-version +1 -0
  6. data/.standard.yml +1 -0
  7. data/Appraisals +2 -0
  8. data/Gemfile +9 -0
  9. data/README.md +2 -0
  10. data/active_storage_encryption.gemspec +47 -0
  11. data/gemfiles/rails_7.gemfile +1 -0
  12. data/gemfiles/rails_7.gemfile.lock +78 -1
  13. data/gemfiles/rails_8.gemfile +1 -0
  14. data/gemfiles/rails_8.gemfile.lock +78 -1
  15. data/lib/active_storage/service/encrypted_gcs_service.rb +10 -0
  16. data/lib/active_storage_encryption/encrypted_blob_proxy_controller.rb +1 -1
  17. data/lib/active_storage_encryption/encrypted_gcs_service.rb +158 -0
  18. data/lib/active_storage_encryption/engine.rb +4 -0
  19. data/lib/active_storage_encryption/overrides.rb +33 -5
  20. data/lib/active_storage_encryption/private_url_policy.rb +1 -1
  21. data/lib/active_storage_encryption/version.rb +1 -1
  22. data/lib/active_storage_encryption.rb +1 -0
  23. data/lib/generators/add_encryption_key_to_active_storage_blobs.rb.erb +9 -0
  24. data/lib/generators/install_generator.rb +25 -0
  25. data/test/dummy/app/assets/images/.keep +0 -0
  26. data/test/dummy/app/controllers/concerns/.keep +0 -0
  27. data/test/dummy/app/models/concerns/.keep +0 -0
  28. data/test/dummy/app/models/user.rb +5 -0
  29. data/test/dummy/config/environments/test.rb +2 -0
  30. data/test/dummy/config/storage.yml +3 -0
  31. data/test/dummy/db/migrate/20250428093315_create_users.rb +7 -0
  32. data/test/dummy/db/schema.rb +6 -3
  33. data/test/dummy/lib/assets/.keep +0 -0
  34. data/test/dummy/log/.keep +0 -0
  35. data/test/integration/.keep +0 -0
  36. data/test/lib/encrypted_gcs_service_test.rb +201 -0
  37. data/test/lib/encrypted_s3_service_test.rb +4 -1
  38. data/test/lib/overrides_test.rb +349 -0
  39. metadata +52 -127
  40. data/test/dummy/log/development.log +0 -304
  41. data/test/dummy/log/test.log +0 -66969
  42. data/test/dummy/storage/0a/mt/0amtaps713liftrtbxt9h998epz4 +0 -0
  43. data/test/dummy/storage/0b/93/0b93pygovuunam1a3ovzwmrbuw2x +0 -0
  44. data/test/dummy/storage/0m/3s/0m3s7r3nboblijr1jxlnvm3p3l4b +0 -0
  45. data/test/dummy/storage/0o/9s/0o9s4ctbpu757qh7ucyony0itek4 +0 -0
  46. data/test/dummy/storage/1e/q6/1eq646og0wazgfw7bwjqz2uem0g4 +0 -0
  47. data/test/dummy/storage/1n/o3/1no30cpwrm727bm6arvb7zxagdg1 +0 -0
  48. data/test/dummy/storage/1x/6w/1x6wsoq3pew17reztwax78lrr3hc +0 -0
  49. data/test/dummy/storage/28/de/28deswrv89c9f2tk7dz1l5uovd4r +0 -0
  50. data/test/dummy/storage/2h/sd/2hsd1mh20c6os2nzyoicfyymhwev +0 -0
  51. data/test/dummy/storage/2t/ni/2tnidhdk4c6cj0tnw3jiw88dgs4g +0 -0
  52. data/test/dummy/storage/2v/e0/2ve0555nluisy2el5cf4txzgae3j +0 -0
  53. data/test/dummy/storage/2z/c5/2zc5mj8g0o9l7mfnim0vs4v48xd6 +0 -0
  54. data/test/dummy/storage/34/xc/34xc9hk74dm9227d6mhgfcfxl4ue +0 -0
  55. data/test/dummy/storage/3z/0t/3z0tnve7ivrq0qyrvfhfzztjhjqs +0 -0
  56. data/test/dummy/storage/49/14/4914188q1dptpw4po91cp54f32bg +0 -0
  57. data/test/dummy/storage/4c/74/4c7412lfz0pm2ocg6u01h67bnsch +0 -0
  58. data/test/dummy/storage/52/qf/52qfbgjlf3gor3agsyrt09t19o55 +0 -0
  59. data/test/dummy/storage/57/go/57gok1uc4ebc3ugrjrje4lpe1ram +0 -0
  60. data/test/dummy/storage/5f/dv/5fdvt6tu1mkyajbz4hbxbw6fpt9w +0 -0
  61. data/test/dummy/storage/5x/b7/5xb7zzi66fi5f6yrn09pq4ogb9wo +0 -0
  62. data/test/dummy/storage/6m/vr/6mvr1fr5it125tm4vahjw6bv9wkz +0 -0
  63. data/test/dummy/storage/7b/hb/7bhbdxqn67lape1f49jqfktcei4n +0 -0
  64. data/test/dummy/storage/7n/4v/7n4vpm1q14y4qffc4jj78m036gtw +0 -0
  65. data/test/dummy/storage/7q/ku/7qkufbjwbbqwnf89uciosleixnew +0 -0
  66. data/test/dummy/storage/8l/5v/8l5vb4o02hx46s5qohfn5to945p3 +0 -0
  67. data/test/dummy/storage/8q/pu/8qpun3f8vzl7auxajvqyq8f48ngw +0 -0
  68. data/test/dummy/storage/8w/ag/8wag4ptmox207h7mobamk0tcebwx +0 -0
  69. data/test/dummy/storage/8w/v8/8wv8lrhsw4s2r6guh1csd3jd89ii +0 -0
  70. data/test/dummy/storage/9b/c6/9bc6wlpfnqdywpnxgeoin3w9b5ch +0 -0
  71. data/test/dummy/storage/9l/wk/9lwkt21k5iburdaitbwliw7krtwt +0 -0
  72. data/test/dummy/storage/9p/0v/9p0vgfw3l2854k7so3rp33rmyh7p +0 -0
  73. data/test/dummy/storage/9r/sy/9rsya3r6syft34qz24g1h4u4qq44 +0 -0
  74. data/test/dummy/storage/9s/es/9seslusr46xjf3mfzq10hkp13kc1 +0 -0
  75. data/test/dummy/storage/9t/nv/9tnvn5v52fkvurpgszf4gco78t5h +0 -0
  76. data/test/dummy/storage/9u/to/9utokgxyu6xyovandu7pjhogoaqp +0 -0
  77. data/test/dummy/storage/9w/a4/9wa4c20p4dvm1cd5thnv9f7ei13w +0 -0
  78. data/test/dummy/storage/at/kg/atkgs5gwz2xdv9lvqftsg6p7gcpu +0 -0
  79. data/test/dummy/storage/at/qo/atqomgf3rpb2f6e1tq1yn2xqzojv +0 -0
  80. data/test/dummy/storage/ba/lq/balqtije6kf82ht4lr70ajaae9kc +0 -0
  81. data/test/dummy/storage/bf/i1/bfi1ij9rygr6lkx1r0lhgi8o5smx +0 -0
  82. data/test/dummy/storage/bg/ye/bgyenotrv3aj6lk88edwv0c41pfj +0 -0
  83. data/test/dummy/storage/bu/xe/buxed4b1l78kcax53fa37awm9ywk +0 -0
  84. data/test/dummy/storage/d2/c1/d2c11nhikb474oq3q7so0xbhukvj +0 -0
  85. data/test/dummy/storage/development.sqlite3 +0 -0
  86. data/test/dummy/storage/dk/hy/dkhybxn2o27a8xgvfhsxpgqxa1zf +0 -0
  87. data/test/dummy/storage/e7/2n/e72nz5cz3wf6qvh4dw4qfnw6ucog +0 -0
  88. data/test/dummy/storage/eo/4q/eo4qn68m7al0ehhe0s23ycuzkjto +0 -0
  89. data/test/dummy/storage/ew/8s/ew8sejdsx8ddmrzkvfa37ebz1ts1 +0 -0
  90. data/test/dummy/storage/f8/q1/f8q1kpg2tou8ru0afj8d2gy6ym5p +0 -0
  91. data/test/dummy/storage/fr/55/fr558uhp1k93jzhb4butyi2ry51t +0 -0
  92. data/test/dummy/storage/g4/nh/g4nhx1zxbeiegqpgn8ppsl1yhm0t +0 -0
  93. data/test/dummy/storage/gg/r5/ggr51egxhqfh4w5eluzs47qceb76 +0 -0
  94. data/test/dummy/storage/gh/ua/ghuaagralqmjy8rkbwmuv3010lvs +0 -0
  95. data/test/dummy/storage/gx/uh/gxuhmf52ufli3m7ng8irp8ghxa1v +0 -0
  96. data/test/dummy/storage/h0/m1/h0m1emy251xus1d9qh6u25dzy18o +0 -0
  97. data/test/dummy/storage/hh/kc/hhkc2q8paptyvhw2m5hlwylhtfo5 +0 -0
  98. data/test/dummy/storage/hq/0q/hq0q04kr6qzrp0qaee8rehcp2tzx +0 -0
  99. data/test/dummy/storage/ii/g1/iig1ge3fsjitai4g2fkq4qt369wh +0 -0
  100. data/test/dummy/storage/io/f0/iof0mv7w8qjd6m826g52pzyxedet +0 -0
  101. data/test/dummy/storage/jk/2i/jk2ifmx6ac35ubk3esufnm6bn1m1 +0 -0
  102. data/test/dummy/storage/jw/4t/jw4trdeyfkw3j8z70xcnr9a7gqe5 +0 -0
  103. data/test/dummy/storage/ke/k2/kek24leksglm1rs2a78mfmot0p3s +0 -0
  104. data/test/dummy/storage/kh/6d/kh6doaxxwxiyes0yqz2dmmpajkzv +0 -0
  105. data/test/dummy/storage/kj/7n/kj7nookjhisagd80z8hlv3wn50am +0 -0
  106. data/test/dummy/storage/kq/lf/kqlf5udtrgrk4v55qodxyt6i68p8 +0 -0
  107. data/test/dummy/storage/ky/33/ky334jbo8eb08pj9qbe919iz91mh +0 -0
  108. data/test/dummy/storage/lt/zw/ltzw4lur2bheit1273ogpfzhv7j1 +0 -0
  109. data/test/dummy/storage/m2/ve/m2vejmyttn1ium81dopppom6vum6 +0 -0
  110. data/test/dummy/storage/m8/d4/m8d4r9iauedq8wlpvnx1f3ou0jwg +0 -0
  111. data/test/dummy/storage/m9/ee/m9eetioklzatyff94gq0vn1cga1n +0 -0
  112. data/test/dummy/storage/ma/v0/mav084zvmyoh1a8i7dcwqy2aaoi9 +0 -0
  113. data/test/dummy/storage/mg/pa/mgpauiu02i28j3poef65k3q0gfpw +0 -0
  114. data/test/dummy/storage/mm/8g/mm8gp5evncb1ol1lj2jlmra2ixij +0 -0
  115. data/test/dummy/storage/mm/d2/mmd21x8c1amgnidzw0wowiwug4g3 +0 -0
  116. data/test/dummy/storage/n2/qr/n2qro0y9heko9cwxlf10wiqiipsw +0 -0
  117. data/test/dummy/storage/n8/b7/n8b7b7qgu6jtw577dnn10jrrmszs +0 -0
  118. data/test/dummy/storage/n8/p2/n8p2ine0qqhphn09kqtxco4y7g0a +0 -0
  119. data/test/dummy/storage/nk/vh/nkvhgk7snpdy2ak6k02htxx9swp7 +0 -0
  120. data/test/dummy/storage/nn/s0/nns0nggo0x645ytco52adnsi4myp +0 -0
  121. data/test/dummy/storage/nu/kz/nukzl7cckkzh68i7kyjkm9mzw7c0 +0 -0
  122. data/test/dummy/storage/nv/8v/nv8vyoghcde1yr1bjpsw4327qt7s +0 -0
  123. data/test/dummy/storage/of/on/ofonhf1gs26k3dpj6o7b0ktzfowh +0 -0
  124. data/test/dummy/storage/pl/pf/plpfs59hvdoogj9gdweqta36csqn +0 -0
  125. data/test/dummy/storage/q5/g5/q5g55ekmscu10pzfw6q4syigt81g +0 -0
  126. data/test/dummy/storage/q5/kc/q5kcr9twyb9v4mh31pay0t7nkuwu +0 -0
  127. data/test/dummy/storage/qa/xd/qaxdngi74r52ahqg1pz8hjddeajc +0 -0
  128. data/test/dummy/storage/r7/5v/r75vadn34ak53vinylgnfdl1s8rt +0 -0
  129. data/test/dummy/storage/rj/rg/rjrghnyzyvxpkjw1a57mrloz72x1 +0 -0
  130. data/test/dummy/storage/se/h7/seh7eorfoanpp6de62pubv7kyu1a +0 -0
  131. data/test/dummy/storage/sj/i1/sji1oj12soz2fcjcoz0gejvzo8to +0 -0
  132. data/test/dummy/storage/sn/2r/sn2rku9thay4hbcbt926an69maku +0 -0
  133. data/test/dummy/storage/sw/jm/swjmbmxou3tnarcirxc6gdycxh91 +0 -0
  134. data/test/dummy/storage/sz/mq/szmqlydvpgqaw7p3v0wh444wtcif +0 -0
  135. data/test/dummy/storage/test.sqlite3 +0 -0
  136. data/test/dummy/storage/tg/by/tgbyrdvg94ivhhy2z59e8l9fod10 +0 -0
  137. data/test/dummy/storage/u5/vm/u5vmz08tuayqggd436et8fiaeml1 +0 -0
  138. data/test/dummy/storage/u6/pf/u6pf4yky0vbmvid3fa3lm4lre68g +0 -0
  139. data/test/dummy/storage/ub/ql/ubqlmlilt8ujgdpngcm1zae41kgy +0 -0
  140. data/test/dummy/storage/un/29/un29e9khqism72ag27ojccmn5sds +0 -0
  141. data/test/dummy/storage/ux/ns/uxnsvuk4rr1p67n1oq6tmraz0gaw +0 -0
  142. data/test/dummy/storage/v1/qo/v1qor0zxg3lctk9mbwyos3oag9gj +0 -0
  143. data/test/dummy/storage/v8/ok/v8okmd7374w1obna13a7anllx2vu +0 -0
  144. data/test/dummy/storage/vd/tf/vdtfmz2ctis3dr1r35do9bow2xj5 +0 -0
  145. data/test/dummy/storage/vo/dg/vodgq1inccnujjt3auber7tt8w8o +0 -0
  146. data/test/dummy/storage/vp/oe/vpoeiq00tf9pk0jcjlccomkju1zc +0 -0
  147. data/test/dummy/storage/vu/kg/vukgoj6qf96bhealui2yaeyn4n72 +0 -0
  148. data/test/dummy/storage/w7/2z/w72zoqu7v6v6jp0tpy671dcbvpow +0 -0
  149. data/test/dummy/storage/wa/3f/wa3fncsnozc6n4xfu32gw34geqcd +0 -0
  150. data/test/dummy/storage/wy/ix/wyixbqx3f6a4agb8bjhrtpblpaua +0 -0
  151. data/test/dummy/storage/xd/st/xdsttma3tqt7mex0vhp1vsm3dq16 +0 -0
  152. data/test/dummy/storage/xv/ej/xvejm2e064bnpunx3nmktaqs0x90 +0 -0
  153. data/test/dummy/storage/xx/py/xxpyyodssq2xmp57qrtvuw0wchwk +0 -0
  154. data/test/dummy/storage/xz/ik/xzikejc5sohi3zexa93s9xmg4jst +0 -0
  155. data/test/dummy/storage/y4/g8/y4g8teo86blcv0zysa2d2jawvk6i +0 -0
  156. data/test/dummy/storage/y9/58/y958xli6aoktx1ehuyjc1k8dcbzv +0 -0
  157. data/test/dummy/storage/yj/lw/yjlw8bf70iujb16deja8ae43rqbc +0 -0
  158. data/test/dummy/storage/z3/qy/z3qyb9avbucwhxa8909rpfued0y5 +0 -0
  159. data/test/dummy/storage/zr/wu/zrwudcg4kgo7r0jemszuzok8grqp +0 -0
  160. data/test/dummy/tmp/local_secret.txt +0 -1
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class ActiveStorageEncryption::OverridesTest < ActiveSupport::TestCase
6
+ include ActiveJob::TestHelper
7
+
8
+ setup do
9
+ ActiveStorage::Current.url_options = {
10
+ host: "www.example.com",
11
+ protocol: "https"
12
+ }
13
+ end
14
+
15
+ def test_encryption_key_is_set_on_encrypted_service_before_saving
16
+ blob = ActiveStorage::Blob.create!(
17
+ key: "yoyo",
18
+ filename: "test.txt",
19
+ byte_size: 10,
20
+ checksum: "abab",
21
+ metadata: {"identified" => true},
22
+ content_type: "text/plain",
23
+ encryption_key: "blabla",
24
+ service_name: "encrypted_disk"
25
+ )
26
+
27
+ assert blob.valid?
28
+ blob.encryption_key = nil
29
+ refute blob.valid?
30
+ assert_equal ["Encryption key must be present for this service"], blob.errors.full_messages
31
+ end
32
+
33
+ def test_attach_download_and_destroy_with_encryption_works
34
+ user = User.create!
35
+ with_uploadable_random_file do |file|
36
+ user.file.attach(io: file, filename: "test.txt")
37
+ end
38
+ assert user.file.url.include?("/active-storage-encryption/blob/")
39
+ assert user.file.blob.encryption_key
40
+ with_uploadable_random_file do |file|
41
+ assert_equal file.size, user.file.blob.byte_size
42
+ end
43
+ with_uploadable_random_file do |file|
44
+ assert_equal file.read, user.file.download
45
+ end
46
+ user.file.destroy
47
+ user.reload
48
+ refute user.file.attached?
49
+ end
50
+
51
+ def test_generate_random_encryption_key_is_long_enough
52
+ key = ActiveStorage::Blob.generate_random_encryption_key
53
+ assert_equal 48, key.size
54
+ assert_equal Encoding::BINARY, key.encoding
55
+ end
56
+
57
+ def test_service_encrypted
58
+ blob = ActiveStorage::Blob.create!(service_name: :encrypted_disk, checksum: "yoyo", encryption_key: "haha", filename: "test", key: "hahahaha", byte_size: 50)
59
+ assert blob.service_encrypted?
60
+ blob_2 = ActiveStorage::Blob.create!(service_name: :test, checksum: "yoyo", filename: "test", key: "ok", byte_size: 50)
61
+ refute blob_2.service_encrypted?
62
+ end
63
+
64
+ def test_create_before_direct_upload_works_with_encryption_and_without
65
+ blob = with_uploadable_random_file do |file|
66
+ ActiveStorage::Blob.create_before_direct_upload!(
67
+ filename: "test_upload",
68
+ byte_size: file.size,
69
+ checksum: "something",
70
+ metadata: {"identified" => true},
71
+ service_name: "encrypted_disk"
72
+ )
73
+ end
74
+ assert_raises ActiveStorage::FileNotFoundError do
75
+ blob.download
76
+ end
77
+
78
+ assert blob.service_encrypted?
79
+ assert blob.encryption_key
80
+
81
+ blob_2 = with_uploadable_random_file do |file|
82
+ ActiveStorage::Blob.create_before_direct_upload!(
83
+ filename: "test_upload_2",
84
+ byte_size: file.size,
85
+ checksum: "something",
86
+ metadata: {"identified" => true},
87
+ service_name: "test"
88
+ )
89
+ end
90
+ refute blob_2.service_encrypted?
91
+ refute blob_2.encryption_key
92
+ end
93
+
94
+ def test_create_and_upload_works_with_encryption_and_without
95
+ encrypted_blob = with_uploadable_random_file do |file|
96
+ ActiveStorage::Blob.create_and_upload!(
97
+ io: file,
98
+ filename: "test_upload",
99
+ metadata: {"identified" => true},
100
+ service_name: "encrypted_disk"
101
+ )
102
+ end
103
+
104
+ assert encrypted_blob.service_encrypted?
105
+ assert encrypted_blob.url.include?("/active-storage-encryption/blob/")
106
+ assert encrypted_blob.encryption_key
107
+ with_uploadable_random_file do |file|
108
+ assert_equal file.size, encrypted_blob.byte_size
109
+ end
110
+ with_uploadable_random_file do |file|
111
+ assert_equal file.read, encrypted_blob.download
112
+ end
113
+
114
+ unencrypted_blob = with_uploadable_random_file do |file|
115
+ ActiveStorage::Blob.create_and_upload!(
116
+ io: file,
117
+ filename: "test_upload_2",
118
+ metadata: {"identified" => true},
119
+ service_name: "test"
120
+ )
121
+ end
122
+
123
+ refute unencrypted_blob.service_encrypted?
124
+ assert unencrypted_blob.url.include?("/rails/active_storage/disk/")
125
+ refute unencrypted_blob.encryption_key
126
+ with_uploadable_random_file do |file|
127
+ assert_equal file.size, unencrypted_blob.byte_size
128
+ end
129
+ with_uploadable_random_file do |file|
130
+ assert_equal file.read, unencrypted_blob.download
131
+ end
132
+ end
133
+
134
+ def test_open_temp_reads_the_content_of_the_blob_with_encryption_and_without
135
+ encrypted_blob = with_uploadable_random_file do |file|
136
+ ActiveStorage::Blob.create_and_upload!(
137
+ io: file,
138
+ filename: "test_upload",
139
+ metadata: {"identified" => true},
140
+ service_name: "encrypted_disk"
141
+ )
142
+ end
143
+
144
+ with_uploadable_random_file do |file|
145
+ encrypted_blob.open do |b|
146
+ assert_equal file.read, b.read
147
+ end
148
+ end
149
+
150
+ unencrypted_blob = with_uploadable_random_file do |file|
151
+ ActiveStorage::Blob.create_and_upload!(
152
+ io: file,
153
+ filename: "test_upload_2",
154
+ metadata: {"identified" => true},
155
+ service_name: "test"
156
+ )
157
+ end
158
+
159
+ with_uploadable_random_file do |file|
160
+ unencrypted_blob.open do |b|
161
+ assert_equal file.read, b.read
162
+ end
163
+ end
164
+ end
165
+
166
+ def test_can_download_a_chunk_with_encryption_and_without
167
+ encrypted_blob = with_uploadable_random_file do |file|
168
+ ActiveStorage::Blob.create_and_upload!(
169
+ io: file,
170
+ filename: "test_upload",
171
+ metadata: {"identified" => true},
172
+ service_name: "encrypted_disk"
173
+ )
174
+ end
175
+
176
+ chunk = encrypted_blob.download_chunk(0..5.bytes)
177
+ with_uploadable_random_file do |file|
178
+ assert_equal chunk, file.read(6)
179
+ end
180
+
181
+ unencrypted_blob = with_uploadable_random_file do |file|
182
+ ActiveStorage::Blob.create_and_upload!(
183
+ io: file,
184
+ filename: "test_upload_2",
185
+ metadata: {"identified" => true},
186
+ service_name: "test"
187
+ )
188
+ end
189
+
190
+ chunk = unencrypted_blob.download_chunk(0..5.bytes)
191
+ with_uploadable_random_file do |file|
192
+ assert_equal chunk, file.read(6)
193
+ end
194
+ end
195
+
196
+ def test_serializable_hash_works_with_encryption_and_without
197
+ encrypted_blob = with_uploadable_random_file do |file|
198
+ ActiveStorage::Blob.create_and_upload!(
199
+ io: file,
200
+ filename: "test_upload",
201
+ metadata: {"identified" => true},
202
+ service_name: "encrypted_disk"
203
+ )
204
+ end
205
+ encrypted_blob_hash = {
206
+ "id" => encrypted_blob.id,
207
+ "key" => encrypted_blob.key,
208
+ "filename" => encrypted_blob.filename,
209
+ "content_type" => encrypted_blob.content_type,
210
+ "metadata" => encrypted_blob.metadata,
211
+ "service_name" => encrypted_blob.service_name,
212
+ "byte_size" => encrypted_blob.byte_size,
213
+ "checksum" => encrypted_blob.checksum,
214
+ "created_at" => encrypted_blob.created_at
215
+ }
216
+ assert_equal encrypted_blob_hash.sort, encrypted_blob.serializable_hash.sort
217
+
218
+ unencrypted_blob = with_uploadable_random_file do |file|
219
+ ActiveStorage::Blob.create_and_upload!(
220
+ io: file,
221
+ filename: "test_upload_2",
222
+ metadata: {"identified" => true},
223
+ service_name: "encrypted_disk"
224
+ )
225
+ end
226
+ unencrypted_blob_hash = {
227
+ "id" => unencrypted_blob.id,
228
+ "key" => unencrypted_blob.key,
229
+ "filename" => unencrypted_blob.filename,
230
+ "content_type" => encrypted_blob.content_type,
231
+ "metadata" => unencrypted_blob.metadata,
232
+ "service_name" => unencrypted_blob.service_name,
233
+ "byte_size" => unencrypted_blob.byte_size,
234
+ "checksum" => unencrypted_blob.checksum,
235
+ "created_at" => unencrypted_blob.created_at
236
+ }
237
+ assert_equal unencrypted_blob_hash, unencrypted_blob.serializable_hash
238
+ end
239
+
240
+ def test_instance_compose_works_with_encryption_and_without
241
+ rng = Random.new(Minitest.seed)
242
+
243
+ encrypted_blob_1 = with_uploadable_random_file do |file|
244
+ ActiveStorage::Blob.create_and_upload!(
245
+ io: file, filename: "test_upload", metadata: {"identified" => true},
246
+ service_name: "encrypted_disk"
247
+ )
248
+ end
249
+ encrypted_blob_2 = with_uploadable_random_file do |file|
250
+ ActiveStorage::Blob.create_and_upload!(
251
+ io: file, filename: "test_upload_2", metadata: {"identified" => true},
252
+ service_name: "encrypted_disk"
253
+ )
254
+ end
255
+ new_encrypted_blob = ActiveStorage::Blob.create_before_direct_upload!(
256
+ key: "new_blob_key", filename: "combined_test_upload",
257
+ metadata: {"identified" => true}, content_type: "plain/text",
258
+ checksum: "okok",
259
+ byte_size: encrypted_blob_1.byte_size + encrypted_blob_2.byte_size,
260
+ encryption_key: rng.bytes(68),
261
+ service_name: "encrypted_disk"
262
+ )
263
+ new_encrypted_blob.compose([encrypted_blob_1.key, encrypted_blob_2.key], source_encryption_keys: [encrypted_blob_1.encryption_key, encrypted_blob_2.encryption_key])
264
+ with_uploadable_random_file do |file|
265
+ assert_equal file.read * 2, new_encrypted_blob.download
266
+ end
267
+
268
+ unencrypted_blob_1 = with_uploadable_random_file do |file|
269
+ ActiveStorage::Blob.create_and_upload!(
270
+ io: file, filename: "test_upload_3", metadata: {"identified" => true},
271
+ service_name: "test"
272
+ )
273
+ end
274
+ unencrypted_blob_2 = with_uploadable_random_file do |file|
275
+ ActiveStorage::Blob.create_and_upload!(
276
+ io: file, filename: "test_upload_4", metadata: {"identified" => true},
277
+ service_name: "test"
278
+ )
279
+ end
280
+ new_unencrypted_blob = ActiveStorage::Blob.create_before_direct_upload!(
281
+ key: "new_blob_key_2", filename: "combined_test_upload",
282
+ metadata: {"identified" => true}, content_type: "plain/text",
283
+ checksum: "okok",
284
+ byte_size: unencrypted_blob_1.byte_size + unencrypted_blob_2.byte_size
285
+ )
286
+ new_unencrypted_blob.compose([unencrypted_blob_1.key, unencrypted_blob_2.key])
287
+ with_uploadable_random_file do |file|
288
+ assert_equal file.read * 2, new_unencrypted_blob.download
289
+ end
290
+ end
291
+
292
+ def test_class_compose_works_with_and_without_encryption
293
+ rng = Random.new(Minitest.seed)
294
+
295
+ encrypted_blob_1 = with_uploadable_random_file do |file|
296
+ ActiveStorage::Blob.create_and_upload!(
297
+ io: file, filename: "test_upload", metadata: {"identified" => true},
298
+ service_name: "encrypted_disk"
299
+ )
300
+ end
301
+ encrypted_blob_2 = with_uploadable_random_file do |file|
302
+ ActiveStorage::Blob.create_and_upload!(
303
+ io: file, filename: "test_upload_2", metadata: {"identified" => true},
304
+ service_name: "encrypted_disk"
305
+ )
306
+ end
307
+ new_enc_blob = ActiveStorage::Blob.compose(
308
+ [encrypted_blob_1, encrypted_blob_2],
309
+ content_type: "text/plain",
310
+ filename: "composed_blob",
311
+ metadata: {"identified" => true},
312
+ key: "new_enc_blob",
313
+ service_name: "encrypted_disk",
314
+ encryption_key: rng.bytes(68)
315
+ )
316
+ with_uploadable_random_file do |file|
317
+ assert_equal file.read * 2, new_enc_blob.download
318
+ end
319
+
320
+ unencrypted_blob_1 = with_uploadable_random_file do |file|
321
+ ActiveStorage::Blob.create_and_upload!(
322
+ io: file, filename: "test_upload_3", metadata: {"identified" => true},
323
+ service_name: "test"
324
+ )
325
+ end
326
+ unencrypted_blob_2 = with_uploadable_random_file do |file|
327
+ ActiveStorage::Blob.create_and_upload!(
328
+ io: file, filename: "test_upload_4", metadata: {"identified" => true},
329
+ service_name: "test"
330
+ )
331
+ end
332
+ new_unencrypted_blob = ActiveStorage::Blob.compose(
333
+ [unencrypted_blob_1, unencrypted_blob_2],
334
+ key: "new_unencblob_key_2", filename: "combined_test_upload_2",
335
+ metadata: {"identified" => true}, service_name: "test"
336
+ )
337
+ with_uploadable_random_file do |file|
338
+ assert_equal file.read * 2, new_unencrypted_blob.download
339
+ end
340
+ end
341
+
342
+ private
343
+
344
+ def with_uploadable_random_file(size = 128, &blk)
345
+ rng = Random.new(Minitest.seed)
346
+ plaintext_upload_bytes = rng.bytes(size)
347
+ StringIO.open(plaintext_upload_bytes, "rb", &blk)
348
+ end
349
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_storage_encryption
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  - Sebastian van Hesteren
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2025-03-27 00:00:00.000000000 Z
11
+ date: 2025-05-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rails
@@ -95,6 +94,20 @@ dependencies:
95
94
  - - ">="
96
95
  - !ruby/object:Gem::Version
97
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: google-cloud-storage
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
98
111
  - !ruby/object:Gem::Dependency
99
112
  name: sqlite3
100
113
  requirement: !ruby/object:Gem::Requirement
@@ -165,6 +178,20 @@ dependencies:
165
178
  - - ">="
166
179
  - !ruby/object:Gem::Version
167
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: pry
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
168
195
  description: Adds customer-supplied encryption keys to storage services.
169
196
  email:
170
197
  - me@julik.nl
@@ -172,10 +199,17 @@ executables: []
172
199
  extensions: []
173
200
  extra_rdoc_files: []
174
201
  files:
202
+ - ".github/dependabot.yml"
203
+ - ".github/workflows/ci.yml"
204
+ - ".gitignore"
205
+ - ".ruby-version"
206
+ - ".standard.yml"
175
207
  - Appraisals
208
+ - Gemfile
176
209
  - MIT-LICENSE
177
210
  - README.md
178
211
  - Rakefile
212
+ - active_storage_encryption.gemspec
179
213
  - bin/rails
180
214
  - bin/rubocop
181
215
  - config/initializers/active_storage_encryption.rb
@@ -185,6 +219,7 @@ files:
185
219
  - gemfiles/rails_8.gemfile
186
220
  - gemfiles/rails_8.gemfile.lock
187
221
  - lib/active_storage/service/encrypted_disk_service.rb
222
+ - lib/active_storage/service/encrypted_gcs_service.rb
188
223
  - lib/active_storage/service/encrypted_mirror_service.rb
189
224
  - lib/active_storage/service/encrypted_s3_service.rb
190
225
  - lib/active_storage_encryption.rb
@@ -193,6 +228,7 @@ files:
193
228
  - lib/active_storage_encryption/encrypted_disk_service.rb
194
229
  - lib/active_storage_encryption/encrypted_disk_service/v1_scheme.rb
195
230
  - lib/active_storage_encryption/encrypted_disk_service/v2_scheme.rb
231
+ - lib/active_storage_encryption/encrypted_gcs_service.rb
196
232
  - lib/active_storage_encryption/encrypted_mirror_service.rb
197
233
  - lib/active_storage_encryption/encrypted_s3_service.rb
198
234
  - lib/active_storage_encryption/engine.rb
@@ -200,13 +236,19 @@ files:
200
236
  - lib/active_storage_encryption/private_url_policy.rb
201
237
  - lib/active_storage_encryption/resumable_gcs_upload.rb
202
238
  - lib/active_storage_encryption/version.rb
239
+ - lib/generators/add_encryption_key_to_active_storage_blobs.rb.erb
240
+ - lib/generators/install_generator.rb
203
241
  - lib/tasks/active_storage_encryption_tasks.rake
204
242
  - test/active_storage_encryption_test.rb
205
243
  - test/dummy/Rakefile
244
+ - test/dummy/app/assets/images/.keep
206
245
  - test/dummy/app/assets/stylesheets/application.css
207
246
  - test/dummy/app/controllers/application_controller.rb
247
+ - test/dummy/app/controllers/concerns/.keep
208
248
  - test/dummy/app/helpers/application_helper.rb
209
249
  - test/dummy/app/models/application_record.rb
250
+ - test/dummy/app/models/concerns/.keep
251
+ - test/dummy/app/models/user.rb
210
252
  - test/dummy/app/views/layouts/application.html.erb
211
253
  - test/dummy/app/views/pwa/manifest.json.erb
212
254
  - test/dummy/app/views/pwa/service-worker.js
@@ -233,139 +275,24 @@ files:
233
275
  - test/dummy/config/storage.yml
234
276
  - test/dummy/db/migrate/20250304023851_create_active_storage_tables.active_storage.rb
235
277
  - test/dummy/db/migrate/20250304023853_add_blob_encryption_key_column.rb
278
+ - test/dummy/db/migrate/20250428093315_create_users.rb
236
279
  - test/dummy/db/schema.rb
237
- - test/dummy/log/development.log
238
- - test/dummy/log/test.log
280
+ - test/dummy/lib/assets/.keep
281
+ - test/dummy/log/.keep
239
282
  - test/dummy/public/404.html
240
283
  - test/dummy/public/406-unsupported-browser.html
241
284
  - test/dummy/public/422.html
242
285
  - test/dummy/public/500.html
243
286
  - test/dummy/public/icon.png
244
287
  - test/dummy/public/icon.svg
245
- - test/dummy/storage/0a/mt/0amtaps713liftrtbxt9h998epz4
246
- - test/dummy/storage/0b/93/0b93pygovuunam1a3ovzwmrbuw2x
247
- - test/dummy/storage/0m/3s/0m3s7r3nboblijr1jxlnvm3p3l4b
248
- - test/dummy/storage/0o/9s/0o9s4ctbpu757qh7ucyony0itek4
249
- - test/dummy/storage/1e/q6/1eq646og0wazgfw7bwjqz2uem0g4
250
- - test/dummy/storage/1n/o3/1no30cpwrm727bm6arvb7zxagdg1
251
- - test/dummy/storage/1x/6w/1x6wsoq3pew17reztwax78lrr3hc
252
- - test/dummy/storage/28/de/28deswrv89c9f2tk7dz1l5uovd4r
253
- - test/dummy/storage/2h/sd/2hsd1mh20c6os2nzyoicfyymhwev
254
- - test/dummy/storage/2t/ni/2tnidhdk4c6cj0tnw3jiw88dgs4g
255
- - test/dummy/storage/2v/e0/2ve0555nluisy2el5cf4txzgae3j
256
- - test/dummy/storage/2z/c5/2zc5mj8g0o9l7mfnim0vs4v48xd6
257
- - test/dummy/storage/34/xc/34xc9hk74dm9227d6mhgfcfxl4ue
258
- - test/dummy/storage/3z/0t/3z0tnve7ivrq0qyrvfhfzztjhjqs
259
- - test/dummy/storage/49/14/4914188q1dptpw4po91cp54f32bg
260
- - test/dummy/storage/4c/74/4c7412lfz0pm2ocg6u01h67bnsch
261
- - test/dummy/storage/52/qf/52qfbgjlf3gor3agsyrt09t19o55
262
- - test/dummy/storage/57/go/57gok1uc4ebc3ugrjrje4lpe1ram
263
- - test/dummy/storage/5f/dv/5fdvt6tu1mkyajbz4hbxbw6fpt9w
264
- - test/dummy/storage/5x/b7/5xb7zzi66fi5f6yrn09pq4ogb9wo
265
- - test/dummy/storage/6m/vr/6mvr1fr5it125tm4vahjw6bv9wkz
266
- - test/dummy/storage/7b/hb/7bhbdxqn67lape1f49jqfktcei4n
267
- - test/dummy/storage/7n/4v/7n4vpm1q14y4qffc4jj78m036gtw
268
- - test/dummy/storage/7q/ku/7qkufbjwbbqwnf89uciosleixnew
269
- - test/dummy/storage/8l/5v/8l5vb4o02hx46s5qohfn5to945p3
270
- - test/dummy/storage/8q/pu/8qpun3f8vzl7auxajvqyq8f48ngw
271
- - test/dummy/storage/8w/ag/8wag4ptmox207h7mobamk0tcebwx
272
- - test/dummy/storage/8w/v8/8wv8lrhsw4s2r6guh1csd3jd89ii
273
- - test/dummy/storage/9b/c6/9bc6wlpfnqdywpnxgeoin3w9b5ch
274
- - test/dummy/storage/9l/wk/9lwkt21k5iburdaitbwliw7krtwt
275
- - test/dummy/storage/9p/0v/9p0vgfw3l2854k7so3rp33rmyh7p
276
- - test/dummy/storage/9r/sy/9rsya3r6syft34qz24g1h4u4qq44
277
- - test/dummy/storage/9s/es/9seslusr46xjf3mfzq10hkp13kc1
278
- - test/dummy/storage/9t/nv/9tnvn5v52fkvurpgszf4gco78t5h
279
- - test/dummy/storage/9u/to/9utokgxyu6xyovandu7pjhogoaqp
280
- - test/dummy/storage/9w/a4/9wa4c20p4dvm1cd5thnv9f7ei13w
281
- - test/dummy/storage/at/kg/atkgs5gwz2xdv9lvqftsg6p7gcpu
282
- - test/dummy/storage/at/qo/atqomgf3rpb2f6e1tq1yn2xqzojv
283
- - test/dummy/storage/ba/lq/balqtije6kf82ht4lr70ajaae9kc
284
- - test/dummy/storage/bf/i1/bfi1ij9rygr6lkx1r0lhgi8o5smx
285
- - test/dummy/storage/bg/ye/bgyenotrv3aj6lk88edwv0c41pfj
286
- - test/dummy/storage/bu/xe/buxed4b1l78kcax53fa37awm9ywk
287
- - test/dummy/storage/d2/c1/d2c11nhikb474oq3q7so0xbhukvj
288
- - test/dummy/storage/development.sqlite3
289
- - test/dummy/storage/dk/hy/dkhybxn2o27a8xgvfhsxpgqxa1zf
290
- - test/dummy/storage/e7/2n/e72nz5cz3wf6qvh4dw4qfnw6ucog
291
- - test/dummy/storage/eo/4q/eo4qn68m7al0ehhe0s23ycuzkjto
292
- - test/dummy/storage/ew/8s/ew8sejdsx8ddmrzkvfa37ebz1ts1
293
- - test/dummy/storage/f8/q1/f8q1kpg2tou8ru0afj8d2gy6ym5p
294
- - test/dummy/storage/fr/55/fr558uhp1k93jzhb4butyi2ry51t
295
- - test/dummy/storage/g4/nh/g4nhx1zxbeiegqpgn8ppsl1yhm0t
296
- - test/dummy/storage/gg/r5/ggr51egxhqfh4w5eluzs47qceb76
297
- - test/dummy/storage/gh/ua/ghuaagralqmjy8rkbwmuv3010lvs
298
- - test/dummy/storage/gx/uh/gxuhmf52ufli3m7ng8irp8ghxa1v
299
- - test/dummy/storage/h0/m1/h0m1emy251xus1d9qh6u25dzy18o
300
- - test/dummy/storage/hh/kc/hhkc2q8paptyvhw2m5hlwylhtfo5
301
- - test/dummy/storage/hq/0q/hq0q04kr6qzrp0qaee8rehcp2tzx
302
- - test/dummy/storage/ii/g1/iig1ge3fsjitai4g2fkq4qt369wh
303
- - test/dummy/storage/io/f0/iof0mv7w8qjd6m826g52pzyxedet
304
- - test/dummy/storage/jk/2i/jk2ifmx6ac35ubk3esufnm6bn1m1
305
- - test/dummy/storage/jw/4t/jw4trdeyfkw3j8z70xcnr9a7gqe5
306
- - test/dummy/storage/ke/k2/kek24leksglm1rs2a78mfmot0p3s
307
- - test/dummy/storage/kh/6d/kh6doaxxwxiyes0yqz2dmmpajkzv
308
- - test/dummy/storage/kj/7n/kj7nookjhisagd80z8hlv3wn50am
309
- - test/dummy/storage/kq/lf/kqlf5udtrgrk4v55qodxyt6i68p8
310
- - test/dummy/storage/ky/33/ky334jbo8eb08pj9qbe919iz91mh
311
- - test/dummy/storage/lt/zw/ltzw4lur2bheit1273ogpfzhv7j1
312
- - test/dummy/storage/m2/ve/m2vejmyttn1ium81dopppom6vum6
313
- - test/dummy/storage/m8/d4/m8d4r9iauedq8wlpvnx1f3ou0jwg
314
- - test/dummy/storage/m9/ee/m9eetioklzatyff94gq0vn1cga1n
315
- - test/dummy/storage/ma/v0/mav084zvmyoh1a8i7dcwqy2aaoi9
316
- - test/dummy/storage/mg/pa/mgpauiu02i28j3poef65k3q0gfpw
317
- - test/dummy/storage/mm/8g/mm8gp5evncb1ol1lj2jlmra2ixij
318
- - test/dummy/storage/mm/d2/mmd21x8c1amgnidzw0wowiwug4g3
319
- - test/dummy/storage/n2/qr/n2qro0y9heko9cwxlf10wiqiipsw
320
- - test/dummy/storage/n8/b7/n8b7b7qgu6jtw577dnn10jrrmszs
321
- - test/dummy/storage/n8/p2/n8p2ine0qqhphn09kqtxco4y7g0a
322
- - test/dummy/storage/nk/vh/nkvhgk7snpdy2ak6k02htxx9swp7
323
- - test/dummy/storage/nn/s0/nns0nggo0x645ytco52adnsi4myp
324
- - test/dummy/storage/nu/kz/nukzl7cckkzh68i7kyjkm9mzw7c0
325
- - test/dummy/storage/nv/8v/nv8vyoghcde1yr1bjpsw4327qt7s
326
- - test/dummy/storage/of/on/ofonhf1gs26k3dpj6o7b0ktzfowh
327
- - test/dummy/storage/pl/pf/plpfs59hvdoogj9gdweqta36csqn
328
- - test/dummy/storage/q5/g5/q5g55ekmscu10pzfw6q4syigt81g
329
- - test/dummy/storage/q5/kc/q5kcr9twyb9v4mh31pay0t7nkuwu
330
- - test/dummy/storage/qa/xd/qaxdngi74r52ahqg1pz8hjddeajc
331
- - test/dummy/storage/r7/5v/r75vadn34ak53vinylgnfdl1s8rt
332
- - test/dummy/storage/rj/rg/rjrghnyzyvxpkjw1a57mrloz72x1
333
- - test/dummy/storage/se/h7/seh7eorfoanpp6de62pubv7kyu1a
334
- - test/dummy/storage/sj/i1/sji1oj12soz2fcjcoz0gejvzo8to
335
- - test/dummy/storage/sn/2r/sn2rku9thay4hbcbt926an69maku
336
- - test/dummy/storage/sw/jm/swjmbmxou3tnarcirxc6gdycxh91
337
- - test/dummy/storage/sz/mq/szmqlydvpgqaw7p3v0wh444wtcif
338
- - test/dummy/storage/test.sqlite3
339
- - test/dummy/storage/tg/by/tgbyrdvg94ivhhy2z59e8l9fod10
340
- - test/dummy/storage/u5/vm/u5vmz08tuayqggd436et8fiaeml1
341
- - test/dummy/storage/u6/pf/u6pf4yky0vbmvid3fa3lm4lre68g
342
- - test/dummy/storage/ub/ql/ubqlmlilt8ujgdpngcm1zae41kgy
343
- - test/dummy/storage/un/29/un29e9khqism72ag27ojccmn5sds
344
- - test/dummy/storage/ux/ns/uxnsvuk4rr1p67n1oq6tmraz0gaw
345
- - test/dummy/storage/v1/qo/v1qor0zxg3lctk9mbwyos3oag9gj
346
- - test/dummy/storage/v8/ok/v8okmd7374w1obna13a7anllx2vu
347
- - test/dummy/storage/vd/tf/vdtfmz2ctis3dr1r35do9bow2xj5
348
- - test/dummy/storage/vo/dg/vodgq1inccnujjt3auber7tt8w8o
349
- - test/dummy/storage/vp/oe/vpoeiq00tf9pk0jcjlccomkju1zc
350
- - test/dummy/storage/vu/kg/vukgoj6qf96bhealui2yaeyn4n72
351
- - test/dummy/storage/w7/2z/w72zoqu7v6v6jp0tpy671dcbvpow
352
- - test/dummy/storage/wa/3f/wa3fncsnozc6n4xfu32gw34geqcd
353
- - test/dummy/storage/wy/ix/wyixbqx3f6a4agb8bjhrtpblpaua
354
- - test/dummy/storage/xd/st/xdsttma3tqt7mex0vhp1vsm3dq16
355
- - test/dummy/storage/xv/ej/xvejm2e064bnpunx3nmktaqs0x90
356
- - test/dummy/storage/xx/py/xxpyyodssq2xmp57qrtvuw0wchwk
357
- - test/dummy/storage/xz/ik/xzikejc5sohi3zexa93s9xmg4jst
358
- - test/dummy/storage/y4/g8/y4g8teo86blcv0zysa2d2jawvk6i
359
- - test/dummy/storage/y9/58/y958xli6aoktx1ehuyjc1k8dcbzv
360
- - test/dummy/storage/yj/lw/yjlw8bf70iujb16deja8ae43rqbc
361
- - test/dummy/storage/z3/qy/z3qyb9avbucwhxa8909rpfued0y5
362
- - test/dummy/storage/zr/wu/zrwudcg4kgo7r0jemszuzok8grqp
363
- - test/dummy/tmp/local_secret.txt
288
+ - test/integration/.keep
364
289
  - test/integration/encrypted_blob_proxy_controller_test.rb
365
290
  - test/integration/encrypted_blobs_controller_test.rb
366
291
  - test/lib/encrypted_disk_service_test.rb
292
+ - test/lib/encrypted_gcs_service_test.rb
367
293
  - test/lib/encrypted_mirror_service_test.rb
368
294
  - test/lib/encrypted_s3_service_test.rb
295
+ - test/lib/overrides_test.rb
369
296
  - test/test_helper.rb
370
297
  homepage: https://github.com/cheddar-me/active_storage_encryption
371
298
  licenses:
@@ -375,7 +302,6 @@ metadata:
375
302
  homepage_uri: https://github.com/cheddar-me/active_storage_encryption
376
303
  source_code_uri: https://github.com/cheddar-me/active_storage_encryption
377
304
  changelog_uri: https://github.com/cheddar-me/active_storage_encryption/blob/main/CHANGELOG.md
378
- post_install_message:
379
305
  rdoc_options: []
380
306
  require_paths:
381
307
  - lib
@@ -390,8 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
390
316
  - !ruby/object:Gem::Version
391
317
  version: '0'
392
318
  requirements: []
393
- rubygems_version: 3.4.10
394
- signing_key:
319
+ rubygems_version: 3.6.6
395
320
  specification_version: 4
396
321
  summary: Customer-supplied encryption key support for ActiveStorage blobs.
397
322
  test_files: []