lockbox 1.4.0 → 2.1.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: c227a11280d70eaaa03e7c81649bee696c2c88ab9a1825503f7db4e9af5d2d12
4
- data.tar.gz: 52a2969568229aefa391a093c93ae1771d021ddc2396d3e4f65ec2f509a82c41
3
+ metadata.gz: 732b29630e94d7e05292a10ff4106f05152418a335fb7a5e7da9b5a632c05856
4
+ data.tar.gz: 8d7d40d7d4f8bb5ecc4737c4664a87bfc33d1f331b0a46d36ebc6a3ce5573ee2
5
5
  SHA512:
6
- metadata.gz: 1734e1db6d559ed4221e81b30ca6a13db348a6089f58581376ce4cf16019a5787f8dd1b80abd1534ef2c82e294db4db1d8cb30089413d4e2e009544d3185d350
7
- data.tar.gz: d5bdb1947c5fac19038fe21651bf35d32d675eba024569ec085b015ad991a9c1ea5e65437df908f78e0083486726032571c6dee364fe92a95a7447e33495cf83
6
+ metadata.gz: 11ba243e0e997a3140a5f3682f38023b9dcf29fdd9c181ea9f693404b4204bd7e83a4caef669126ff9fe432882773ff8f52e129117c201f0dc6eae7de9539849
7
+ data.tar.gz: 206197fbff415597cb816f77d9354fc8b2ee0c60ceccfb1b5414a44cf19a14b1b9224e09d1c7b0148a9ecb4b0d6673f70f2f35008b6a105bab8e02a55fdf81ce
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 2.1.0 (2025-10-15)
2
+
3
+ - Added warning for `download_chunk` method
4
+ - Fixed error for `download` method with block
5
+ - Dropped support for Active Record < 7.1 and Ruby < 3.2
6
+
7
+ ## 2.0.1 (2024-12-29)
8
+
9
+ - Added support for Ruby 3.4
10
+
11
+ ## 2.0.0 (2024-10-26)
12
+
13
+ - Improved `attributes`, `attribute_names`, and `has_attribute?` when ciphertext attributes not loaded
14
+ - Removed deprecated `lockbox_encrypts` (use `has_encrypted` instead)
15
+ - Dropped support for Active Record < 7 and Ruby < 3.1
16
+ - Dropped support for Mongoid < 8
17
+
18
+ ## 1.4.1 (2024-09-09)
19
+
20
+ - Fixed error message for previews for Active Storage 7.1.4
21
+
1
22
  ## 1.4.0 (2024-08-09)
2
23
 
3
24
  - Added support for Active Record 7.2
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2024 Andrew Kane
3
+ Copyright (c) 2018-2025 Andrew Kane
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -35,7 +35,7 @@ Set the following environment variable with your key (you can use this one in de
35
35
  LOCKBOX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000
36
36
  ```
37
37
 
38
- or add it to your credentials for each environment (`rails credentials:edit --environment <env>` for Rails 6+)
38
+ or add it to your credentials for each environment (`rails credentials:edit --environment <env>`)
39
39
 
40
40
  ```yml
41
41
  lockbox:
@@ -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[7.1]
75
+ class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.0]
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[7.1]
254
+ class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[8.0]
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[7.1]
385
+ class AddLicenseToUsers < ActiveRecord::Migration[8.0]
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[7.1]
913
+ class AddEmailCiphertextToUsers < ActiveRecord::Migration[8.0]
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[7.1]
964
+ class MigrateToLockbox < ActiveRecord::Migration[8.0]
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[7.1]
997
+ class RemovePreviousEncryptedColumns < ActiveRecord::Migration[8.0]
998
998
  def change
999
999
  remove_column :users, :encrypted_name, :text
1000
1000
  remove_column :users, :encrypted_name_iv, :text
@@ -1004,18 +1004,6 @@ class RemovePreviousEncryptedColumns < ActiveRecord::Migration[7.1]
1004
1004
  end
1005
1005
  ```
1006
1006
 
1007
- ## Upgrading
1008
-
1009
- ### 1.0.0
1010
-
1011
- `encrypts` is now deprecated in favor of `has_encrypted` to avoid conflicting with Active Record encryption.
1012
-
1013
- ```ruby
1014
- class User < ApplicationRecord
1015
- has_encrypted :email
1016
- end
1017
- ```
1018
-
1019
1007
  ## History
1020
1008
 
1021
1009
  View the [changelog](https://github.com/ankane/lockbox/blob/master/CHANGELOG.md)
@@ -29,11 +29,7 @@ module Lockbox
29
29
  # use connection_config instead of connection.adapter
30
30
  # so database connection isn't needed
31
31
  def adapter
32
- if ActiveRecord::VERSION::STRING.to_f >= 6.1
33
- ActiveRecord::Base.connection_db_config.adapter.to_s
34
- else
35
- ActiveRecord::Base.connection_config[:adapter].to_s
36
- end
32
+ ActiveRecord::Base.connection_db_config.adapter.to_s
37
33
  end
38
34
  end
39
35
  end
@@ -34,13 +34,6 @@ module Lockbox
34
34
  end
35
35
 
36
36
  module AttachedOne
37
- if ActiveStorage::VERSION::MAJOR < 6
38
- def attach(attachable)
39
- attachable = encrypt_attachable(attachable) if encrypted?
40
- super(attachable)
41
- end
42
- end
43
-
44
37
  def rotate_encryption!
45
38
  raise "Not encrypted" unless encrypted?
46
39
 
@@ -51,19 +44,6 @@ module Lockbox
51
44
  end
52
45
 
53
46
  module AttachedMany
54
- if ActiveStorage::VERSION::MAJOR < 6
55
- def attach(*attachables)
56
- if encrypted?
57
- attachables =
58
- attachables.flatten.collect do |attachable|
59
- encrypt_attachable(attachable)
60
- end
61
- end
62
-
63
- super(attachables)
64
- end
65
- end
66
-
67
47
  def rotate_encryption!
68
48
  raise "Not encrypted" unless encrypted?
69
49
 
@@ -100,7 +80,7 @@ module Lockbox
100
80
 
101
81
  module Attachment
102
82
  def download
103
- result = super
83
+ result = super(&nil)
104
84
 
105
85
  options = Utils.encrypted_options(record, name)
106
86
  # only trust the metadata when migrating
@@ -111,45 +91,70 @@ module Lockbox
111
91
  result = Utils.decrypt_result(record, name, options, result)
112
92
  end
113
93
 
114
- result
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
103
+ end
104
+
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)
108
+ super
115
109
  end
116
110
 
117
- def variant(*args)
111
+ def variant(...)
118
112
  raise Lockbox::Error, "Variant not supported for encrypted files" if Utils.encrypted_options(record, name)
119
113
  super
120
114
  end
121
115
 
122
- def preview(*args)
116
+ def preview(...)
123
117
  raise Lockbox::Error, "Preview not supported for encrypted files" if Utils.encrypted_options(record, name)
124
118
  super
125
119
  end
126
120
 
127
- if ActiveStorage::VERSION::MAJOR >= 6
128
- def open(**options)
129
- blob.open(**options) do |file|
130
- options = Utils.encrypted_options(record, name)
131
- # only trust the metadata when migrating
132
- # as earlier versions of Lockbox won't have it
133
- # and it's not a good practice to trust modifiable data
134
- encrypted = options && (!options[:migrating] || blob.metadata["encrypted"])
135
- if encrypted
136
- result = Utils.decrypt_result(record, name, options, file.read)
137
- file.rewind
138
- # truncate may not be available on all platforms
139
- # according to the Ruby docs
140
- # may need to create a new temp file instead
141
- file.truncate(0)
142
- file.write(result)
143
- file.rewind
144
- end
145
-
146
- yield file
121
+ if ActiveStorage::VERSION::STRING.to_f == 7.1 && ActiveStorage.version >= "7.1.4"
122
+ def transform_variants_later
123
+ blob.instance_variable_set(:@lockbox_encrypted, true) if Utils.encrypted_options(record, name)
124
+ super
125
+ end
126
+ end
127
+
128
+ def open(**options)
129
+ blob.open(**options) do |file|
130
+ options = Utils.encrypted_options(record, name)
131
+ # only trust the metadata when migrating
132
+ # as earlier versions of Lockbox won't have it
133
+ # and it's not a good practice to trust modifiable data
134
+ encrypted = options && (!options[:migrating] || blob.metadata["encrypted"])
135
+ if encrypted
136
+ result = Utils.decrypt_result(record, name, options, file.read)
137
+ file.rewind
138
+ # truncate may not be available on all platforms
139
+ # according to the Ruby docs
140
+ # may need to create a new temp file instead
141
+ file.truncate(0)
142
+ file.write(result)
143
+ file.rewind
147
144
  end
145
+
146
+ yield file
148
147
  end
149
148
  end
150
149
  end
151
150
 
152
151
  module Blob
152
+ if ActiveStorage::VERSION::STRING.to_f == 7.1 && ActiveStorage.version >= "7.1.4"
153
+ def preview_image_needed_before_processing_variants?
154
+ !instance_variable_defined?(:@lockbox_encrypted) && super
155
+ end
156
+ end
157
+
153
158
  private
154
159
 
155
160
  def extract_content_type(io)
@@ -15,12 +15,12 @@ module Lockbox
15
15
  def encrypt(message, **options)
16
16
  message = check_string(message)
17
17
  ciphertext = @boxes.first.encrypt(message, **options)
18
- ciphertext = Base64.strict_encode64(ciphertext) if @encode
18
+ ciphertext = [ciphertext].pack("m0") if @encode
19
19
  ciphertext
20
20
  end
21
21
 
22
22
  def decrypt(ciphertext, **options)
23
- ciphertext = Base64.decode64(ciphertext) if @encode
23
+ ciphertext = ciphertext.unpack1("m") if @encode
24
24
  ciphertext = check_string(ciphertext)
25
25
 
26
26
  # ensure binary
data/lib/lockbox/model.rb CHANGED
@@ -60,7 +60,7 @@ module Lockbox
60
60
  class_eval do
61
61
  # Lockbox uses custom inspect
62
62
  # but this could be useful for other gems
63
- if activerecord && ActiveRecord::VERSION::MAJOR >= 6
63
+ if activerecord
64
64
  # only add virtual attribute
65
65
  # need to use regexp since strings do partial matching
66
66
  # also, need to use += instead of <<
@@ -114,12 +114,6 @@ module Lockbox
114
114
  k = lockbox_encrypted_attributes[k]
115
115
  elsif values.key?(k)
116
116
  v = respond_to?(:attribute_for_inspect) ? attribute_for_inspect(k) : values[k].inspect
117
-
118
- # fix for https://github.com/rails/rails/issues/40725
119
- # TODO only apply to Active Record 6.0
120
- if respond_to?(:inspection_filter, true) && v != "nil"
121
- v = inspection_filter.filter_param(k, v)
122
- end
123
117
  else
124
118
  next
125
119
  end
@@ -148,7 +142,40 @@ module Lockbox
148
142
  end
149
143
  end
150
144
  end
151
- super
145
+
146
+ # remove attributes that do not have a ciphertext attribute
147
+ attributes = super
148
+ self.class.lockbox_attributes.each do |k, lockbox_attribute|
149
+ if !attributes.include?(lockbox_attribute[:encrypted_attribute].to_s)
150
+ attributes.delete(k.to_s)
151
+ attributes.delete(lockbox_attribute[:attribute])
152
+ end
153
+ end
154
+ attributes
155
+ end
156
+
157
+ # remove attribute names that do not have a ciphertext attribute
158
+ def attribute_names
159
+ # hash preserves key order
160
+ names_set = super.to_h { |v| [v, true] }
161
+ self.class.lockbox_attributes.each do |k, lockbox_attribute|
162
+ if !names_set.include?(lockbox_attribute[:encrypted_attribute].to_s)
163
+ names_set.delete(k.to_s)
164
+ names_set.delete(lockbox_attribute[:attribute])
165
+ end
166
+ end
167
+ names_set.keys
168
+ end
169
+
170
+ # check the ciphertext attribute for encrypted attributes
171
+ def has_attribute?(attr_name)
172
+ attr_name = attr_name.to_s
173
+ _, lockbox_attribute = self.class.lockbox_attributes.find { |_, la| la[:attribute] == attr_name }
174
+ if lockbox_attribute
175
+ super(lockbox_attribute[:encrypted_attribute])
176
+ else
177
+ super
178
+ end
152
179
  end
153
180
 
154
181
  # needed for in-place modifications
@@ -232,71 +259,69 @@ module Lockbox
232
259
  result
233
260
  end
234
261
 
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
262
+ if ActiveRecord::VERSION::STRING.to_f >= 7.2
263
+ def self.insert(attributes, **options)
264
+ super(lockbox_map_record_attributes(attributes), **options)
248
265
  end
249
266
 
250
- def self.insert_all(attributes, **options)
251
- super(lockbox_map_attributes(attributes), **options)
267
+ def self.insert!(attributes, **options)
268
+ super(lockbox_map_record_attributes(attributes), **options)
252
269
  end
253
270
 
254
- def self.insert_all!(attributes, **options)
255
- super(lockbox_map_attributes(attributes), **options)
271
+ def self.upsert(attributes, **options)
272
+ super(lockbox_map_record_attributes(attributes, check_readonly: true), **options)
256
273
  end
274
+ end
257
275
 
258
- def self.upsert_all(attributes, **options)
259
- super(lockbox_map_attributes(attributes, check_readonly: true), **options)
260
- end
276
+ def self.insert_all(attributes, **options)
277
+ super(lockbox_map_attributes(attributes), **options)
278
+ end
261
279
 
262
- # private
263
- # does not try to handle :returning option for simplicity
264
- def self.lockbox_map_attributes(records, check_readonly: false)
265
- return records unless records.is_a?(Array)
280
+ def self.insert_all!(attributes, **options)
281
+ super(lockbox_map_attributes(attributes), **options)
282
+ end
266
283
 
267
- records.map do |attributes|
268
- lockbox_map_record_attributes(attributes, check_readonly: false)
269
- end
270
- end
284
+ def self.upsert_all(attributes, **options)
285
+ super(lockbox_map_attributes(attributes, check_readonly: true), **options)
286
+ end
271
287
 
272
- # private
273
- def self.lockbox_map_record_attributes(attributes, check_readonly: false)
274
- return attributes unless attributes.is_a?(Hash)
288
+ # private
289
+ # does not try to handle :returning option for simplicity
290
+ def self.lockbox_map_attributes(records, check_readonly: false)
291
+ return records unless records.is_a?(Array)
275
292
 
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
293
+ records.map do |attributes|
294
+ lockbox_map_record_attributes(attributes, check_readonly: false)
295
+ end
296
+ end
281
297
 
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]}"
289
- end
298
+ # private
299
+ def self.lockbox_map_record_attributes(attributes, check_readonly: false)
300
+ return attributes unless attributes.is_a?(Hash)
301
+
302
+ # transform keys like Active Record
303
+ attributes = attributes.transform_keys do |key|
304
+ n = key.to_s
305
+ attribute_aliases[n] || n
306
+ end
290
307
 
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
308
+ lockbox_attributes = self.lockbox_attributes.slice(*attributes.keys.map(&:to_sym))
309
+ lockbox_attributes.each do |key, lockbox_attribute|
310
+ attribute = key.to_s
311
+ # check read only
312
+ # users should mark both plaintext and ciphertext columns
313
+ if check_readonly && readonly_attributes.include?(attribute) && !readonly_attributes.include?(lockbox_attribute[:encrypted_attribute].to_s)
314
+ warn "[lockbox] WARNING: Mark attribute as readonly: #{lockbox_attribute[:encrypted_attribute]}"
296
315
  end
297
316
 
298
- attributes
317
+ message = attributes[attribute]
318
+ attributes.delete(attribute) unless lockbox_attribute[:migrating]
319
+ encrypted_attribute = lockbox_attribute[:encrypted_attribute]
320
+ ciphertext = send("generate_#{encrypted_attribute}", message)
321
+ attributes[encrypted_attribute] = ciphertext
299
322
  end
323
+
324
+ attributes
300
325
  end
301
326
  else
302
327
  def reload
@@ -327,13 +352,8 @@ module Lockbox
327
352
  elsif attributes_to_define_after_schema_loads.key?(name.to_s)
328
353
  opt = attributes_to_define_after_schema_loads[name.to_s][1]
329
354
 
330
- has_default =
331
- if ActiveRecord::VERSION::MAJOR >= 7
332
- # not ideal, since NO_DEFAULT_PROVIDED is private
333
- opt != ActiveRecord::Attributes::ClassMethods.const_get(:NO_DEFAULT_PROVIDED)
334
- else
335
- opt.is_a?(Hash) && opt.key?(:default)
336
- end
355
+ # not ideal, since NO_DEFAULT_PROVIDED is private
356
+ has_default = opt != ActiveRecord::Attributes::ClassMethods.const_get(:NO_DEFAULT_PROVIDED)
337
357
 
338
358
  if has_default
339
359
  warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
@@ -357,24 +377,13 @@ module Lockbox
357
377
 
358
378
  attribute name, attribute_type
359
379
 
360
- if ActiveRecord::VERSION::STRING.to_f >= 7.1
361
- case options[:type]
362
- when :json
363
- serialize name, coder: JSON
364
- when :hash
365
- serialize name, type: Hash, coder: default_column_serializer || YAML
366
- when :array
367
- serialize name, type: Array, coder: default_column_serializer || YAML
368
- end
369
- else
370
- case options[:type]
371
- when :json
372
- serialize name, JSON
373
- when :hash
374
- serialize name, Hash
375
- when :array
376
- serialize name, Array
377
- end
380
+ case options[:type]
381
+ when :json
382
+ serialize name, coder: JSON
383
+ when :hash
384
+ serialize name, type: Hash, coder: default_column_serializer || YAML
385
+ when :array
386
+ serialize name, type: Array, coder: default_column_serializer || YAML
378
387
  end
379
388
  elsif ActiveRecord::VERSION::STRING.to_f >= 7.2
380
389
  decorate_attributes([name]) do |attr_name, cast_type|
@@ -408,21 +417,14 @@ module Lockbox
408
417
  else
409
418
  attribute name, :string
410
419
  end
411
- else
420
+ elsif attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
412
421
  # hack for Active Record 6.1+ to set string type after serialize
413
422
  # otherwise, type gets set to ActiveModel::Type::Value
414
423
  # which always returns false for changed_in_place?
415
424
  # earlier versions of Active Record take the previous code path
416
- if ActiveRecord::VERSION::STRING.to_f >= 7.0 && attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
417
- attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call(nil)
418
- if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
419
- attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
420
- end
421
- elsif ActiveRecord::VERSION::STRING.to_f >= 6.1 && attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
422
- attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call
423
- if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
424
- attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
425
- end
425
+ attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call(nil)
426
+ if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
427
+ attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
426
428
  end
427
429
  end
428
430
 
@@ -581,7 +583,6 @@ module Lockbox
581
583
  case options[:type]
582
584
  when :boolean
583
585
  message = ActiveRecord::Type::Boolean.new.serialize(message)
584
- message = nil if message == "" # for Active Record < 5.2
585
586
  message = message ? "t" : "f" unless message.nil?
586
587
  when :date
587
588
  message = ActiveRecord::Type::Date.new.serialize(message)
@@ -589,7 +590,6 @@ module Lockbox
589
590
  message = message.strftime("%Y-%m-%d") unless message.nil?
590
591
  when :datetime
591
592
  message = ActiveRecord::Type::DateTime.new.serialize(message)
592
- message = nil unless message.respond_to?(:iso8601) # for Active Record < 5.2
593
593
  message = message.iso8601(9) unless message.nil?
594
594
  when :time
595
595
  message = ActiveRecord::Type::Time.new.serialize(message)
@@ -606,14 +606,7 @@ module Lockbox
606
606
  # double precision, big endian
607
607
  message = [message].pack("G") unless message.nil?
608
608
  when :decimal
609
- message =
610
- if ActiveRecord::VERSION::MAJOR >= 6
611
- ActiveRecord::Type::Decimal.new.serialize(message)
612
- else
613
- # issue with serialize in Active Record < 6
614
- # https://github.com/rails/rails/commit/a741208f80dd33420a56486bd9ed2b0b9862234a
615
- ActiveRecord::Type::Decimal.new.cast(message)
616
- end
609
+ message = ActiveRecord::Type::Decimal.new.serialize(message)
617
610
  # Postgres stores 4 decimal digits in 2 bytes
618
611
  # plus 3 to 8 bytes of overhead
619
612
  # but use string for simplicity
@@ -716,12 +709,6 @@ module Lockbox
716
709
  end
717
710
  end
718
711
 
719
- def lockbox_encrypts(*attributes, **options)
720
- deprecator = ActiveSupport::VERSION::STRING.to_f >= 7.2 ? ActiveSupport.deprecator : ActiveSupport::Deprecation
721
- deprecator.warn("`#{__callee__}` is deprecated in favor of `has_encrypted`")
722
- has_encrypted(*attributes, **options)
723
- end
724
-
725
712
  module Attached
726
713
  def encrypts_attached(*attributes, **options)
727
714
  attributes.each do |name|
@@ -12,32 +12,15 @@ module Lockbox
12
12
  require "lockbox/active_storage_extensions"
13
13
 
14
14
  ActiveStorage::Attached.prepend(Lockbox::ActiveStorageExtensions::Attached)
15
- if ActiveStorage::VERSION::MAJOR >= 6
16
- ActiveStorage::Attached::Changes::CreateOne.prepend(Lockbox::ActiveStorageExtensions::CreateOne)
17
- end
15
+ ActiveStorage::Attached::Changes::CreateOne.prepend(Lockbox::ActiveStorageExtensions::CreateOne)
18
16
  ActiveStorage::Attached::One.prepend(Lockbox::ActiveStorageExtensions::AttachedOne)
19
17
  ActiveStorage::Attached::Many.prepend(Lockbox::ActiveStorageExtensions::AttachedMany)
20
18
 
21
- # use load hooks when possible
22
- if ActiveStorage::VERSION::MAJOR >= 7
23
- ActiveSupport.on_load(:active_storage_attachment) do
24
- prepend Lockbox::ActiveStorageExtensions::Attachment
25
- end
26
- ActiveSupport.on_load(:active_storage_blob) do
27
- prepend Lockbox::ActiveStorageExtensions::Blob
28
- end
29
- elsif ActiveStorage::VERSION::MAJOR >= 6
30
- ActiveSupport.on_load(:active_storage_attachment) do
31
- include Lockbox::ActiveStorageExtensions::Attachment
32
- end
33
- ActiveSupport.on_load(:active_storage_blob) do
34
- prepend Lockbox::ActiveStorageExtensions::Blob
35
- end
36
- else
37
- app.config.to_prepare do
38
- ActiveStorage::Attachment.include(Lockbox::ActiveStorageExtensions::Attachment)
39
- ActiveStorage::Blob.prepend(Lockbox::ActiveStorageExtensions::Blob)
40
- end
19
+ ActiveSupport.on_load(:active_storage_attachment) do
20
+ prepend Lockbox::ActiveStorageExtensions::Attachment
21
+ end
22
+ ActiveSupport.on_load(:active_storage_blob) do
23
+ prepend Lockbox::ActiveStorageExtensions::Blob
41
24
  end
42
25
  end
43
26
  end
@@ -1,3 +1,3 @@
1
1
  module Lockbox
2
- VERSION = "1.4.0"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/lockbox.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # stdlib
2
- require "base64"
3
2
  require "openssl"
4
3
  require "securerandom"
5
4
  require "stringio"
@@ -99,8 +98,12 @@ end
99
98
  if defined?(ActiveSupport.on_load)
100
99
  ActiveSupport.on_load(:active_record) do
101
100
  ar_version = ActiveRecord::VERSION::STRING.to_f
102
- if ar_version < 5.2
103
- if ar_version >= 5
101
+ if ar_version < 7.1
102
+ if ar_version >= 7.0
103
+ raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 2.1"
104
+ elsif ar_version >= 5.2
105
+ raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 2"
106
+ elsif ar_version >= 5
104
107
  raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 0.7"
105
108
  else
106
109
  raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} not supported"
@@ -109,12 +112,19 @@ if defined?(ActiveSupport.on_load)
109
112
 
110
113
  extend Lockbox::Model
111
114
  extend Lockbox::Model::Attached
112
- singleton_class.alias_method(:encrypts, :lockbox_encrypts) if ActiveRecord::VERSION::MAJOR < 7
113
115
  ActiveRecord::Relation.prepend Lockbox::Calculations
114
116
  end
115
117
 
116
118
  ActiveSupport.on_load(:mongoid) do
119
+ mongoid_version = Mongoid::VERSION.to_i
120
+ if mongoid_version < 8
121
+ if mongoid_version >= 6
122
+ raise Lockbox::Error, "Mongoid #{Mongoid::VERSION} requires Lockbox < 2"
123
+ else
124
+ raise Lockbox::Error, "Mongoid #{Mongoid::VERSION} not supported"
125
+ end
126
+ end
127
+
117
128
  Mongoid::Document::ClassMethods.include(Lockbox::Model)
118
- Mongoid::Document::ClassMethods.alias_method(:encrypts, :lockbox_encrypts)
119
129
  end
120
130
  end
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lockbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-08-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email: andrew@ankane.org
15
13
  executables: []
16
14
  extensions: []
@@ -43,7 +41,6 @@ homepage: https://github.com/ankane/lockbox
43
41
  licenses:
44
42
  - MIT
45
43
  metadata: {}
46
- post_install_message:
47
44
  rdoc_options: []
48
45
  require_paths:
49
46
  - lib
@@ -51,15 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
51
48
  requirements:
52
49
  - - ">="
53
50
  - !ruby/object:Gem::Version
54
- version: '2.6'
51
+ version: '3.2'
55
52
  required_rubygems_version: !ruby/object:Gem::Requirement
56
53
  requirements:
57
54
  - - ">="
58
55
  - !ruby/object:Gem::Version
59
56
  version: '0'
60
57
  requirements: []
61
- rubygems_version: 3.5.11
62
- signing_key:
58
+ rubygems_version: 3.6.9
63
59
  specification_version: 4
64
60
  summary: Modern encryption for Ruby and Rails
65
61
  test_files: []