lockbox 0.6.8 → 1.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +57 -169
- data/lib/lockbox/calculations.rb +6 -1
- data/lib/lockbox/carrier_wave_extensions.rb +2 -3
- data/lib/lockbox/model.rb +72 -8
- data/lib/lockbox/version.rb +1 -1
- data/lib/lockbox.rb +33 -31
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 860bb7bcddfde22f980e11185c2afae0453e6547064d4538024a16f7652f953d
|
4
|
+
data.tar.gz: 5c97a4bcde5621234bf3f832d0c4a2eb7ca2a986541cb203b6370170141fe29b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1435f9cd4dac0ea8bbee39d787c2a28c73b72a3de260759c27a5bc6c53f2c2fb344f144fa391742f3415baf7a0ea183e683353773c0eb305e145f1084d11918
|
7
|
+
data.tar.gz: 7623133737e4e465a3c63a0c5bbec337382fcce37469e9685a77578af02218d82b5988a057d3b9dc1d7c1accc9de84014ec9bf32d5ac1f0bc8d15455a80c392c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 1.1.0 (2022-10-09)
|
2
|
+
|
3
|
+
- Added support for `insert`, `insert_all`, `insert_all!`, `upsert`, and `upsert_all`
|
4
|
+
|
5
|
+
## 1.0.0 (2022-06-11)
|
6
|
+
|
7
|
+
- Deprecated `encrypts` in favor of `has_encrypted` to avoid conflicting with Active Record encryption
|
8
|
+
- Deprecated `lockbox_encrypts` in favor of `has_encrypted`
|
9
|
+
- Fixed error with `pluck`
|
10
|
+
- Restored warning for attributes with `default` option
|
11
|
+
- Dropped support for Active Record < 5.2 and Ruby < 2.6
|
12
|
+
|
1
13
|
## 0.6.8 (2022-01-25)
|
2
14
|
|
3
15
|
- Fixed issue with `encrypts` loading model schema early
|
data/README.md
CHANGED
@@ -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[
|
75
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[7.0]
|
76
76
|
def change
|
77
77
|
add_column :users, :email_ciphertext, :text
|
78
78
|
end
|
@@ -83,12 +83,10 @@ Add to your model:
|
|
83
83
|
|
84
84
|
```ruby
|
85
85
|
class User < ApplicationRecord
|
86
|
-
|
86
|
+
has_encrypted :email
|
87
87
|
end
|
88
88
|
```
|
89
89
|
|
90
|
-
**Note:** With Rails 7, use `lockbox_encrypts` instead of `encrypts`
|
91
|
-
|
92
90
|
You can use `email` just like any other attribute.
|
93
91
|
|
94
92
|
```ruby
|
@@ -103,7 +101,7 @@ You can specify multiple fields in single line.
|
|
103
101
|
|
104
102
|
```ruby
|
105
103
|
class User < ApplicationRecord
|
106
|
-
|
104
|
+
has_encrypted :email, :phone, :city
|
107
105
|
end
|
108
106
|
```
|
109
107
|
|
@@ -113,17 +111,17 @@ Fields are strings by default. Specify the type of a field with:
|
|
113
111
|
|
114
112
|
```ruby
|
115
113
|
class User < ApplicationRecord
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
114
|
+
has_encrypted :birthday, type: :date
|
115
|
+
has_encrypted :signed_at, type: :datetime
|
116
|
+
has_encrypted :opens_at, type: :time
|
117
|
+
has_encrypted :active, type: :boolean
|
118
|
+
has_encrypted :salary, type: :integer
|
119
|
+
has_encrypted :latitude, type: :float
|
120
|
+
has_encrypted :video, type: :binary
|
121
|
+
has_encrypted :properties, type: :json
|
122
|
+
has_encrypted :settings, type: :hash
|
123
|
+
has_encrypted :messages, type: :array
|
124
|
+
has_encrypted :ip, type: :inet
|
127
125
|
end
|
128
126
|
```
|
129
127
|
|
@@ -137,7 +135,7 @@ class User < ApplicationRecord
|
|
137
135
|
store :settings, accessors: [:color, :homepage]
|
138
136
|
attribute :configuration, CustomType.new
|
139
137
|
|
140
|
-
|
138
|
+
has_encrypted :properties, :settings, :configuration
|
141
139
|
end
|
142
140
|
```
|
143
141
|
|
@@ -145,7 +143,7 @@ For [StoreModel](https://github.com/DmitryTsepelev/store_model), use:
|
|
145
143
|
|
146
144
|
```ruby
|
147
145
|
class User < ApplicationRecord
|
148
|
-
|
146
|
+
has_encrypted :configuration, type: Configuration.to_type
|
149
147
|
|
150
148
|
after_initialize do
|
151
149
|
self.configuration ||= {}
|
@@ -176,7 +174,7 @@ Add a new column for the ciphertext, then add to your model:
|
|
176
174
|
|
177
175
|
```ruby
|
178
176
|
class User < ApplicationRecord
|
179
|
-
|
177
|
+
has_encrypted :email, migrating: true
|
180
178
|
end
|
181
179
|
```
|
182
180
|
|
@@ -190,7 +188,7 @@ Then update the model to the desired state:
|
|
190
188
|
|
191
189
|
```ruby
|
192
190
|
class User < ApplicationRecord
|
193
|
-
|
191
|
+
has_encrypted :email
|
194
192
|
|
195
193
|
# remove this line after dropping email column
|
196
194
|
self.ignored_columns = ["email"]
|
@@ -250,7 +248,7 @@ User.decrypt_email_ciphertext(user.email_ciphertext)
|
|
250
248
|
Create a migration with:
|
251
249
|
|
252
250
|
```ruby
|
253
|
-
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[
|
251
|
+
class AddBodyCiphertextToRichTexts < ActiveRecord::Migration[7.0]
|
254
252
|
def change
|
255
253
|
add_column :action_text_rich_texts, :body_ciphertext, :text
|
256
254
|
end
|
@@ -289,7 +287,7 @@ Add to your model:
|
|
289
287
|
class User
|
290
288
|
field :email_ciphertext, type: String
|
291
289
|
|
292
|
-
|
290
|
+
has_encrypted :email
|
293
291
|
end
|
294
292
|
```
|
295
293
|
|
@@ -381,7 +379,7 @@ Encryption is applied to all versions after processing.
|
|
381
379
|
You can mount the uploader [as normal](https://github.com/carrierwaveuploader/carrierwave#activerecord). With Active Record, this involves creating a migration:
|
382
380
|
|
383
381
|
```ruby
|
384
|
-
class AddLicenseToUsers < ActiveRecord::Migration[
|
382
|
+
class AddLicenseToUsers < ActiveRecord::Migration[7.0]
|
385
383
|
def change
|
386
384
|
add_column :users, :license, :string
|
387
385
|
end
|
@@ -574,7 +572,7 @@ Update your model:
|
|
574
572
|
|
575
573
|
```ruby
|
576
574
|
class User < ApplicationRecord
|
577
|
-
|
575
|
+
has_encrypted :email, previous_versions: [{master_key: previous_key}]
|
578
576
|
end
|
579
577
|
```
|
580
578
|
|
@@ -710,101 +708,41 @@ Lockbox uses 256-bit keys.
|
|
710
708
|
|
711
709
|
### XSalsa20
|
712
710
|
|
713
|
-
You can also use XSalsa20, which uses an extended nonce so you don’t have to worry about nonce collisions. First, [install Libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium). For Homebrew, use:
|
711
|
+
You can also use XSalsa20, which uses an extended nonce so you don’t have to worry about nonce collisions. First, [install Libsodium](https://github.com/crypto-rb/rbnacl/wiki/Installing-libsodium). It comes preinstalled on [Heroku](https://devcenter.heroku.com/articles/stack-packages). For Homebrew, use:
|
714
712
|
|
715
713
|
```sh
|
716
714
|
brew install libsodium
|
717
715
|
```
|
718
716
|
|
719
|
-
And
|
717
|
+
And for Ubuntu, use:
|
718
|
+
|
719
|
+
```sh
|
720
|
+
sudo apt-get install libsodium23
|
721
|
+
```
|
722
|
+
|
723
|
+
Then add to your Gemfile:
|
720
724
|
|
721
725
|
```ruby
|
722
726
|
gem "rbnacl"
|
723
727
|
```
|
724
728
|
|
725
|
-
|
729
|
+
And add to your model:
|
726
730
|
|
727
731
|
|
728
732
|
```ruby
|
729
733
|
class User < ApplicationRecord
|
730
|
-
|
734
|
+
has_encrypted :email, algorithm: "xsalsa20"
|
731
735
|
end
|
732
736
|
```
|
733
737
|
|
734
738
|
Make it the default with:
|
735
739
|
|
736
740
|
```ruby
|
737
|
-
Lockbox.default_options =
|
741
|
+
Lockbox.default_options[:algorithm] = "xsalsa20"
|
738
742
|
```
|
739
743
|
|
740
744
|
You can also pass an algorithm to `previous_versions` for key rotation.
|
741
745
|
|
742
|
-
#### XSalsa20 Deployment
|
743
|
-
|
744
|
-
##### Heroku
|
745
|
-
|
746
|
-
Heroku [comes with libsodium](https://devcenter.heroku.com/articles/stack-packages) preinstalled.
|
747
|
-
|
748
|
-
##### Ubuntu
|
749
|
-
|
750
|
-
For Ubuntu 20.04 and 18.04, use:
|
751
|
-
|
752
|
-
```sh
|
753
|
-
sudo apt-get install libsodium23
|
754
|
-
```
|
755
|
-
|
756
|
-
For Ubuntu 16.04, use:
|
757
|
-
|
758
|
-
```sh
|
759
|
-
sudo apt-get install libsodium18
|
760
|
-
```
|
761
|
-
|
762
|
-
##### GitHub Actions
|
763
|
-
|
764
|
-
For Ubuntu 20.04 and 18.04, use:
|
765
|
-
|
766
|
-
```yml
|
767
|
-
- name: Install Libsodium
|
768
|
-
run: sudo apt-get update && sudo apt-get install libsodium23
|
769
|
-
```
|
770
|
-
|
771
|
-
For Ubuntu 16.04, use:
|
772
|
-
|
773
|
-
```yml
|
774
|
-
- name: Install Libsodium
|
775
|
-
run: sudo apt-get update && sudo apt-get install libsodium18
|
776
|
-
```
|
777
|
-
|
778
|
-
##### Travis CI
|
779
|
-
|
780
|
-
On Bionic, add to `.travis.yml`:
|
781
|
-
|
782
|
-
```yml
|
783
|
-
addons:
|
784
|
-
apt:
|
785
|
-
packages:
|
786
|
-
- libsodium23
|
787
|
-
```
|
788
|
-
|
789
|
-
On Xenial, add to `.travis.yml`:
|
790
|
-
|
791
|
-
```yml
|
792
|
-
addons:
|
793
|
-
apt:
|
794
|
-
packages:
|
795
|
-
- libsodium18
|
796
|
-
```
|
797
|
-
|
798
|
-
##### CircleCI
|
799
|
-
|
800
|
-
Add a step to `.circleci/config.yml`:
|
801
|
-
|
802
|
-
```yml
|
803
|
-
- run:
|
804
|
-
name: install Libsodium
|
805
|
-
command: sudo apt-get install -y libsodium18
|
806
|
-
```
|
807
|
-
|
808
746
|
## Hybrid Cryptography
|
809
747
|
|
810
748
|
[Hybrid cryptography](https://en.wikipedia.org/wiki/Hybrid_cryptosystem) allows servers to encrypt data without being able to decrypt it.
|
@@ -821,7 +759,7 @@ Store the keys with your other secrets. Then use:
|
|
821
759
|
|
822
760
|
```ruby
|
823
761
|
class User < ApplicationRecord
|
824
|
-
|
762
|
+
has_encrypted :email, algorithm: "hybrid", encryption_key: encryption_key, decryption_key: decryption_key
|
825
763
|
end
|
826
764
|
```
|
827
765
|
|
@@ -851,7 +789,7 @@ To rename a table with encrypted columns/uploaders, use:
|
|
851
789
|
|
852
790
|
```ruby
|
853
791
|
class User < ApplicationRecord
|
854
|
-
|
792
|
+
has_encrypted :email, key_table: "original_table"
|
855
793
|
end
|
856
794
|
```
|
857
795
|
|
@@ -859,7 +797,7 @@ To rename an encrypted column itself, use:
|
|
859
797
|
|
860
798
|
```ruby
|
861
799
|
class User < ApplicationRecord
|
862
|
-
|
800
|
+
has_encrypted :email, key_attribute: "original_column"
|
863
801
|
end
|
864
802
|
```
|
865
803
|
|
@@ -869,7 +807,7 @@ To set a key for an individual field/uploader, use a string:
|
|
869
807
|
|
870
808
|
```ruby
|
871
809
|
class User < ApplicationRecord
|
872
|
-
|
810
|
+
has_encrypted :email, key: ENV["USER_EMAIL_ENCRYPTION_KEY"]
|
873
811
|
end
|
874
812
|
```
|
875
813
|
|
@@ -877,7 +815,7 @@ Or a proc:
|
|
877
815
|
|
878
816
|
```ruby
|
879
817
|
class User < ApplicationRecord
|
880
|
-
|
818
|
+
has_encrypted :email, key: -> { code }
|
881
819
|
end
|
882
820
|
```
|
883
821
|
|
@@ -887,7 +825,7 @@ To use a different key for each record, use a symbol:
|
|
887
825
|
|
888
826
|
```ruby
|
889
827
|
class User < ApplicationRecord
|
890
|
-
|
828
|
+
has_encrypted :email, key: :some_method
|
891
829
|
end
|
892
830
|
```
|
893
831
|
|
@@ -895,7 +833,7 @@ Or a proc:
|
|
895
833
|
|
896
834
|
```ruby
|
897
835
|
class User < ApplicationRecord
|
898
|
-
|
836
|
+
has_encrypted :email, key: -> { some_method }
|
899
837
|
end
|
900
838
|
```
|
901
839
|
|
@@ -907,7 +845,7 @@ For Active Record and Mongoid, use:
|
|
907
845
|
|
908
846
|
```ruby
|
909
847
|
class User < ApplicationRecord
|
910
|
-
|
848
|
+
has_encrypted :email, key: :kms_key
|
911
849
|
end
|
912
850
|
```
|
913
851
|
|
@@ -995,7 +933,7 @@ lockbox.decrypt(ciphertext, associated_data: "othercontext") # fails
|
|
995
933
|
You can use `binary` columns for the ciphertext instead of `text` columns.
|
996
934
|
|
997
935
|
```ruby
|
998
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[
|
936
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[7.0]
|
999
937
|
def change
|
1000
938
|
add_column :users, :email_ciphertext, :binary
|
1001
939
|
end
|
@@ -1006,7 +944,7 @@ Disable Base64 encoding to save space.
|
|
1006
944
|
|
1007
945
|
```ruby
|
1008
946
|
class User < ApplicationRecord
|
1009
|
-
|
947
|
+
has_encrypted :email, encode: false
|
1010
948
|
end
|
1011
949
|
```
|
1012
950
|
|
@@ -1040,7 +978,7 @@ end
|
|
1040
978
|
Create a migration with:
|
1041
979
|
|
1042
980
|
```ruby
|
1043
|
-
class MigrateToLockbox < ActiveRecord::Migration[
|
981
|
+
class MigrateToLockbox < ActiveRecord::Migration[7.0]
|
1044
982
|
def change
|
1045
983
|
add_column :users, :name_ciphertext, :text
|
1046
984
|
add_column :users, :email_ciphertext, :text
|
@@ -1048,11 +986,11 @@ class MigrateToLockbox < ActiveRecord::Migration[6.1]
|
|
1048
986
|
end
|
1049
987
|
```
|
1050
988
|
|
1051
|
-
And add `
|
989
|
+
And add `has_encrypted` to your model with the `migrating` option:
|
1052
990
|
|
1053
991
|
```ruby
|
1054
992
|
class User < ApplicationRecord
|
1055
|
-
|
993
|
+
has_encrypted :name, :email, migrating: true
|
1056
994
|
end
|
1057
995
|
```
|
1058
996
|
|
@@ -1066,14 +1004,14 @@ Once all records are migrated, remove the `migrating` option and the previous mo
|
|
1066
1004
|
|
1067
1005
|
```ruby
|
1068
1006
|
class User < ApplicationRecord
|
1069
|
-
|
1007
|
+
has_encrypted :name, :email
|
1070
1008
|
end
|
1071
1009
|
```
|
1072
1010
|
|
1073
1011
|
Then remove the previous gem from your Gemfile and drop its columns.
|
1074
1012
|
|
1075
1013
|
```ruby
|
1076
|
-
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[
|
1014
|
+
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[7.0]
|
1077
1015
|
def change
|
1078
1016
|
remove_column :users, :encrypted_name, :text
|
1079
1017
|
remove_column :users, :encrypted_name_iv, :text
|
@@ -1085,81 +1023,31 @@ end
|
|
1085
1023
|
|
1086
1024
|
## Upgrading
|
1087
1025
|
|
1088
|
-
### 0.
|
1026
|
+
### 1.0.0
|
1089
1027
|
|
1090
|
-
|
1028
|
+
`encrypts` is now deprecated in favor of `has_encrypted` to avoid conflicting with Active Record encryption.
|
1091
1029
|
|
1092
1030
|
```ruby
|
1093
|
-
User
|
1094
|
-
|
1095
|
-
|
1096
|
-
metadata = user.license.metadata
|
1097
|
-
unless metadata["encrypted"]
|
1098
|
-
user.license.blob.update!(metadata: metadata.merge("encrypted" => true))
|
1099
|
-
end
|
1031
|
+
class User < ApplicationRecord
|
1032
|
+
has_encrypted :email
|
1100
1033
|
end
|
1101
1034
|
```
|
1102
1035
|
|
1103
|
-
### 0.
|
1036
|
+
### 0.6.0
|
1104
1037
|
|
1105
|
-
0.
|
1038
|
+
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:
|
1106
1039
|
|
1107
1040
|
```ruby
|
1108
1041
|
User.with_attached_license.find_each do |user|
|
1109
1042
|
next unless user.license.attached?
|
1110
1043
|
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
license.update!(content_type: content_type)
|
1044
|
+
metadata = user.license.metadata
|
1045
|
+
unless metadata["encrypted"]
|
1046
|
+
user.license.blob.update!(metadata: metadata.merge("encrypted" => true))
|
1115
1047
|
end
|
1116
1048
|
end
|
1117
1049
|
```
|
1118
1050
|
|
1119
|
-
### 0.2.0
|
1120
|
-
|
1121
|
-
0.2.0 brings a number of improvements. Here are a few to be aware of:
|
1122
|
-
|
1123
|
-
- Added `encrypts` method for database fields
|
1124
|
-
- Added support for XSalsa20
|
1125
|
-
- `attached_encrypted` is deprecated in favor of `encrypts_attached`.
|
1126
|
-
|
1127
|
-
#### Optional
|
1128
|
-
|
1129
|
-
To switch to a master key, generate a key:
|
1130
|
-
|
1131
|
-
```ruby
|
1132
|
-
Lockbox.generate_key
|
1133
|
-
```
|
1134
|
-
|
1135
|
-
And set `ENV["LOCKBOX_MASTER_KEY"]` or `Lockbox.master_key`.
|
1136
|
-
|
1137
|
-
Update your model:
|
1138
|
-
|
1139
|
-
```ruby
|
1140
|
-
class User < ApplicationRecord
|
1141
|
-
encrypts_attached :license, previous_versions: [{key: key}]
|
1142
|
-
end
|
1143
|
-
```
|
1144
|
-
|
1145
|
-
New uploads will be encrypted with the new key.
|
1146
|
-
|
1147
|
-
You can rotate existing records with:
|
1148
|
-
|
1149
|
-
```ruby
|
1150
|
-
User.unscoped.find_each do |user|
|
1151
|
-
user.license.rotate_encryption!
|
1152
|
-
end
|
1153
|
-
```
|
1154
|
-
|
1155
|
-
Once that’s complete, update your model:
|
1156
|
-
|
1157
|
-
```ruby
|
1158
|
-
class User < ApplicationRecord
|
1159
|
-
encrypts_attached :license
|
1160
|
-
end
|
1161
|
-
```
|
1162
|
-
|
1163
1051
|
## History
|
1164
1052
|
|
1165
1053
|
View the [changelog](https://github.com/ankane/lockbox/blob/master/CHANGELOG.md)
|
data/lib/lockbox/calculations.rb
CHANGED
@@ -3,7 +3,12 @@ module Lockbox
|
|
3
3
|
def pluck(*column_names)
|
4
4
|
return super unless model.respond_to?(:lockbox_attributes)
|
5
5
|
|
6
|
-
lockbox_columns = column_names.map.with_index
|
6
|
+
lockbox_columns = column_names.map.with_index do |c, i|
|
7
|
+
next unless c.respond_to?(:to_sym)
|
8
|
+
[model.lockbox_attributes[c.to_sym], i]
|
9
|
+
end.select do |la, _i|
|
10
|
+
la && !la[:migrating]
|
11
|
+
end
|
7
12
|
|
8
13
|
return super unless lockbox_columns.any?
|
9
14
|
|
@@ -106,10 +106,9 @@ module Lockbox
|
|
106
106
|
end
|
107
107
|
|
108
108
|
if CarrierWave::VERSION.to_i > 2
|
109
|
-
raise "CarrierWave
|
109
|
+
raise Lockbox::Error, "CarrierWave #{CarrierWave::VERSION} not supported in this version of Lockbox"
|
110
110
|
elsif CarrierWave::VERSION.to_i < 1
|
111
|
-
|
112
|
-
warn "CarrierWave version (#{CarrierWave::VERSION}) not supported in this version of Lockbox (#{Lockbox::VERSION})"
|
111
|
+
raise Lockbox::Error, "CarrierWave #{CarrierWave::VERSION} not supported"
|
113
112
|
end
|
114
113
|
|
115
114
|
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 has_encrypted(*attributes, **options)
|
4
4
|
# support objects
|
5
5
|
# case options[:type]
|
6
6
|
# when Date
|
@@ -226,6 +226,52 @@ module Lockbox
|
|
226
226
|
|
227
227
|
result
|
228
228
|
end
|
229
|
+
|
230
|
+
if ActiveRecord::VERSION::MAJOR >= 6
|
231
|
+
def self.insert_all(attributes, **options)
|
232
|
+
super(lockbox_map_attributes(attributes), **options)
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.insert_all!(attributes, **options)
|
236
|
+
super(lockbox_map_attributes(attributes), **options)
|
237
|
+
end
|
238
|
+
|
239
|
+
def self.upsert_all(attributes, **options)
|
240
|
+
super(lockbox_map_attributes(attributes, check_readonly: true), **options)
|
241
|
+
end
|
242
|
+
|
243
|
+
# private
|
244
|
+
# does not try to handle :returning option for simplicity
|
245
|
+
def self.lockbox_map_attributes(records, check_readonly: false)
|
246
|
+
return records unless records.is_a?(Array)
|
247
|
+
|
248
|
+
records.map do |attributes|
|
249
|
+
# transform keys like Active Record
|
250
|
+
attributes = attributes.transform_keys do |key|
|
251
|
+
n = key.to_s
|
252
|
+
attribute_aliases[n] || n
|
253
|
+
end
|
254
|
+
|
255
|
+
lockbox_attributes = self.lockbox_attributes.slice(*attributes.keys.map(&:to_sym))
|
256
|
+
lockbox_attributes.each do |key, lockbox_attribute|
|
257
|
+
attribute = key.to_s
|
258
|
+
# check read only
|
259
|
+
# users should mark both plaintext and ciphertext columns
|
260
|
+
if check_readonly && readonly_attributes.include?(attribute) && !readonly_attributes.include?(lockbox_attribute[:encrypted_attribute].to_s)
|
261
|
+
warn "[lockbox] WARNING: Mark attribute as readonly: #{lockbox_attribute[:encrypted_attribute]}"
|
262
|
+
end
|
263
|
+
|
264
|
+
message = attributes[attribute]
|
265
|
+
attributes.delete(attribute) unless lockbox_attribute[:migrating]
|
266
|
+
encrypted_attribute = lockbox_attribute[:encrypted_attribute]
|
267
|
+
ciphertext = send("generate_#{encrypted_attribute}", message)
|
268
|
+
attributes[encrypted_attribute] = ciphertext
|
269
|
+
end
|
270
|
+
|
271
|
+
attributes
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
229
275
|
else
|
230
276
|
def reload
|
231
277
|
self.class.lockbox_attributes.each do |_, v|
|
@@ -241,6 +287,23 @@ module Lockbox
|
|
241
287
|
@lockbox_attributes[original_name] = options
|
242
288
|
|
243
289
|
if activerecord
|
290
|
+
# warn on default attributes
|
291
|
+
if attributes_to_define_after_schema_loads.key?(name.to_s)
|
292
|
+
opt = attributes_to_define_after_schema_loads[name.to_s][1]
|
293
|
+
|
294
|
+
has_default =
|
295
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
296
|
+
# not ideal, since NO_DEFAULT_PROVIDED is private
|
297
|
+
opt != ActiveRecord::Attributes::ClassMethods.const_get(:NO_DEFAULT_PROVIDED)
|
298
|
+
else
|
299
|
+
opt.is_a?(Hash) && opt.key?(:default)
|
300
|
+
end
|
301
|
+
|
302
|
+
if has_default
|
303
|
+
warn "[lockbox] WARNING: attributes with `:default` option are not supported. Use `after_initialize` instead."
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
244
307
|
# preference:
|
245
308
|
# 1. type option
|
246
309
|
# 2. existing virtual attribute
|
@@ -303,11 +366,9 @@ module Lockbox
|
|
303
366
|
send("restore_#{encrypted_attribute}!")
|
304
367
|
end
|
305
368
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
super()
|
310
|
-
end
|
369
|
+
define_method("#{name}_in_database") do
|
370
|
+
send(name) # writes attribute when not already set
|
371
|
+
super()
|
311
372
|
end
|
312
373
|
|
313
374
|
define_method("#{name}?") do
|
@@ -443,7 +504,6 @@ module Lockbox
|
|
443
504
|
table = activerecord ? table_name : collection_name.to_s
|
444
505
|
|
445
506
|
unless message.nil?
|
446
|
-
# TODO use attribute type class in 0.7.0
|
447
507
|
case options[:type]
|
448
508
|
when :boolean
|
449
509
|
message = ActiveRecord::Type::Boolean.new.serialize(message)
|
@@ -506,7 +566,6 @@ module Lockbox
|
|
506
566
|
end
|
507
567
|
|
508
568
|
unless message.nil?
|
509
|
-
# TODO use attribute type class in 0.7.0
|
510
569
|
case options[:type]
|
511
570
|
when :boolean
|
512
571
|
message = message == "t"
|
@@ -564,6 +623,11 @@ module Lockbox
|
|
564
623
|
end
|
565
624
|
end
|
566
625
|
|
626
|
+
def lockbox_encrypts(*attributes, **options)
|
627
|
+
ActiveSupport::Deprecation.warn("`#{__callee__}` is deprecated in favor of `has_encrypted`")
|
628
|
+
has_encrypted(*attributes, **options)
|
629
|
+
end
|
630
|
+
|
567
631
|
module Attached
|
568
632
|
def encrypts_attached(*attributes, **options)
|
569
633
|
attributes.each do |name|
|
data/lib/lockbox/version.rb
CHANGED
data/lib/lockbox.rb
CHANGED
@@ -16,36 +16,6 @@ require "lockbox/padding"
|
|
16
16
|
require "lockbox/utils"
|
17
17
|
require "lockbox/version"
|
18
18
|
|
19
|
-
# integrations
|
20
|
-
require "lockbox/carrier_wave_extensions" if defined?(CarrierWave)
|
21
|
-
require "lockbox/railtie" if defined?(Rails)
|
22
|
-
|
23
|
-
if defined?(ActiveSupport::LogSubscriber)
|
24
|
-
require "lockbox/log_subscriber"
|
25
|
-
Lockbox::LogSubscriber.attach_to :lockbox
|
26
|
-
end
|
27
|
-
|
28
|
-
if defined?(ActiveSupport.on_load)
|
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
|
-
|
35
|
-
extend Lockbox::Model
|
36
|
-
extend Lockbox::Model::Attached
|
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
|
40
|
-
end
|
41
|
-
|
42
|
-
ActiveSupport.on_load(:mongoid) do
|
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)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
19
|
module Lockbox
|
50
20
|
class Error < StandardError; end
|
51
21
|
class DecryptionError < Error; end
|
@@ -110,7 +80,39 @@ module Lockbox
|
|
110
80
|
|
111
81
|
def self.encrypts_action_text_body(**options)
|
112
82
|
ActiveSupport.on_load(:action_text_rich_text) do
|
113
|
-
ActionText::RichText.
|
83
|
+
ActionText::RichText.has_encrypted :body, **options
|
114
84
|
end
|
115
85
|
end
|
116
86
|
end
|
87
|
+
|
88
|
+
# integrations
|
89
|
+
require "lockbox/carrier_wave_extensions" if defined?(CarrierWave)
|
90
|
+
require "lockbox/railtie" if defined?(Rails)
|
91
|
+
|
92
|
+
if defined?(ActiveSupport::LogSubscriber)
|
93
|
+
require "lockbox/log_subscriber"
|
94
|
+
Lockbox::LogSubscriber.attach_to :lockbox
|
95
|
+
end
|
96
|
+
|
97
|
+
if defined?(ActiveSupport.on_load)
|
98
|
+
ActiveSupport.on_load(:active_record) do
|
99
|
+
ar_version = ActiveRecord::VERSION::STRING.to_f
|
100
|
+
if ar_version < 5.2
|
101
|
+
if ar_version >= 5
|
102
|
+
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} requires Lockbox < 0.7"
|
103
|
+
else
|
104
|
+
raise Lockbox::Error, "Active Record #{ActiveRecord::VERSION::STRING} not supported"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
extend Lockbox::Model
|
109
|
+
extend Lockbox::Model::Attached
|
110
|
+
singleton_class.alias_method(:encrypts, :lockbox_encrypts) if ActiveRecord::VERSION::MAJOR < 7
|
111
|
+
ActiveRecord::Relation.prepend Lockbox::Calculations
|
112
|
+
end
|
113
|
+
|
114
|
+
ActiveSupport.on_load(:mongoid) do
|
115
|
+
Mongoid::Document::ClassMethods.include(Lockbox::Model)
|
116
|
+
Mongoid::Document::ClassMethods.alias_method(:encrypts, :lockbox_encrypts)
|
117
|
+
end
|
118
|
+
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:
|
4
|
+
version: 1.1.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: 2022-
|
11
|
+
date: 2022-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: andrew@ankane.org
|
@@ -51,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '2.
|
54
|
+
version: '2.6'
|
55
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
59
|
version: '0'
|
60
60
|
requirements: []
|
61
|
-
rubygems_version: 3.3.
|
61
|
+
rubygems_version: 3.3.7
|
62
62
|
signing_key:
|
63
63
|
specification_version: 4
|
64
64
|
summary: Modern encryption for Ruby and Rails
|