lockbox 1.3.3 → 1.4.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 +6 -0
- data/README.md +1 -16
- data/lib/lockbox/carrier_wave_extensions.rb +1 -1
- data/lib/lockbox/migrator.rb +1 -1
- data/lib/lockbox/model.rb +83 -34
- data/lib/lockbox/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c227a11280d70eaaa03e7c81649bee696c2c88ab9a1825503f7db4e9af5d2d12
|
4
|
+
data.tar.gz: 52a2969568229aefa391a093c93ae1771d021ddc2396d3e4f65ec2f509a82c41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1734e1db6d559ed4221e81b30ca6a13db348a6089f58581376ce4cf16019a5787f8dd1b80abd1534ef2c82e294db4db1d8cb30089413d4e2e009544d3185d350
|
7
|
+
data.tar.gz: d5bdb1947c5fac19038fe21651bf35d32d675eba024569ec085b015ad991a9c1ea5e65437df908f78e0083486726032571c6dee364fe92a95a7447e33495cf83
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -194,7 +194,7 @@ class User < ApplicationRecord
|
|
194
194
|
has_encrypted :email
|
195
195
|
|
196
196
|
# remove this line after dropping email column
|
197
|
-
self.ignored_columns
|
197
|
+
self.ignored_columns += ["email"]
|
198
198
|
end
|
199
199
|
```
|
200
200
|
|
@@ -1016,21 +1016,6 @@ class User < ApplicationRecord
|
|
1016
1016
|
end
|
1017
1017
|
```
|
1018
1018
|
|
1019
|
-
### 0.6.0
|
1020
|
-
|
1021
|
-
0.6.0 adds `encrypted: true` to Active Storage metadata for new files. This field is informational, but if you prefer to add it to existing files, use:
|
1022
|
-
|
1023
|
-
```ruby
|
1024
|
-
User.with_attached_license.find_each do |user|
|
1025
|
-
next unless user.license.attached?
|
1026
|
-
|
1027
|
-
metadata = user.license.metadata
|
1028
|
-
unless metadata["encrypted"]
|
1029
|
-
user.license.blob.update!(metadata: metadata.merge("encrypted" => true))
|
1030
|
-
end
|
1031
|
-
end
|
1032
|
-
```
|
1033
|
-
|
1034
1019
|
## History
|
1035
1020
|
|
1036
1021
|
View the [changelog](https://github.com/ankane/lockbox/blob/master/CHANGELOG.md)
|
data/lib/lockbox/migrator.rb
CHANGED
@@ -2,7 +2,7 @@ module Lockbox
|
|
2
2
|
class Migrator
|
3
3
|
def initialize(relation, batch_size:)
|
4
4
|
@relation = relation
|
5
|
-
@transaction = @relation.respond_to?(:transaction)
|
5
|
+
@transaction = @relation.respond_to?(:transaction) && !mongoid_relation?(base_relation)
|
6
6
|
@batch_size = batch_size
|
7
7
|
end
|
8
8
|
|
data/lib/lockbox/model.rb
CHANGED
@@ -137,13 +137,16 @@ module Lockbox
|
|
137
137
|
# essentially a no-op if already loaded
|
138
138
|
# an exception is thrown if decryption fails
|
139
139
|
self.class.lockbox_attributes.each do |_, lockbox_attribute|
|
140
|
-
# don't try to decrypt if no decryption key given
|
141
|
-
next if lockbox_attribute[:algorithm] == "hybrid" && lockbox_attribute[:decryption_key].nil?
|
142
|
-
|
143
140
|
# it is possible that the encrypted attribute is not loaded, eg.
|
144
141
|
# if the record was fetched partially (`User.select(:id).first`).
|
145
142
|
# accessing a not loaded attribute raises an `ActiveModel::MissingAttributeError`.
|
146
|
-
|
143
|
+
if has_attribute?(lockbox_attribute[:encrypted_attribute])
|
144
|
+
begin
|
145
|
+
send(lockbox_attribute[:attribute])
|
146
|
+
rescue ArgumentError => e
|
147
|
+
raise e if e.message != "No decryption key set"
|
148
|
+
end
|
149
|
+
end
|
147
150
|
end
|
148
151
|
super
|
149
152
|
end
|
@@ -230,6 +233,20 @@ module Lockbox
|
|
230
233
|
end
|
231
234
|
|
232
235
|
if ActiveRecord::VERSION::MAJOR >= 6
|
236
|
+
if ActiveRecord::VERSION::STRING.to_f >= 7.2
|
237
|
+
def self.insert(attributes, **options)
|
238
|
+
super(lockbox_map_record_attributes(attributes), **options)
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.insert!(attributes, **options)
|
242
|
+
super(lockbox_map_record_attributes(attributes), **options)
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.upsert(attributes, **options)
|
246
|
+
super(lockbox_map_record_attributes(attributes, check_readonly: true), **options)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
233
250
|
def self.insert_all(attributes, **options)
|
234
251
|
super(lockbox_map_attributes(attributes), **options)
|
235
252
|
end
|
@@ -248,30 +265,37 @@ module Lockbox
|
|
248
265
|
return records unless records.is_a?(Array)
|
249
266
|
|
250
267
|
records.map do |attributes|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
attribute_aliases[n] || n
|
255
|
-
end
|
268
|
+
lockbox_map_record_attributes(attributes, check_readonly: false)
|
269
|
+
end
|
270
|
+
end
|
256
271
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
272
|
+
# private
|
273
|
+
def self.lockbox_map_record_attributes(attributes, check_readonly: false)
|
274
|
+
return attributes unless attributes.is_a?(Hash)
|
275
|
+
|
276
|
+
# transform keys like Active Record
|
277
|
+
attributes = attributes.transform_keys do |key|
|
278
|
+
n = key.to_s
|
279
|
+
attribute_aliases[n] || n
|
280
|
+
end
|
281
|
+
|
282
|
+
lockbox_attributes = self.lockbox_attributes.slice(*attributes.keys.map(&:to_sym))
|
283
|
+
lockbox_attributes.each do |key, lockbox_attribute|
|
284
|
+
attribute = key.to_s
|
285
|
+
# check read only
|
286
|
+
# users should mark both plaintext and ciphertext columns
|
287
|
+
if check_readonly && readonly_attributes.include?(attribute) && !readonly_attributes.include?(lockbox_attribute[:encrypted_attribute].to_s)
|
288
|
+
warn "[lockbox] WARNING: Mark attribute as readonly: #{lockbox_attribute[:encrypted_attribute]}"
|
271
289
|
end
|
272
290
|
|
273
|
-
attributes
|
291
|
+
message = attributes[attribute]
|
292
|
+
attributes.delete(attribute) unless lockbox_attribute[:migrating]
|
293
|
+
encrypted_attribute = lockbox_attribute[:encrypted_attribute]
|
294
|
+
ciphertext = send("generate_#{encrypted_attribute}", message)
|
295
|
+
attributes[encrypted_attribute] = ciphertext
|
274
296
|
end
|
297
|
+
|
298
|
+
attributes
|
275
299
|
end
|
276
300
|
end
|
277
301
|
else
|
@@ -295,7 +319,12 @@ module Lockbox
|
|
295
319
|
end
|
296
320
|
|
297
321
|
# warn on default attributes
|
298
|
-
if
|
322
|
+
if ActiveRecord::VERSION::STRING.to_f >= 7.2
|
323
|
+
# TODO improve
|
324
|
+
if pending_attribute_modifications.any? { |v| v.is_a?(ActiveModel::AttributeRegistration::ClassMethods::PendingDefault) && v.name == name.to_s }
|
325
|
+
warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
|
326
|
+
end
|
327
|
+
elsif attributes_to_define_after_schema_loads.key?(name.to_s)
|
299
328
|
opt = attributes_to_define_after_schema_loads[name.to_s][1]
|
300
329
|
|
301
330
|
has_default =
|
@@ -347,6 +376,26 @@ module Lockbox
|
|
347
376
|
serialize name, Array
|
348
377
|
end
|
349
378
|
end
|
379
|
+
elsif ActiveRecord::VERSION::STRING.to_f >= 7.2
|
380
|
+
decorate_attributes([name]) do |attr_name, cast_type|
|
381
|
+
if cast_type.instance_of?(ActiveRecord::Type::Value)
|
382
|
+
original_type = pending_attribute_modifications.find { |v| v.is_a?(ActiveModel::AttributeRegistration::ClassMethods::PendingType) && v.name == original_name.to_s && !v.type.nil? }&.type
|
383
|
+
if original_type
|
384
|
+
original_type
|
385
|
+
elsif options[:migrating]
|
386
|
+
cast_type
|
387
|
+
else
|
388
|
+
ActiveRecord::Type::String.new
|
389
|
+
end
|
390
|
+
elsif cast_type.is_a?(ActiveRecord::Type::Serialized) && cast_type.subtype.instance_of?(ActiveModel::Type::Value)
|
391
|
+
# hack to set string type after serialize
|
392
|
+
# otherwise, type gets set to ActiveModel::Type::Value
|
393
|
+
# which always returns false for changed_in_place?
|
394
|
+
ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, cast_type.coder)
|
395
|
+
else
|
396
|
+
cast_type
|
397
|
+
end
|
398
|
+
end
|
350
399
|
elsif !attributes_to_define_after_schema_loads.key?(name.to_s)
|
351
400
|
# when migrating it's best to specify the type directly
|
352
401
|
# however, we can try to use the original type if its already defined
|
@@ -360,8 +409,7 @@ module Lockbox
|
|
360
409
|
attribute name, :string
|
361
410
|
end
|
362
411
|
else
|
363
|
-
# hack for Active Record 6.1
|
364
|
-
# to set string type after serialize
|
412
|
+
# hack for Active Record 6.1+ to set string type after serialize
|
365
413
|
# otherwise, type gets set to ActiveModel::Type::Value
|
366
414
|
# which always returns false for changed_in_place?
|
367
415
|
# earlier versions of Active Record take the previous code path
|
@@ -447,12 +495,12 @@ module Lockbox
|
|
447
495
|
# decrypt first for dirty tracking
|
448
496
|
# don't raise error if can't decrypt previous
|
449
497
|
# don't try to decrypt if no decryption key given
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
498
|
+
begin
|
499
|
+
send(name)
|
500
|
+
rescue Lockbox::DecryptionError
|
501
|
+
warn "[lockbox] Decrypting previous value failed"
|
502
|
+
rescue ArgumentError => e
|
503
|
+
raise e if e.message != "No decryption key set"
|
456
504
|
end
|
457
505
|
|
458
506
|
send("lockbox_direct_#{name}=", message)
|
@@ -669,7 +717,8 @@ module Lockbox
|
|
669
717
|
end
|
670
718
|
|
671
719
|
def lockbox_encrypts(*attributes, **options)
|
672
|
-
ActiveSupport::
|
720
|
+
deprecator = ActiveSupport::VERSION::STRING.to_f >= 7.2 ? ActiveSupport.deprecator : ActiveSupport::Deprecation
|
721
|
+
deprecator.warn("`#{__callee__}` is deprecated in favor of `has_encrypted`")
|
673
722
|
has_encrypted(*attributes, **options)
|
674
723
|
end
|
675
724
|
|
data/lib/lockbox/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: andrew@ankane.org
|
@@ -58,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
58
|
- !ruby/object:Gem::Version
|
59
59
|
version: '0'
|
60
60
|
requirements: []
|
61
|
-
rubygems_version: 3.5.
|
61
|
+
rubygems_version: 3.5.11
|
62
62
|
signing_key:
|
63
63
|
specification_version: 4
|
64
64
|
summary: Modern encryption for Ruby and Rails
|