lockbox 0.6.0 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/LICENSE.txt +1 -1
- data/README.md +40 -25
- data/lib/generators/lockbox/audits_generator.rb +11 -3
- data/lib/lockbox.rb +11 -2
- data/lib/lockbox/carrier_wave_extensions.rb +14 -3
- data/lib/lockbox/model.rb +72 -7
- data/lib/lockbox/railtie.rb +13 -1
- data/lib/lockbox/version.rb +1 -1
- metadata +5 -187
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c45d2348a69188c159cef722e1bcf20cbb47091d41a18dea9aad623479138f9b
|
4
|
+
data.tar.gz: 62aa904c4dadd24e8fa727f8d7d8e966d2f9aac8494c05185ecee6bb1129de5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f65d019fd987015f246fa3fb901ff23047663cce1a09edeca6e1a69e39e06b6442ec74a12057bc1850704414bfd8ca283da55a1990fa130119faba9df01e136f
|
7
|
+
data.tar.gz: 98c7b76529683742621eea3e9c16b6cd1666b125720da58e1f3babb79d2e6b717e47de97b7593425d3ae106553d896d720e75a43165a88cd79a92a255a65a223
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
## 0.6.5 (2021-07-07)
|
2
|
+
|
3
|
+
- Fixed issue with `pluck` extension not loading in some cases
|
4
|
+
|
5
|
+
## 0.6.4 (2021-04-05)
|
6
|
+
|
7
|
+
- Fixed in place changes in callbacks
|
8
|
+
- Fixed `[]` method for encrypted attributes
|
9
|
+
|
10
|
+
## 0.6.3 (2021-03-30)
|
11
|
+
|
12
|
+
- Fixed empty arrays and hashes
|
13
|
+
- Fixed content type for CarrierWave 2.2.1
|
14
|
+
|
15
|
+
## 0.6.2 (2021-02-08)
|
16
|
+
|
17
|
+
- Added `inet` type
|
18
|
+
- Fixed error when `lockbox` key in Rails credentials has a string value
|
19
|
+
- Fixed deprecation warning with Active Record 6.1
|
20
|
+
|
21
|
+
## 0.6.1 (2020-12-03)
|
22
|
+
|
23
|
+
- Added integration with Rails credentials
|
24
|
+
- Fixed in place changes for Active Record 6.1
|
25
|
+
- Fixed error with `content_type` method for CarrierWave < 2
|
26
|
+
|
1
27
|
## 0.6.0 (2020-12-03)
|
2
28
|
|
3
29
|
- Added `encrypted` flag to Active Storage metadata
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -27,7 +27,7 @@ Generate a key
|
|
27
27
|
Lockbox.generate_key
|
28
28
|
```
|
29
29
|
|
30
|
-
Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production.
|
30
|
+
Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production.
|
31
31
|
|
32
32
|
Set the following environment variable with your key (you can use this one in development)
|
33
33
|
|
@@ -35,10 +35,17 @@ 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+)
|
39
|
+
|
40
|
+
```yml
|
41
|
+
lockbox:
|
42
|
+
master_key: "0000000000000000000000000000000000000000000000000000000000000000"
|
43
|
+
```
|
44
|
+
|
38
45
|
or create `config/initializers/lockbox.rb` with something like
|
39
46
|
|
40
47
|
```ruby
|
41
|
-
Lockbox.master_key = Rails.application.credentials.
|
48
|
+
Lockbox.master_key = Rails.application.credentials.lockbox[:master_key]
|
42
49
|
```
|
43
50
|
|
44
51
|
Then follow the instructions below for the data you want to encrypt.
|
@@ -65,7 +72,7 @@ Then follow the instructions below for the data you want to encrypt.
|
|
65
72
|
Create a migration with:
|
66
73
|
|
67
74
|
```ruby
|
68
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[6.
|
75
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[6.1]
|
69
76
|
def change
|
70
77
|
add_column :users, :email_ciphertext, :text
|
71
78
|
end
|
@@ -114,6 +121,7 @@ class User < ApplicationRecord
|
|
114
121
|
encrypts :properties, type: :json
|
115
122
|
encrypts :settings, type: :hash
|
116
123
|
encrypts :messages, type: :array
|
124
|
+
encrypts :ip, type: :inet
|
117
125
|
end
|
118
126
|
```
|
119
127
|
|
@@ -240,7 +248,7 @@ User.decrypt_email_ciphertext(user.email_ciphertext)
|
|
240
248
|
Create a migration with:
|
241
249
|
|
242
250
|
```ruby
|
243
|
-
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[6.
|
251
|
+
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[6.1]
|
244
252
|
def change
|
245
253
|
add_column :action_text_rich_texts, :body_ciphertext, :text
|
246
254
|
end
|
@@ -328,9 +336,7 @@ def license
|
|
328
336
|
end
|
329
337
|
```
|
330
338
|
|
331
|
-
#### Migrating Existing Files
|
332
|
-
|
333
|
-
**Note:** This feature is experimental. Please try it in a non-production environment and [share](https://github.com/ankane/lockbox/issues/44) how it goes.
|
339
|
+
#### Migrating Existing Files
|
334
340
|
|
335
341
|
Lockbox makes it easy to encrypt existing files without downtime.
|
336
342
|
|
@@ -371,7 +377,7 @@ Encryption is applied to all versions after processing.
|
|
371
377
|
You can mount the uploader [as normal](https://github.com/carrierwaveuploader/carrierwave#activerecord). With Active Record, this involves creating a migration:
|
372
378
|
|
373
379
|
```ruby
|
374
|
-
class AddLicenseToUsers < ActiveRecord::Migration[6.
|
380
|
+
class AddLicenseToUsers < ActiveRecord::Migration[6.1]
|
375
381
|
def change
|
376
382
|
add_column :users, :license, :string
|
377
383
|
end
|
@@ -560,12 +566,10 @@ Update your model:
|
|
560
566
|
|
561
567
|
```ruby
|
562
568
|
class User < ApplicationRecord
|
563
|
-
encrypts :email, previous_versions: [{
|
569
|
+
encrypts :email, previous_versions: [{master_key: previous_key}]
|
564
570
|
end
|
565
571
|
```
|
566
572
|
|
567
|
-
Use `master_key` instead of `key` if passing the master key.
|
568
|
-
|
569
573
|
To rotate existing records, use:
|
570
574
|
|
571
575
|
```ruby
|
@@ -579,11 +583,9 @@ Once all records are rotated, you can remove `previous_versions` from the model.
|
|
579
583
|
Update your initializer:
|
580
584
|
|
581
585
|
```ruby
|
582
|
-
Lockbox.encrypts_action_text_body(previous_versions: [{
|
586
|
+
Lockbox.encrypts_action_text_body(previous_versions: [{master_key: previous_key}])
|
583
587
|
```
|
584
588
|
|
585
|
-
Use `master_key` instead of `key` if passing the master key.
|
586
|
-
|
587
589
|
To rotate existing records, use:
|
588
590
|
|
589
591
|
```ruby
|
@@ -598,12 +600,10 @@ Update your model:
|
|
598
600
|
|
599
601
|
```ruby
|
600
602
|
class User < ApplicationRecord
|
601
|
-
encrypts_attached :license, previous_versions: [{
|
603
|
+
encrypts_attached :license, previous_versions: [{master_key: previous_key}]
|
602
604
|
end
|
603
605
|
```
|
604
606
|
|
605
|
-
Use `master_key` instead of `key` if passing the master key.
|
606
|
-
|
607
607
|
To rotate existing files, use:
|
608
608
|
|
609
609
|
```ruby
|
@@ -620,12 +620,10 @@ Update your model:
|
|
620
620
|
|
621
621
|
```ruby
|
622
622
|
class LicenseUploader < CarrierWave::Uploader::Base
|
623
|
-
encrypt previous_versions: [{
|
623
|
+
encrypt previous_versions: [{master_key: previous_key}]
|
624
624
|
end
|
625
625
|
```
|
626
626
|
|
627
|
-
Use `master_key` instead of `key` if passing the master key.
|
628
|
-
|
629
627
|
To rotate existing files, use:
|
630
628
|
|
631
629
|
```ruby
|
@@ -700,7 +698,7 @@ This is the default algorithm. It’s:
|
|
700
698
|
|
701
699
|
Lockbox uses 256-bit keys.
|
702
700
|
|
703
|
-
**For users who do a lot of encryptions:** You should rotate an individual key after 2 billion encryptions to minimize the chance of a [nonce collision](https://www.cryptologie.net/article/402/is-symmetric-security-solved/), which will expose the key. Each database field and file uploader use a different key (derived from the master key) to extend this window.
|
701
|
+
**For users who do a lot of encryptions:** You should rotate an individual key after 2 billion encryptions to minimize the chance of a [nonce collision](https://www.cryptologie.net/article/402/is-symmetric-security-solved/), which will expose the authentication key. Each database field and file uploader use a different key (derived from the master key) to extend this window.
|
704
702
|
|
705
703
|
### XSalsa20
|
706
704
|
|
@@ -989,7 +987,7 @@ lockbox.decrypt(ciphertext, associated_data: "othercontext") # fails
|
|
989
987
|
You can use `binary` columns for the ciphertext instead of `text` columns.
|
990
988
|
|
991
989
|
```ruby
|
992
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[6.
|
990
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[6.1]
|
993
991
|
def change
|
994
992
|
add_column :users, :email_ciphertext, :binary
|
995
993
|
end
|
@@ -1034,7 +1032,7 @@ end
|
|
1034
1032
|
Create a migration with:
|
1035
1033
|
|
1036
1034
|
```ruby
|
1037
|
-
class MigrateToLockbox < ActiveRecord::Migration[6.
|
1035
|
+
class MigrateToLockbox < ActiveRecord::Migration[6.1]
|
1038
1036
|
def change
|
1039
1037
|
add_column :users, :name_ciphertext, :text
|
1040
1038
|
add_column :users, :email_ciphertext, :text
|
@@ -1067,7 +1065,7 @@ end
|
|
1067
1065
|
Then remove the previous gem from your Gemfile and drop its columns.
|
1068
1066
|
|
1069
1067
|
```ruby
|
1070
|
-
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[6.
|
1068
|
+
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[6.1]
|
1071
1069
|
def change
|
1072
1070
|
remove_column :users, :encrypted_name, :text
|
1073
1071
|
remove_column :users, :encrypted_name_iv, :text
|
@@ -1079,12 +1077,29 @@ end
|
|
1079
1077
|
|
1080
1078
|
## Upgrading
|
1081
1079
|
|
1080
|
+
### 0.6.0
|
1081
|
+
|
1082
|
+
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:
|
1083
|
+
|
1084
|
+
```ruby
|
1085
|
+
User.with_attached_license.find_each do |user|
|
1086
|
+
next unless user.license.attached?
|
1087
|
+
|
1088
|
+
metadata = user.license.metadata
|
1089
|
+
unless metadata["encrypted"]
|
1090
|
+
user.license.blob.update!(metadata: metadata.merge("encrypted" => true))
|
1091
|
+
end
|
1092
|
+
end
|
1093
|
+
```
|
1094
|
+
|
1082
1095
|
### 0.3.6
|
1083
1096
|
|
1084
1097
|
0.3.6 makes content type detection more reliable for Active Storage. You can check and update the content type of existing files with:
|
1085
1098
|
|
1086
1099
|
```ruby
|
1087
|
-
User.find_each do |user|
|
1100
|
+
User.with_attached_license.find_each do |user|
|
1101
|
+
next unless user.license.attached?
|
1102
|
+
|
1088
1103
|
license = user.license
|
1089
1104
|
content_type = Marcel::MimeType.for(license.download, name: license.filename.to_s)
|
1090
1105
|
if content_type != license.content_type
|
@@ -16,9 +16,7 @@ module Lockbox
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def data_type
|
19
|
-
|
20
|
-
# so database connection isn't needed
|
21
|
-
case ActiveRecord::Base.connection_config[:adapter].to_s
|
19
|
+
case adapter
|
22
20
|
when /postg/i # postgres, postgis
|
23
21
|
"jsonb"
|
24
22
|
when /mysql/i
|
@@ -27,6 +25,16 @@ module Lockbox
|
|
27
25
|
"text"
|
28
26
|
end
|
29
27
|
end
|
28
|
+
|
29
|
+
# use connection_config instead of connection.adapter
|
30
|
+
# so database connection isn't needed
|
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
|
37
|
+
end
|
30
38
|
end
|
31
39
|
end
|
32
40
|
end
|
data/lib/lockbox.rb
CHANGED
@@ -27,13 +27,22 @@ end
|
|
27
27
|
|
28
28
|
if defined?(ActiveSupport.on_load)
|
29
29
|
ActiveSupport.on_load(:active_record) do
|
30
|
+
# TODO raise error in 0.7.0
|
31
|
+
if ActiveRecord::VERSION::STRING.to_f <= 5.0
|
32
|
+
warn "Active Record version (#{ActiveRecord::VERSION::STRING}) not supported in this version of Lockbox (#{Lockbox::VERSION})"
|
33
|
+
end
|
34
|
+
|
30
35
|
extend Lockbox::Model
|
31
36
|
extend Lockbox::Model::Attached
|
32
|
-
|
37
|
+
# alias_method is private in Ruby < 2.5
|
38
|
+
singleton_class.send(:alias_method, :encrypts, :lockbox_encrypts) if ActiveRecord::VERSION::MAJOR < 7
|
39
|
+
ActiveRecord::Relation.prepend Lockbox::Calculations
|
33
40
|
end
|
34
41
|
|
35
42
|
ActiveSupport.on_load(:mongoid) do
|
36
43
|
Mongoid::Document::ClassMethods.include(Lockbox::Model)
|
44
|
+
# alias_method is private in Ruby < 2.5
|
45
|
+
Mongoid::Document::ClassMethods.send(:alias_method, :encrypts, :lockbox_encrypts)
|
37
46
|
end
|
38
47
|
end
|
39
48
|
|
@@ -101,7 +110,7 @@ module Lockbox
|
|
101
110
|
|
102
111
|
def self.encrypts_action_text_body(**options)
|
103
112
|
ActiveSupport.on_load(:action_text_rich_text) do
|
104
|
-
ActionText::RichText.
|
113
|
+
ActionText::RichText.lockbox_encrypts :body, **options
|
105
114
|
end
|
106
115
|
end
|
107
116
|
end
|
@@ -32,9 +32,17 @@ module Lockbox
|
|
32
32
|
read.bytesize
|
33
33
|
end
|
34
34
|
|
35
|
-
# based on CarrierWave::SanitizedFile#mime_magic_content_type
|
36
35
|
def content_type
|
37
|
-
|
36
|
+
if Gem::Version.new(CarrierWave::VERSION) >= Gem::Version.new("2.2.1")
|
37
|
+
# based on CarrierWave::SanitizedFile#marcel_magic_content_type
|
38
|
+
Marcel::Magic.by_magic(read).try(:type) || "invalid/invalid"
|
39
|
+
elsif CarrierWave::VERSION.to_i >= 2
|
40
|
+
# based on CarrierWave::SanitizedFile#mime_magic_content_type
|
41
|
+
MimeMagic.by_magic(read).try(:type) || "invalid/invalid"
|
42
|
+
else
|
43
|
+
# uses filename
|
44
|
+
super
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
48
|
# disable processing since already processed
|
@@ -98,7 +106,10 @@ module Lockbox
|
|
98
106
|
end
|
99
107
|
|
100
108
|
if CarrierWave::VERSION.to_i > 2
|
101
|
-
raise "CarrierWave version not supported in this version of Lockbox
|
109
|
+
raise "CarrierWave version (#{CarrierWave::VERSION}) not supported in this version of Lockbox (#{Lockbox::VERSION})"
|
110
|
+
elsif CarrierWave::VERSION.to_i < 1
|
111
|
+
# TODO raise error in 0.7.0
|
112
|
+
warn "CarrierWave version (#{CarrierWave::VERSION}) not supported in this version of Lockbox (#{Lockbox::VERSION})"
|
102
113
|
end
|
103
114
|
|
104
115
|
CarrierWave::Uploader::Base.extend(Lockbox::CarrierWaveExtensions)
|
data/lib/lockbox/model.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lockbox
|
2
2
|
module Model
|
3
|
-
def
|
3
|
+
def lockbox_encrypts(*attributes, **options)
|
4
4
|
# support objects
|
5
5
|
# case options[:type]
|
6
6
|
# when Date
|
@@ -22,7 +22,8 @@ module Lockbox
|
|
22
22
|
# end
|
23
23
|
|
24
24
|
custom_type = options[:type].respond_to?(:serialize) && options[:type].respond_to?(:deserialize)
|
25
|
-
|
25
|
+
valid_types = [nil, :string, :boolean, :date, :datetime, :time, :integer, :float, :binary, :json, :hash, :array, :inet]
|
26
|
+
raise ArgumentError, "Unknown type: #{options[:type]}" unless custom_type || valid_types.include?(options[:type])
|
26
27
|
|
27
28
|
activerecord = defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
28
29
|
raise ArgumentError, "Type not supported yet with Mongoid" if options[:type] && !activerecord
|
@@ -148,16 +149,38 @@ module Lockbox
|
|
148
149
|
# needed for in-place modifications
|
149
150
|
# assigned attributes are encrypted on assignment
|
150
151
|
# and then again here
|
151
|
-
|
152
|
+
def lockbox_sync_attributes
|
152
153
|
self.class.lockbox_attributes.each do |_, lockbox_attribute|
|
153
154
|
attribute = lockbox_attribute[:attribute]
|
154
155
|
|
155
|
-
if attribute_changed_in_place?(attribute)
|
156
|
+
if attribute_changed_in_place?(attribute) || (send("#{attribute}_changed?") && !send("#{lockbox_attribute[:encrypted_attribute]}_changed?"))
|
156
157
|
send("#{attribute}=", send(attribute))
|
157
158
|
end
|
158
159
|
end
|
159
160
|
end
|
160
161
|
|
162
|
+
# safety check
|
163
|
+
[:_create_record, :_update_record].each do |method_name|
|
164
|
+
unless private_method_defined?(method_name) || method_defined?(method_name)
|
165
|
+
raise Lockbox::Error, "Expected #{method_name} to be defined. Please report an issue."
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def _create_record(*)
|
170
|
+
lockbox_sync_attributes
|
171
|
+
super
|
172
|
+
end
|
173
|
+
|
174
|
+
def _update_record(*)
|
175
|
+
lockbox_sync_attributes
|
176
|
+
super
|
177
|
+
end
|
178
|
+
|
179
|
+
def [](attr_name)
|
180
|
+
send(attr_name) if self.class.lockbox_attributes.any? { |_, la| la[:attribute] == attr_name.to_s }
|
181
|
+
super
|
182
|
+
end
|
183
|
+
|
161
184
|
def update_columns(attributes)
|
162
185
|
return super unless attributes.is_a?(Hash)
|
163
186
|
|
@@ -193,8 +216,11 @@ module Lockbox
|
|
193
216
|
attributes_to_set.each do |k, v|
|
194
217
|
if respond_to?(:write_attribute_without_type_cast, true)
|
195
218
|
write_attribute_without_type_cast(k, v)
|
196
|
-
|
219
|
+
elsif respond_to?(:raw_write_attribute, true)
|
197
220
|
raw_write_attribute(k, v)
|
221
|
+
else
|
222
|
+
@attributes.write_cast_value(k, v)
|
223
|
+
clear_attribute_change(k)
|
198
224
|
end
|
199
225
|
end
|
200
226
|
|
@@ -247,6 +273,23 @@ module Lockbox
|
|
247
273
|
else
|
248
274
|
attribute name, :string
|
249
275
|
end
|
276
|
+
else
|
277
|
+
# hack for Active Record 6.1
|
278
|
+
# to set string type after serialize
|
279
|
+
# otherwise, type gets set to ActiveModel::Type::Value
|
280
|
+
# which always returns false for changed_in_place?
|
281
|
+
# earlier versions of Active Record take the previous code path
|
282
|
+
if ActiveRecord::VERSION::STRING.to_f >= 7.0 && attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
|
283
|
+
attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call(nil)
|
284
|
+
if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
|
285
|
+
attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
|
286
|
+
end
|
287
|
+
elsif ActiveRecord::VERSION::STRING.to_f >= 6.1 && attributes_to_define_after_schema_loads[name.to_s].first.is_a?(Proc)
|
288
|
+
attribute_type = attributes_to_define_after_schema_loads[name.to_s].first.call
|
289
|
+
if attribute_type.is_a?(ActiveRecord::Type::Serialized) && attribute_type.subtype.nil?
|
290
|
+
attribute name, ActiveRecord::Type::Serialized.new(ActiveRecord::Type::String.new, attribute_type.coder)
|
291
|
+
end
|
292
|
+
end
|
250
293
|
end
|
251
294
|
|
252
295
|
define_method("#{name}_was") do
|
@@ -358,7 +401,11 @@ module Lockbox
|
|
358
401
|
# check for this explicitly as a layer of safety
|
359
402
|
if message.nil? || ((message == {} || message == []) && activerecord && @attributes[name.to_s].value_before_type_cast.nil?)
|
360
403
|
ciphertext = send(encrypted_attribute)
|
361
|
-
|
404
|
+
|
405
|
+
# keep original message for empty hashes and arrays
|
406
|
+
unless ciphertext.nil?
|
407
|
+
message = self.class.send(decrypt_method_name, ciphertext, context: self)
|
408
|
+
end
|
362
409
|
|
363
410
|
if activerecord
|
364
411
|
# set previous attribute so changes populate correctly
|
@@ -370,8 +417,13 @@ module Lockbox
|
|
370
417
|
# decrypt method does type casting
|
371
418
|
if respond_to?(:write_attribute_without_type_cast, true)
|
372
419
|
write_attribute_without_type_cast(name.to_s, message) if !@attributes.frozen?
|
373
|
-
|
420
|
+
elsif respond_to?(:raw_write_attribute, true)
|
374
421
|
raw_write_attribute(name, message) if !@attributes.frozen?
|
422
|
+
else
|
423
|
+
if !@attributes.frozen?
|
424
|
+
@attributes.write_cast_value(name.to_s, message)
|
425
|
+
clear_attribute_change(name)
|
426
|
+
end
|
375
427
|
end
|
376
428
|
else
|
377
429
|
instance_variable_set("@#{name}", message)
|
@@ -414,6 +466,14 @@ module Lockbox
|
|
414
466
|
message = ActiveRecord::Type::Float.new.serialize(message)
|
415
467
|
# double precision, big endian
|
416
468
|
message = [message].pack("G") unless message.nil?
|
469
|
+
when :inet
|
470
|
+
unless message.nil?
|
471
|
+
ip = message.is_a?(IPAddr) ? message : (IPAddr.new(message) rescue nil)
|
472
|
+
# same format as Postgres, with ipv4 padded to 16 bytes
|
473
|
+
# family, netmask, ip
|
474
|
+
# return nil for invalid IP like Active Record
|
475
|
+
message = ip ? [ip.ipv4? ? 0 : 1, ip.prefix, ip.hton].pack("CCa16") : nil
|
476
|
+
end
|
417
477
|
when :string, :binary
|
418
478
|
# do nothing
|
419
479
|
# encrypt will convert to binary
|
@@ -460,6 +520,11 @@ module Lockbox
|
|
460
520
|
when :binary
|
461
521
|
# do nothing
|
462
522
|
# decrypt returns binary string
|
523
|
+
when :inet
|
524
|
+
family, prefix, addr = message.unpack("CCa16")
|
525
|
+
len = family == 0 ? 4 : 16
|
526
|
+
message = IPAddr.new_ntoh(addr.first(len))
|
527
|
+
message.prefix = prefix
|
463
528
|
else
|
464
529
|
# use original name for serialized attributes
|
465
530
|
type = (try(:attribute_types) || {})[original_name.to_s]
|
data/lib/lockbox/railtie.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
module Lockbox
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
initializer "lockbox" do |app|
|
4
|
+
if defined?(Rails.application.credentials)
|
5
|
+
# needs to work when lockbox key has a string value
|
6
|
+
Lockbox.master_key ||= Rails.application.credentials.try(:lockbox).try(:fetch, :master_key, nil)
|
7
|
+
end
|
8
|
+
|
4
9
|
require "lockbox/carrier_wave_extensions" if defined?(CarrierWave)
|
5
10
|
|
6
11
|
if defined?(ActiveStorage)
|
@@ -14,7 +19,14 @@ module Lockbox
|
|
14
19
|
ActiveStorage::Attached::Many.prepend(Lockbox::ActiveStorageExtensions::AttachedMany)
|
15
20
|
|
16
21
|
# use load hooks when possible
|
17
|
-
if ActiveStorage::VERSION::MAJOR >=
|
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
|
18
30
|
ActiveSupport.on_load(:active_storage_attachment) do
|
19
31
|
include Lockbox::ActiveStorageExtensions::Attachment
|
20
32
|
end
|
data/lib/lockbox/version.rb
CHANGED
metadata
CHANGED
@@ -1,199 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: carrierwave
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: combustion
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.3'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.3'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rails
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: minitest
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '5'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '5'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rbnacl
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '6'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '6'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: sqlite3
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: pg
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - ">="
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - ">="
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: mysql2
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: shrine
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">="
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '0'
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - ">="
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: shrine-mongoid
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '0'
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: benchmark-ips
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - ">="
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '0'
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - ">="
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '0'
|
11
|
+
date: 2021-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
195
13
|
description:
|
196
|
-
email: andrew@
|
14
|
+
email: andrew@ankane.org
|
197
15
|
executables: []
|
198
16
|
extensions: []
|
199
17
|
extra_rdoc_files: []
|
@@ -240,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
240
58
|
- !ruby/object:Gem::Version
|
241
59
|
version: '0'
|
242
60
|
requirements: []
|
243
|
-
rubygems_version: 3.
|
61
|
+
rubygems_version: 3.2.3
|
244
62
|
signing_key:
|
245
63
|
specification_version: 4
|
246
64
|
summary: Modern encryption for Ruby and Rails
|