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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a238a9d70f5c46cabf8824957f739beb662730a2039696da29b6bc2f7a23462
4
- data.tar.gz: eaebb7f1bd209792eee41166c6b5878b99ea0ee9436d030bf76ff13c8c56e4e2
3
+ metadata.gz: c227a11280d70eaaa03e7c81649bee696c2c88ab9a1825503f7db4e9af5d2d12
4
+ data.tar.gz: 52a2969568229aefa391a093c93ae1771d021ddc2396d3e4f65ec2f509a82c41
5
5
  SHA512:
6
- metadata.gz: cc5a1953cbc1493d5eba15ef0d0aed760ee6bd5d0f50e9810d522d57e46e2426f1a8573284834b262208fb6b20638ce15f0f52b5eb1c74dd9f5cc79c9124d6d1
7
- data.tar.gz: ab01a6601a0317e0182f49bff4410ab232f88a88a5e65b68d20c6b761a38c3777a527d66c6c1fc0896ce23fdeacb493ec5487a494bf310760581f0ab511c3be3
6
+ metadata.gz: 1734e1db6d559ed4221e81b30ca6a13db348a6089f58581376ce4cf16019a5787f8dd1b80abd1534ef2c82e294db4db1d8cb30089413d4e2e009544d3185d350
7
+ data.tar.gz: d5bdb1947c5fac19038fe21651bf35d32d675eba024569ec085b015ad991a9c1ea5e65437df908f78e0083486726032571c6dee364fe92a95a7447e33495cf83
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 1.4.0 (2024-08-09)
2
+
3
+ - Added support for Active Record 7.2
4
+ - Added support for Mongoid 9
5
+ - Fixed error when `decryption_key` option is a proc or symbol and returns `nil`
6
+
1
7
  ## 1.3.3 (2024-02-07)
2
8
 
3
9
  - Added warning for encrypting store attributes
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 = ["email"]
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)
@@ -79,7 +79,7 @@ module Lockbox
79
79
  while uploader.parent_version
80
80
  uploader = uploader.parent_version
81
81
  end
82
- uploader.class.name.sub(/Uploader\z/, "").underscore
82
+ uploader.class.name.delete_suffix("Uploader").underscore
83
83
  end
84
84
  end
85
85
 
@@ -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
- send(lockbox_attribute[:attribute]) if has_attribute?(lockbox_attribute[:encrypted_attribute])
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
- # transform keys like Active Record
252
- attributes = attributes.transform_keys do |key|
253
- n = key.to_s
254
- attribute_aliases[n] || n
255
- end
268
+ lockbox_map_record_attributes(attributes, check_readonly: false)
269
+ end
270
+ end
256
271
 
257
- lockbox_attributes = self.lockbox_attributes.slice(*attributes.keys.map(&:to_sym))
258
- lockbox_attributes.each do |key, lockbox_attribute|
259
- attribute = key.to_s
260
- # check read only
261
- # users should mark both plaintext and ciphertext columns
262
- if check_readonly && readonly_attributes.include?(attribute) && !readonly_attributes.include?(lockbox_attribute[:encrypted_attribute].to_s)
263
- warn "[lockbox] WARNING: Mark attribute as readonly: #{lockbox_attribute[:encrypted_attribute]}"
264
- end
265
-
266
- message = attributes[attribute]
267
- attributes.delete(attribute) unless lockbox_attribute[:migrating]
268
- encrypted_attribute = lockbox_attribute[:encrypted_attribute]
269
- ciphertext = send("generate_#{encrypted_attribute}", message)
270
- attributes[encrypted_attribute] = ciphertext
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 attributes_to_define_after_schema_loads.key?(name.to_s)
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
- unless options[:algorithm] == "hybrid" && options[:decryption_key].nil?
451
- begin
452
- send(name)
453
- rescue Lockbox::DecryptionError
454
- warn "[lockbox] Decrypting previous value failed"
455
- end
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::Deprecation.warn("`#{__callee__}` is deprecated in favor of `has_encrypted`")
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
 
@@ -1,3 +1,3 @@
1
1
  module Lockbox
2
- VERSION = "1.3.3"
2
+ VERSION = "1.4.0"
3
3
  end
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.3.3
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-02-07 00:00:00.000000000 Z
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.3
61
+ rubygems_version: 3.5.11
62
62
  signing_key:
63
63
  specification_version: 4
64
64
  summary: Modern encryption for Ruby and Rails