lockbox 2.0.1 → 2.2.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +6 -6
- data/lib/lockbox/active_storage_extensions.rb +18 -17
- data/lib/lockbox/model.rb +19 -64
- data/lib/lockbox/version.rb +1 -1
- data/lib/lockbox.rb +6 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b3a6c05fd83da2e2fc8474f2daf3b55f27ee61dd8a86ff3264df772816bcf92b
|
|
4
|
+
data.tar.gz: d269773e126958e5288a2fcb48ee228dbcd3918648e02834057c8be04f36ec45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f05224cd3e90993ba6a5b7cadfd2bdac839e6590ae3a5702935dc1146b1349ce76d41d3de989f9e10b3510cf420a86936d283968cc343a7eaccfa119a4dff1b6
|
|
7
|
+
data.tar.gz: d185de56bd634a6ba6753d3a1acf91a7f3940edb02ccbcaa89b22839ef4e57e62a5c698074b6cff821bc5869e3b423dee94781fad4a7e0961794982fa2ed1faa
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 2.2.0 (2026-04-04)
|
|
2
|
+
|
|
3
|
+
- Dropped support for Active Record < 7.2 and Ruby < 3.3
|
|
4
|
+
|
|
5
|
+
## 2.1.0 (2025-10-15)
|
|
6
|
+
|
|
7
|
+
- Added warning for `download_chunk` method
|
|
8
|
+
- Fixed error for `download` method with block
|
|
9
|
+
- Dropped support for Active Record < 7.1 and Ruby < 3.2
|
|
10
|
+
|
|
1
11
|
## 2.0.1 (2024-12-29)
|
|
2
12
|
|
|
3
13
|
- Added support for Ruby 3.4
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Then follow the instructions below for the data you want to encrypt.
|
|
|
72
72
|
Create a migration with:
|
|
73
73
|
|
|
74
74
|
```ruby
|
|
75
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.
|
|
75
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.1]
|
|
76
76
|
def change
|
|
77
77
|
add_column :users, :email_ciphertext, :text
|
|
78
78
|
end
|
|
@@ -251,7 +251,7 @@ User.decrypt_email_ciphertext(user.email_ciphertext)
|
|
|
251
251
|
Create a migration with:
|
|
252
252
|
|
|
253
253
|
```ruby
|
|
254
|
-
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[8.
|
|
254
|
+
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[8.1]
|
|
255
255
|
def change
|
|
256
256
|
add_column :action_text_rich_texts, :body_ciphertext, :text
|
|
257
257
|
end
|
|
@@ -382,7 +382,7 @@ Encryption is applied to all versions after processing.
|
|
|
382
382
|
You can mount the uploader [as normal](https://github.com/carrierwaveuploader/carrierwave#activerecord). With Active Record, this involves creating a migration:
|
|
383
383
|
|
|
384
384
|
```ruby
|
|
385
|
-
class AddLicenseToUsers < ActiveRecord::Migration[8.
|
|
385
|
+
class AddLicenseToUsers < ActiveRecord::Migration[8.1]
|
|
386
386
|
def change
|
|
387
387
|
add_column :users, :license, :string
|
|
388
388
|
end
|
|
@@ -910,7 +910,7 @@ end
|
|
|
910
910
|
You can use `binary` columns for the ciphertext instead of `text` columns.
|
|
911
911
|
|
|
912
912
|
```ruby
|
|
913
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.
|
|
913
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.1]
|
|
914
914
|
def change
|
|
915
915
|
add_column :users, :email_ciphertext, :binary
|
|
916
916
|
end
|
|
@@ -961,7 +961,7 @@ end
|
|
|
961
961
|
Create a migration with:
|
|
962
962
|
|
|
963
963
|
```ruby
|
|
964
|
-
class MigrateToLockbox < ActiveRecord::Migration[8.
|
|
964
|
+
class MigrateToLockbox < ActiveRecord::Migration[8.1]
|
|
965
965
|
def change
|
|
966
966
|
add_column :users, :name_ciphertext, :text
|
|
967
967
|
add_column :users, :email_ciphertext, :text
|
|
@@ -994,7 +994,7 @@ end
|
|
|
994
994
|
Then remove the previous gem from your Gemfile and drop its columns.
|
|
995
995
|
|
|
996
996
|
```ruby
|
|
997
|
-
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[8.
|
|
997
|
+
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[8.1]
|
|
998
998
|
def change
|
|
999
999
|
remove_column :users, :encrypted_name, :text
|
|
1000
1000
|
remove_column :users, :encrypted_name_iv, :text
|
|
@@ -80,7 +80,7 @@ module Lockbox
|
|
|
80
80
|
|
|
81
81
|
module Attachment
|
|
82
82
|
def download
|
|
83
|
-
result = super
|
|
83
|
+
result = super(&nil)
|
|
84
84
|
|
|
85
85
|
options = Utils.encrypted_options(record, name)
|
|
86
86
|
# only trust the metadata when migrating
|
|
@@ -91,24 +91,31 @@ module Lockbox
|
|
|
91
91
|
result = Utils.decrypt_result(record, name, options, result)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
if block_given?
|
|
95
|
+
io = StringIO.new(result)
|
|
96
|
+
chunk_size = 5.megabytes
|
|
97
|
+
while (chunk = io.read(chunk_size))
|
|
98
|
+
yield chunk
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
result
|
|
102
|
+
end
|
|
95
103
|
end
|
|
96
104
|
|
|
97
|
-
def
|
|
98
|
-
|
|
105
|
+
def download_chunk(...)
|
|
106
|
+
# TODO raise error in 3.0
|
|
107
|
+
warn "[lockbox] WARNING: download_chunk not supported for encrypted files" if Utils.encrypted_options(record, name)
|
|
99
108
|
super
|
|
100
109
|
end
|
|
101
110
|
|
|
102
|
-
def
|
|
103
|
-
raise Lockbox::Error, "
|
|
111
|
+
def variant(...)
|
|
112
|
+
raise Lockbox::Error, "Variant not supported for encrypted files" if Utils.encrypted_options(record, name)
|
|
104
113
|
super
|
|
105
114
|
end
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
super
|
|
111
|
-
end
|
|
116
|
+
def preview(...)
|
|
117
|
+
raise Lockbox::Error, "Preview not supported for encrypted files" if Utils.encrypted_options(record, name)
|
|
118
|
+
super
|
|
112
119
|
end
|
|
113
120
|
|
|
114
121
|
def open(**options)
|
|
@@ -135,12 +142,6 @@ module Lockbox
|
|
|
135
142
|
end
|
|
136
143
|
|
|
137
144
|
module Blob
|
|
138
|
-
if ActiveStorage::VERSION::STRING.to_f == 7.1 && ActiveStorage.version >= "7.1.4"
|
|
139
|
-
def preview_image_needed_before_processing_variants?
|
|
140
|
-
!instance_variable_defined?(:@lockbox_encrypted) && super
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
145
|
private
|
|
145
146
|
|
|
146
147
|
def extract_content_type(io)
|
data/lib/lockbox/model.rb
CHANGED
|
@@ -259,18 +259,16 @@ module Lockbox
|
|
|
259
259
|
result
|
|
260
260
|
end
|
|
261
261
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
end
|
|
262
|
+
def self.insert(attributes, **options)
|
|
263
|
+
super(lockbox_map_record_attributes(attributes), **options)
|
|
264
|
+
end
|
|
266
265
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
def self.insert!(attributes, **options)
|
|
267
|
+
super(lockbox_map_record_attributes(attributes), **options)
|
|
268
|
+
end
|
|
270
269
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
end
|
|
270
|
+
def self.upsert(attributes, **options)
|
|
271
|
+
super(lockbox_map_record_attributes(attributes, check_readonly: true), **options)
|
|
274
272
|
end
|
|
275
273
|
|
|
276
274
|
def self.insert_all(attributes, **options)
|
|
@@ -344,20 +342,9 @@ module Lockbox
|
|
|
344
342
|
end
|
|
345
343
|
|
|
346
344
|
# warn on default attributes
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
|
|
351
|
-
end
|
|
352
|
-
elsif attributes_to_define_after_schema_loads.key?(name.to_s)
|
|
353
|
-
opt = attributes_to_define_after_schema_loads[name.to_s][1]
|
|
354
|
-
|
|
355
|
-
# not ideal, since NO_DEFAULT_PROVIDED is private
|
|
356
|
-
has_default = opt != ActiveRecord::Attributes::ClassMethods.const_get(:NO_DEFAULT_PROVIDED)
|
|
357
|
-
|
|
358
|
-
if has_default
|
|
359
|
-
warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
|
|
360
|
-
end
|
|
345
|
+
# TODO improve
|
|
346
|
+
if pending_attribute_modifications.any? { |v| v.is_a?(ActiveModel::AttributeRegistration::ClassMethods::PendingDefault) && v.name == name.to_s }
|
|
347
|
+
warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
|
|
361
348
|
end
|
|
362
349
|
|
|
363
350
|
# preference:
|
|
@@ -377,26 +364,15 @@ module Lockbox
|
|
|
377
364
|
|
|
378
365
|
attribute name, attribute_type
|
|
379
366
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
serialize name, type: Array, coder: default_column_serializer || YAML
|
|
388
|
-
end
|
|
389
|
-
else
|
|
390
|
-
case options[:type]
|
|
391
|
-
when :json
|
|
392
|
-
serialize name, JSON
|
|
393
|
-
when :hash
|
|
394
|
-
serialize name, Hash
|
|
395
|
-
when :array
|
|
396
|
-
serialize name, Array
|
|
397
|
-
end
|
|
367
|
+
case options[:type]
|
|
368
|
+
when :json
|
|
369
|
+
serialize name, coder: JSON
|
|
370
|
+
when :hash
|
|
371
|
+
serialize name, type: Hash, coder: default_column_serializer || YAML
|
|
372
|
+
when :array
|
|
373
|
+
serialize name, type: Array, coder: default_column_serializer || YAML
|
|
398
374
|
end
|
|
399
|
-
|
|
375
|
+
else
|
|
400
376
|
decorate_attributes([name]) do |attr_name, cast_type|
|
|
401
377
|
if cast_type.instance_of?(ActiveRecord::Type::Value)
|
|
402
378
|
original_type = pending_attribute_modifications.find { |v| v.is_a?(ActiveModel::AttributeRegistration::ClassMethods::PendingType) && v.name == original_name.to_s && !v.type.nil? }&.type
|
|
@@ -416,27 +392,6 @@ module Lockbox
|
|
|
416
392
|
cast_type
|
|
417
393
|
end
|
|
418
394
|
end
|
|
419
|
-
elsif !attributes_to_define_after_schema_loads.key?(name.to_s)
|
|
420
|
-
# when migrating it's best to specify the type directly
|
|
421
|
-
# however, we can try to use the original type if its already defined
|
|
422
|
-
if attributes_to_define_after_schema_loads.key?(original_name.to_s)
|
|
423
|
-
attribute name, attributes_to_define_after_schema_loads[original_name.to_s].first
|
|
424
|
-
elsif options[:migrating]
|
|
425
|
-
# we use the original attribute for serialization in the encrypt and decrypt methods
|
|
426
|
-
# so we can use a generic value here
|
|
427
|
-
attribute name, ActiveRecord::Type::Value.new
|
|
428
|
-
else
|
|
429
|
-
attribute name, :string
|
|
430
|
-
end
|
|
431
|
-
elsif attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
|
|
432
|
-
# hack for Active Record 6.1+ to set string type after serialize
|
|
433
|
-
# otherwise, type gets set to ActiveModel::Type::Value
|
|
434
|
-
# which always returns false for changed_in_place?
|
|
435
|
-
# earlier versions of Active Record take the previous code path
|
|
436
|
-
attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call(nil)
|
|
437
|
-
if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
|
|
438
|
-
attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
|
|
439
|
-
end
|
|
440
395
|
end
|
|
441
396
|
|
|
442
397
|
define_method("#{name}_was") do
|
data/lib/lockbox/version.rb
CHANGED
data/lib/lockbox.rb
CHANGED
|
@@ -98,8 +98,12 @@ end
|
|
|
98
98
|
if defined?(ActiveSupport.on_load)
|
|
99
99
|
ActiveSupport.on_load(:active_record) do
|
|
100
100
|
ar_version = ActiveRecord::VERSION::STRING.to_f
|
|
101
|
-
if ar_version < 7
|
|
102
|
-
if ar_version >=
|
|
101
|
+
if ar_version < 7.2
|
|
102
|
+
if ar_version >= 7.1
|
|
103
|
+
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 2.2"
|
|
104
|
+
elsif ar_version >= 7.0
|
|
105
|
+
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 2.1"
|
|
106
|
+
elsif ar_version >= 5.2
|
|
103
107
|
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 2"
|
|
104
108
|
elsif ar_version >= 5
|
|
105
109
|
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 0.7"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lockbox
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
email: andrew@ankane.org
|
|
13
13
|
executables: []
|
|
@@ -48,14 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
48
48
|
requirements:
|
|
49
49
|
- - ">="
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
|
-
version: '3.
|
|
51
|
+
version: '3.3'
|
|
52
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
53
|
requirements:
|
|
54
54
|
- - ">="
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
56
|
version: '0'
|
|
57
57
|
requirements: []
|
|
58
|
-
rubygems_version:
|
|
58
|
+
rubygems_version: 4.0.6
|
|
59
59
|
specification_version: 4
|
|
60
60
|
summary: Modern encryption for Ruby and Rails
|
|
61
61
|
test_files: []
|