lockbox 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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