attr_encrypted 1.3.3 → 2.0.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 +7 -0
- checksums.yaml.gz.sig +1 -0
- data/.gitignore +6 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +71 -0
- data/Gemfile +3 -0
- data/README.md +420 -0
- data/Rakefile +4 -15
- data/attr_encrypted.gemspec +60 -0
- data/certs/saghaulor.pem +21 -0
- data/lib/attr_encrypted/adapters/active_record.rb +56 -21
- data/lib/attr_encrypted/adapters/data_mapper.rb +1 -0
- data/lib/attr_encrypted/adapters/sequel.rb +1 -0
- data/lib/attr_encrypted/version.rb +3 -3
- data/lib/attr_encrypted.rb +195 -104
- data/test/active_record_test.rb +118 -32
- data/test/attr_encrypted_test.rb +117 -55
- data/test/compatibility_test.rb +20 -37
- data/test/data_mapper_test.rb +6 -6
- data/test/legacy_active_record_test.rb +16 -12
- data/test/legacy_attr_encrypted_test.rb +31 -30
- data/test/legacy_compatibility_test.rb +22 -31
- data/test/legacy_data_mapper_test.rb +11 -8
- data/test/legacy_sequel_test.rb +13 -9
- data/test/run.sh +12 -52
- data/test/sequel_test.rb +6 -6
- data/test/test_helper.rb +28 -16
- data.tar.gz.sig +0 -0
- metadata +113 -70
- metadata.gz.sig +2 -0
- data/README.rdoc +0 -302
data/test/active_record_test.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'test_helper'
|
|
2
2
|
|
|
3
3
|
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
|
4
4
|
|
|
@@ -25,6 +25,15 @@ def create_tables
|
|
|
25
25
|
t.string :encrypted_password
|
|
26
26
|
t.boolean :is_admin
|
|
27
27
|
end
|
|
28
|
+
create_table :prime_ministers do |t|
|
|
29
|
+
t.string :encrypted_name
|
|
30
|
+
end
|
|
31
|
+
create_table :addresses do |t|
|
|
32
|
+
t.binary :encrypted_street
|
|
33
|
+
t.binary :encrypted_street_iv
|
|
34
|
+
t.binary :encrypted_zipcode
|
|
35
|
+
t.string :mode
|
|
36
|
+
end
|
|
28
37
|
end
|
|
29
38
|
end
|
|
30
39
|
end
|
|
@@ -33,7 +42,6 @@ end
|
|
|
33
42
|
create_tables
|
|
34
43
|
|
|
35
44
|
ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
|
|
36
|
-
ActiveRecord::Base.logger = Logger.new(nil) if ::ActiveRecord::VERSION::STRING < "3.0"
|
|
37
45
|
|
|
38
46
|
if ::ActiveRecord::VERSION::STRING > "4.0"
|
|
39
47
|
module Rack
|
|
@@ -48,15 +56,9 @@ end
|
|
|
48
56
|
class Person < ActiveRecord::Base
|
|
49
57
|
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
|
|
50
58
|
attr_encrypted :email, :key => SECRET_KEY
|
|
51
|
-
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY) }, :marshal => true
|
|
59
|
+
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY, iv: SecureRandom.random_bytes(12)) }, :marshal => true
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
def after_initialize
|
|
55
|
-
initialize_salt_and_credentials
|
|
56
|
-
end
|
|
57
|
-
else
|
|
58
|
-
after_initialize :initialize_salt_and_credentials
|
|
59
|
-
end
|
|
61
|
+
after_initialize :initialize_salt_and_credentials
|
|
60
62
|
|
|
61
63
|
protected
|
|
62
64
|
|
|
@@ -70,6 +72,11 @@ class PersonWithValidation < Person
|
|
|
70
72
|
validates_presence_of :email
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
class PersonWithProcMode < Person
|
|
76
|
+
attr_encrypted :email, :key => SECRET_KEY, :mode => Proc.new { :per_attribute_iv_and_salt }
|
|
77
|
+
attr_encrypted :credentials, :key => SECRET_KEY, :mode => Proc.new { :single_iv_and_salt }, insecure_mode: true
|
|
78
|
+
end
|
|
79
|
+
|
|
73
80
|
class Account < ActiveRecord::Base
|
|
74
81
|
attr_accessor :key
|
|
75
82
|
attr_encrypted :password, :key => Proc.new {|account| account.key}
|
|
@@ -77,22 +84,33 @@ end
|
|
|
77
84
|
|
|
78
85
|
class PersonWithSerialization < ActiveRecord::Base
|
|
79
86
|
self.table_name = 'people'
|
|
80
|
-
attr_encrypted :email, :key =>
|
|
87
|
+
attr_encrypted :email, :key => SECRET_KEY
|
|
81
88
|
serialize :password
|
|
82
89
|
end
|
|
83
90
|
|
|
84
91
|
class UserWithProtectedAttribute < ActiveRecord::Base
|
|
85
92
|
self.table_name = 'users'
|
|
86
|
-
attr_encrypted :password, :key =>
|
|
93
|
+
attr_encrypted :password, :key => SECRET_KEY
|
|
87
94
|
attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
class PersonUsingAlias < ActiveRecord::Base
|
|
91
98
|
self.table_name = 'people'
|
|
92
|
-
attr_encryptor :email, :key =>
|
|
99
|
+
attr_encryptor :email, :key => SECRET_KEY
|
|
93
100
|
end
|
|
94
101
|
|
|
95
|
-
class
|
|
102
|
+
class PrimeMinister < ActiveRecord::Base
|
|
103
|
+
attr_encrypted :name, :marshal => true, :key => SECRET_KEY
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class Address < ActiveRecord::Base
|
|
107
|
+
self.attr_encrypted_options[:marshal] = false
|
|
108
|
+
self.attr_encrypted_options[:encode] = false
|
|
109
|
+
attr_encrypted :street, encode_iv: false, key: SECRET_KEY
|
|
110
|
+
attr_encrypted :zipcode, key: SECRET_KEY, mode: Proc.new { |address| address.mode.to_sym }, insecure_mode: true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class ActiveRecordTest < Minitest::Test
|
|
96
114
|
|
|
97
115
|
def setup
|
|
98
116
|
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
|
@@ -102,16 +120,16 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
102
120
|
|
|
103
121
|
def test_should_encrypt_email
|
|
104
122
|
@person = Person.create :email => 'test@example.com'
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
assert_equal @person.email, Person.
|
|
123
|
+
refute_nil @person.encrypted_email
|
|
124
|
+
refute_equal @person.email, @person.encrypted_email
|
|
125
|
+
assert_equal @person.email, Person.first.email
|
|
108
126
|
end
|
|
109
127
|
|
|
110
128
|
def test_should_marshal_and_encrypt_credentials
|
|
111
129
|
@person = Person.create
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
assert_equal @person.credentials, Person.
|
|
130
|
+
refute_nil @person.encrypted_credentials
|
|
131
|
+
refute_equal @person.credentials, @person.encrypted_credentials
|
|
132
|
+
assert_equal @person.credentials, Person.first.credentials
|
|
115
133
|
end
|
|
116
134
|
|
|
117
135
|
def test_should_encode_by_default
|
|
@@ -127,7 +145,7 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
127
145
|
def test_should_encrypt_decrypt_with_iv
|
|
128
146
|
@person = Person.create :email => 'test@example.com'
|
|
129
147
|
@person2 = Person.find(@person.id)
|
|
130
|
-
|
|
148
|
+
refute_nil @person2.encrypted_email_iv
|
|
131
149
|
assert_equal 'test@example.com', @person2.email
|
|
132
150
|
end
|
|
133
151
|
|
|
@@ -142,6 +160,32 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
142
160
|
Account.create!(:password => "password" , :key => SECRET_KEY)
|
|
143
161
|
end
|
|
144
162
|
|
|
163
|
+
def test_should_set_attributes_regardless_of_arguments_order
|
|
164
|
+
# minitest does not implement `assert_nothing_raised` https://github.com/seattlerb/minitest/issues/112
|
|
165
|
+
Account.new.attributes = { :password => "password" , :key => SECRET_KEY }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def test_should_preserve_hash_key_type
|
|
169
|
+
hash = { :foo => 'bar' }
|
|
170
|
+
account = Account.create!(:key => hash)
|
|
171
|
+
assert_equal account.key, hash
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def test_should_create_changed_predicate
|
|
175
|
+
person = Person.create!(:email => 'test@example.com')
|
|
176
|
+
assert !person.email_changed?
|
|
177
|
+
person.email = 'test2@example.com'
|
|
178
|
+
assert person.email_changed?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_should_create_was_predicate
|
|
182
|
+
original_email = 'test@example.com'
|
|
183
|
+
person = Person.create!(:email => original_email)
|
|
184
|
+
assert !person.email_was
|
|
185
|
+
person.email = 'test2@example.com'
|
|
186
|
+
assert_equal person.email_was, original_email
|
|
187
|
+
end
|
|
188
|
+
|
|
145
189
|
if ::ActiveRecord::VERSION::STRING > "4.0"
|
|
146
190
|
def test_should_assign_attributes
|
|
147
191
|
@user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
|
|
@@ -157,13 +201,13 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
157
201
|
|
|
158
202
|
def test_should_raise_exception_if_not_permitted
|
|
159
203
|
@user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
|
|
160
|
-
|
|
204
|
+
assert_raises ActiveModel::ForbiddenAttributesError do
|
|
161
205
|
@user.attributes = ActionController::Parameters.new(:login => 'modified', :is_admin => true)
|
|
162
206
|
end
|
|
163
207
|
end
|
|
164
208
|
|
|
165
209
|
def test_should_raise_exception_on_init_if_not_permitted
|
|
166
|
-
|
|
210
|
+
assert_raises ActiveModel::ForbiddenAttributesError do
|
|
167
211
|
@user = UserWithProtectedAttribute.new ActionController::Parameters.new(:login => 'modified', :is_admin => true)
|
|
168
212
|
end
|
|
169
213
|
end
|
|
@@ -196,6 +240,22 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
196
240
|
assert_nil(@person.attributes = nil)
|
|
197
241
|
end
|
|
198
242
|
|
|
243
|
+
def test_should_allow_proc_based_mode
|
|
244
|
+
@person = PersonWithProcMode.create :email => 'test@example.com', :credentials => 'password123'
|
|
245
|
+
|
|
246
|
+
# Email is :per_attribute_iv_and_salt
|
|
247
|
+
assert_equal @person.class.encrypted_attributes[:email][:mode].class, Proc
|
|
248
|
+
assert_equal @person.class.encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
|
|
249
|
+
refute_nil @person.encrypted_email_salt
|
|
250
|
+
refute_nil @person.encrypted_email_iv
|
|
251
|
+
|
|
252
|
+
# Credentials is :single_iv_and_salt
|
|
253
|
+
assert_equal @person.class.encrypted_attributes[:credentials][:mode].class, Proc
|
|
254
|
+
assert_equal @person.class.encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
|
|
255
|
+
assert_nil @person.encrypted_credentials_salt
|
|
256
|
+
assert_nil @person.encrypted_credentials_iv
|
|
257
|
+
end
|
|
258
|
+
|
|
199
259
|
if ::ActiveRecord::VERSION::STRING > "3.1"
|
|
200
260
|
def test_should_allow_assign_attributes_with_nil
|
|
201
261
|
@person = Person.new
|
|
@@ -203,15 +263,41 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
|
203
263
|
end
|
|
204
264
|
end
|
|
205
265
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
user.save
|
|
266
|
+
def test_that_alias_encrypts_column
|
|
267
|
+
user = PersonUsingAlias.new
|
|
268
|
+
user.email = 'test@example.com'
|
|
269
|
+
user.save
|
|
211
270
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
271
|
+
refute_nil user.encrypted_email
|
|
272
|
+
refute_equal user.email, user.encrypted_email
|
|
273
|
+
assert_equal user.email, PersonUsingAlias.first.email
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# See https://github.com/attr-encrypted/attr_encrypted/issues/68
|
|
277
|
+
def test_should_invalidate_virtual_attributes_on_reload
|
|
278
|
+
pm = PrimeMinister.new(:name => 'Winston Churchill')
|
|
279
|
+
pm.save!
|
|
280
|
+
assert_equal 'Winston Churchill', pm.name
|
|
281
|
+
pm.name = 'Neville Chamberlain'
|
|
282
|
+
assert_equal 'Neville Chamberlain', pm.name
|
|
283
|
+
|
|
284
|
+
result = pm.reload
|
|
285
|
+
assert_equal pm, result
|
|
286
|
+
assert_equal 'Winston Churchill', pm.name
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_should_save_encrypted_data_as_binary
|
|
290
|
+
street = '123 Elm'
|
|
291
|
+
address = Address.new(street: street)
|
|
292
|
+
address.save!
|
|
293
|
+
refute_equal address.encrypted_street, street
|
|
294
|
+
assert_equal Address.first.street, street
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def test_should_evaluate_proc_based_mode
|
|
298
|
+
street = '123 Elm'
|
|
299
|
+
zipcode = '12345'
|
|
300
|
+
address = Address.new(street: street, zipcode: zipcode, mode: :single_iv_and_salt)
|
|
301
|
+
assert_nil address.encrypted_zipcode_iv
|
|
216
302
|
end
|
|
217
303
|
end
|
data/test/attr_encrypted_test.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# encoding: UTF-8
|
|
2
|
-
|
|
2
|
+
require_relative 'test_helper'
|
|
3
3
|
|
|
4
4
|
class SillyEncryptor
|
|
5
5
|
def self.silly_encrypt(options)
|
|
@@ -12,6 +12,7 @@ class SillyEncryptor
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
class User
|
|
15
|
+
extend AttrEncrypted
|
|
15
16
|
self.attr_encrypted_options[:key] = Proc.new { |user| SECRET_KEY } # default key
|
|
16
17
|
self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
|
|
17
18
|
|
|
@@ -33,7 +34,8 @@ class User
|
|
|
33
34
|
attr_accessor :salt
|
|
34
35
|
attr_accessor :should_encrypt
|
|
35
36
|
|
|
36
|
-
def initialize
|
|
37
|
+
def initialize(email: nil)
|
|
38
|
+
self.email = email
|
|
37
39
|
self.salt = Time.now.to_i.to_s
|
|
38
40
|
self.should_encrypt = true
|
|
39
41
|
end
|
|
@@ -48,19 +50,40 @@ class Admin < User
|
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
class SomeOtherClass
|
|
53
|
+
extend AttrEncrypted
|
|
51
54
|
def self.call(object)
|
|
52
55
|
object.class
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
58
|
|
|
56
|
-
class
|
|
59
|
+
class YetAnotherClass
|
|
60
|
+
extend AttrEncrypted
|
|
61
|
+
self.attr_encrypted_options[:encode_iv] = false
|
|
62
|
+
|
|
63
|
+
attr_encrypted :email, :key => SECRET_KEY
|
|
64
|
+
attr_encrypted :phone_number, :key => SECRET_KEY, mode: Proc.new { |thing| thing.mode }, encode_iv: Proc.new { |thing| thing.encode_iv }, encode_salt: Proc.new { |thing| thing.encode_salt }
|
|
65
|
+
|
|
66
|
+
def initialize(email: nil, encode_iv: 'm', encode_salt: 'm', mode: :per_attribute_iv_and_salt)
|
|
67
|
+
self.email = email
|
|
68
|
+
@encode_iv = encode_iv
|
|
69
|
+
@encode_salt = encode_salt
|
|
70
|
+
@mode = mode
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
attr_reader :encode_iv, :encode_salt, :mode
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class AttrEncryptedTest < Minitest::Test
|
|
77
|
+
def setup
|
|
78
|
+
@iv = SecureRandom.random_bytes(12)
|
|
79
|
+
end
|
|
57
80
|
|
|
58
81
|
def test_should_store_email_in_encrypted_attributes
|
|
59
82
|
assert User.encrypted_attributes.include?(:email)
|
|
60
83
|
end
|
|
61
84
|
|
|
62
85
|
def test_should_not_store_salt_in_encrypted_attributes
|
|
63
|
-
|
|
86
|
+
refute User.encrypted_attributes.include?(:salt)
|
|
64
87
|
end
|
|
65
88
|
|
|
66
89
|
def test_attr_encrypted_should_return_true_for_email
|
|
@@ -68,7 +91,7 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
68
91
|
end
|
|
69
92
|
|
|
70
93
|
def test_attr_encrypted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
|
|
71
|
-
|
|
94
|
+
refute_equal User.encrypted_attributes[:email][:attribute], User.encrypted_attributes[:without_encoding][:attribute]
|
|
72
95
|
end
|
|
73
96
|
|
|
74
97
|
def test_attr_encrypted_should_return_false_for_salt
|
|
@@ -88,71 +111,75 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
88
111
|
end
|
|
89
112
|
|
|
90
113
|
def test_should_not_encrypt_nil_value
|
|
91
|
-
assert_nil User.encrypt_email(nil)
|
|
114
|
+
assert_nil User.encrypt_email(nil, iv: @iv)
|
|
92
115
|
end
|
|
93
116
|
|
|
94
117
|
def test_should_not_encrypt_empty_string
|
|
95
|
-
assert_equal '', User.encrypt_email('')
|
|
118
|
+
assert_equal '', User.encrypt_email('', iv: @iv)
|
|
96
119
|
end
|
|
97
120
|
|
|
98
121
|
def test_should_encrypt_email
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
refute_nil User.encrypt_email('test@example.com', iv: @iv)
|
|
123
|
+
refute_equal 'test@example.com', User.encrypt_email('test@example.com', iv: @iv)
|
|
101
124
|
end
|
|
102
125
|
|
|
103
126
|
def test_should_encrypt_email_when_modifying_the_attr_writer
|
|
104
127
|
@user = User.new
|
|
105
128
|
assert_nil @user.encrypted_email
|
|
106
129
|
@user.email = 'test@example.com'
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
refute_nil @user.encrypted_email
|
|
131
|
+
iv = @user.encrypted_email_iv.unpack('m').first
|
|
132
|
+
salt = @user.encrypted_email_salt[1..-1].unpack('m').first
|
|
133
|
+
assert_equal User.encrypt_email('test@example.com', iv: iv, salt: salt), @user.encrypted_email
|
|
109
134
|
end
|
|
110
135
|
|
|
111
136
|
def test_should_not_decrypt_nil_value
|
|
112
|
-
assert_nil User.decrypt_email(nil)
|
|
137
|
+
assert_nil User.decrypt_email(nil, iv: @iv)
|
|
113
138
|
end
|
|
114
139
|
|
|
115
140
|
def test_should_not_decrypt_empty_string
|
|
116
|
-
assert_equal '', User.decrypt_email('')
|
|
141
|
+
assert_equal '', User.decrypt_email('', iv: @iv)
|
|
117
142
|
end
|
|
118
143
|
|
|
119
144
|
def test_should_decrypt_email
|
|
120
|
-
encrypted_email = User.encrypt_email('test@example.com')
|
|
121
|
-
|
|
122
|
-
assert_equal 'test@example.com', User.decrypt_email(encrypted_email)
|
|
145
|
+
encrypted_email = User.encrypt_email('test@example.com', iv: @iv)
|
|
146
|
+
refute_equal 'test@test.com', encrypted_email
|
|
147
|
+
assert_equal 'test@example.com', User.decrypt_email(encrypted_email, iv: @iv)
|
|
123
148
|
end
|
|
124
149
|
|
|
125
150
|
def test_should_decrypt_email_when_reading
|
|
126
151
|
@user = User.new
|
|
127
152
|
assert_nil @user.email
|
|
128
|
-
|
|
153
|
+
iv = @user.encrypted_email_iv.unpack('m').first
|
|
154
|
+
salt = @user.encrypted_email_salt[1..-1].unpack('m').first
|
|
155
|
+
@user.encrypted_email = User.encrypt_email('test@example.com', iv: iv, salt: salt)
|
|
129
156
|
assert_equal 'test@example.com', @user.email
|
|
130
157
|
end
|
|
131
158
|
|
|
132
159
|
def test_should_encrypt_with_encoding
|
|
133
|
-
assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
|
|
160
|
+
assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
|
|
134
161
|
end
|
|
135
162
|
|
|
136
163
|
def test_should_decrypt_with_encoding
|
|
137
|
-
encrypted = User.encrypt_with_encoding('test')
|
|
138
|
-
assert_equal 'test', User.decrypt_with_encoding(encrypted)
|
|
139
|
-
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
|
|
164
|
+
encrypted = User.encrypt_with_encoding('test', iv: @iv)
|
|
165
|
+
assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
|
|
166
|
+
assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
|
|
140
167
|
end
|
|
141
168
|
|
|
142
169
|
def test_should_encrypt_with_custom_encoding
|
|
143
|
-
assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
|
|
170
|
+
assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
|
|
144
171
|
end
|
|
145
172
|
|
|
146
173
|
def test_should_decrypt_with_custom_encoding
|
|
147
|
-
encrypted = User.encrypt_with_encoding('test')
|
|
148
|
-
assert_equal 'test', User.decrypt_with_encoding(encrypted)
|
|
149
|
-
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
|
|
174
|
+
encrypted = User.encrypt_with_encoding('test', iv: @iv)
|
|
175
|
+
assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
|
|
176
|
+
assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
|
|
150
177
|
end
|
|
151
178
|
|
|
152
179
|
def test_should_encrypt_with_marshaling
|
|
153
180
|
@user = User.new
|
|
154
181
|
@user.with_marshaling = [1, 2, 3]
|
|
155
|
-
|
|
182
|
+
refute_nil @user.encrypted_with_marshaling
|
|
156
183
|
end
|
|
157
184
|
|
|
158
185
|
def test_should_use_custom_encryptor_and_crypt_method_names_and_arguments
|
|
@@ -163,8 +190,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
163
190
|
@user = User.new
|
|
164
191
|
assert_nil @user.ssn_encrypted
|
|
165
192
|
@user.ssn = 'testing'
|
|
166
|
-
|
|
167
|
-
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt )
|
|
193
|
+
refute_nil @user.ssn_encrypted
|
|
194
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt.unpack("m").first )
|
|
168
195
|
assert_equal encrypted, @user.ssn_encrypted
|
|
169
196
|
end
|
|
170
197
|
|
|
@@ -172,8 +199,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
172
199
|
@user = User.new
|
|
173
200
|
assert_nil @user.crypted_password_test
|
|
174
201
|
@user.password = 'testing'
|
|
175
|
-
|
|
176
|
-
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
|
|
202
|
+
refute_nil @user.crypted_password_test
|
|
203
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
|
|
177
204
|
assert_equal encrypted, @user.crypted_password_test
|
|
178
205
|
end
|
|
179
206
|
|
|
@@ -181,8 +208,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
181
208
|
@user = User.new
|
|
182
209
|
assert_nil @user.crypted_password_test
|
|
183
210
|
@user.password = 'testing'
|
|
184
|
-
|
|
185
|
-
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
|
|
211
|
+
refute_nil @user.crypted_password_test
|
|
212
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
|
|
186
213
|
assert_equal encrypted, @user.crypted_password_test
|
|
187
214
|
end
|
|
188
215
|
|
|
@@ -201,31 +228,32 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
201
228
|
end
|
|
202
229
|
|
|
203
230
|
def test_should_evaluate_a_symbol_option
|
|
204
|
-
assert_equal
|
|
231
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, :class)
|
|
205
232
|
end
|
|
206
233
|
|
|
207
234
|
def test_should_evaluate_a_proc_option
|
|
208
|
-
assert_equal
|
|
235
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, proc { |object| object.class })
|
|
209
236
|
end
|
|
210
237
|
|
|
211
238
|
def test_should_evaluate_a_lambda_option
|
|
212
|
-
assert_equal
|
|
239
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, lambda { |object| object.class })
|
|
213
240
|
end
|
|
214
241
|
|
|
215
242
|
def test_should_evaluate_a_method_option
|
|
216
|
-
assert_equal
|
|
243
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call))
|
|
217
244
|
end
|
|
218
245
|
|
|
219
246
|
def test_should_return_a_string_option
|
|
220
|
-
|
|
247
|
+
class_string = 'SomeOtherClass'
|
|
248
|
+
assert_equal class_string, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, class_string)
|
|
221
249
|
end
|
|
222
250
|
|
|
223
251
|
def test_should_encrypt_with_true_if
|
|
224
252
|
@user = User.new
|
|
225
253
|
assert_nil @user.encrypted_with_true_if
|
|
226
254
|
@user.with_true_if = 'testing'
|
|
227
|
-
|
|
228
|
-
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt)
|
|
255
|
+
refute_nil @user.encrypted_with_true_if
|
|
256
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt.unpack("m").first)
|
|
229
257
|
assert_equal encrypted, @user.encrypted_with_true_if
|
|
230
258
|
end
|
|
231
259
|
|
|
@@ -233,7 +261,7 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
233
261
|
@user = User.new
|
|
234
262
|
assert_nil @user.encrypted_with_false_if
|
|
235
263
|
@user.with_false_if = 'testing'
|
|
236
|
-
|
|
264
|
+
refute_nil @user.encrypted_with_false_if
|
|
237
265
|
assert_equal 'testing', @user.encrypted_with_false_if
|
|
238
266
|
end
|
|
239
267
|
|
|
@@ -241,8 +269,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
241
269
|
@user = User.new
|
|
242
270
|
assert_nil @user.encrypted_with_false_unless
|
|
243
271
|
@user.with_false_unless = 'testing'
|
|
244
|
-
|
|
245
|
-
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt)
|
|
272
|
+
refute_nil @user.encrypted_with_false_unless
|
|
273
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt.unpack("m").first)
|
|
246
274
|
assert_equal encrypted, @user.encrypted_with_false_unless
|
|
247
275
|
end
|
|
248
276
|
|
|
@@ -250,7 +278,7 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
250
278
|
@user = User.new
|
|
251
279
|
assert_nil @user.encrypted_with_true_unless
|
|
252
280
|
@user.with_true_unless = 'testing'
|
|
253
|
-
|
|
281
|
+
refute_nil @user.encrypted_with_true_unless
|
|
254
282
|
assert_equal 'testing', @user.encrypted_with_true_unless
|
|
255
283
|
end
|
|
256
284
|
|
|
@@ -261,11 +289,6 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
261
289
|
def test_should_always_reset_options
|
|
262
290
|
@user = User.new
|
|
263
291
|
@user.with_if_changed = "encrypt_stuff"
|
|
264
|
-
@user.stubs(:instance_variable_get).returns(nil)
|
|
265
|
-
@user.stubs(:instance_variable_set).raises("BadStuff")
|
|
266
|
-
assert_raise RuntimeError do
|
|
267
|
-
@user.with_if_changed
|
|
268
|
-
end
|
|
269
292
|
|
|
270
293
|
@user = User.new
|
|
271
294
|
@user.should_encrypt = false
|
|
@@ -275,9 +298,9 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
275
298
|
end
|
|
276
299
|
|
|
277
300
|
def test_should_cast_values_as_strings_before_encrypting
|
|
278
|
-
string_encrypted_email = User.encrypt_email('3')
|
|
279
|
-
assert_equal string_encrypted_email, User.encrypt_email(3)
|
|
280
|
-
assert_equal '3', User.decrypt_email(string_encrypted_email)
|
|
301
|
+
string_encrypted_email = User.encrypt_email('3', iv: @iv)
|
|
302
|
+
assert_equal string_encrypted_email, User.encrypt_email(3, iv: @iv)
|
|
303
|
+
assert_equal '3', User.decrypt_email(string_encrypted_email, iv: @iv)
|
|
281
304
|
end
|
|
282
305
|
|
|
283
306
|
def test_should_create_query_accessor
|
|
@@ -293,7 +316,12 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
293
316
|
@user = User.new
|
|
294
317
|
@user.email = 'email@example.com'
|
|
295
318
|
@user.password = 'p455w0rd'
|
|
296
|
-
|
|
319
|
+
refute_equal @user.encrypted_email_iv, @user.crypted_password_test_iv
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def test_should_generate_iv_per_attribute_by_default
|
|
323
|
+
thing = YetAnotherClass.new(email: 'thing@thing.com')
|
|
324
|
+
refute_nil thing.encrypted_email_iv
|
|
297
325
|
end
|
|
298
326
|
|
|
299
327
|
def test_should_vary_iv_per_instance
|
|
@@ -301,14 +329,15 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
301
329
|
@user1.email = 'email@example.com'
|
|
302
330
|
@user2 = User.new
|
|
303
331
|
@user2.email = 'email@example.com'
|
|
304
|
-
|
|
332
|
+
refute_equal @user1.encrypted_email_iv, @user2.encrypted_email_iv
|
|
333
|
+
refute_equal @user1.encrypted_email, @user2.encrypted_email
|
|
305
334
|
end
|
|
306
335
|
|
|
307
336
|
def test_should_vary_salt_per_attribute
|
|
308
337
|
@user = User.new
|
|
309
338
|
@user.email = 'email@example.com'
|
|
310
339
|
@user.password = 'p455w0rd'
|
|
311
|
-
|
|
340
|
+
refute_equal @user.encrypted_email_salt, @user.crypted_password_test_salt
|
|
312
341
|
end
|
|
313
342
|
|
|
314
343
|
def test_should_vary_salt_per_instance
|
|
@@ -316,7 +345,12 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
316
345
|
@user1.email = 'email@example.com'
|
|
317
346
|
@user2 = User.new
|
|
318
347
|
@user2.email = 'email@example.com'
|
|
319
|
-
|
|
348
|
+
refute_equal @user1.encrypted_email_salt, @user2.encrypted_email_salt
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def test_should_not_generate_salt_per_attribute_by_default
|
|
352
|
+
thing = YetAnotherClass.new(email: 'thing@thing.com')
|
|
353
|
+
assert_nil thing.encrypted_email_salt
|
|
320
354
|
end
|
|
321
355
|
|
|
322
356
|
def test_should_decrypt_second_record
|
|
@@ -328,4 +362,32 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
|
328
362
|
|
|
329
363
|
assert_equal 'test@example.com', @user1.decrypt(:email, @user1.encrypted_email)
|
|
330
364
|
end
|
|
365
|
+
|
|
366
|
+
def test_should_specify_the_default_algorithm
|
|
367
|
+
assert YetAnotherClass.encrypted_attributes[:email][:algorithm]
|
|
368
|
+
assert_equal YetAnotherClass.encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def test_should_not_encode_iv_when_encode_iv_is_false
|
|
372
|
+
email = 'thing@thing.com'
|
|
373
|
+
thing = YetAnotherClass.new(email: email)
|
|
374
|
+
refute thing.encrypted_email_iv =~ base64_encoding_regex
|
|
375
|
+
assert_equal thing.email, email
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def test_should_base64_encode_iv_by_default
|
|
379
|
+
phone_number = '555-555-5555'
|
|
380
|
+
thing = YetAnotherClass.new
|
|
381
|
+
thing.phone_number = phone_number
|
|
382
|
+
assert thing.encrypted_phone_number_iv =~ base64_encoding_regex
|
|
383
|
+
assert_equal thing.phone_number, phone_number
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def test_should_generate_unique_iv_for_every_encrypt_operation
|
|
387
|
+
user = User.new
|
|
388
|
+
user.email = 'initial_value@test.com'
|
|
389
|
+
original_iv = user.encrypted_email_iv
|
|
390
|
+
user.email = 'revised_value@test.com'
|
|
391
|
+
refute_equal original_iv, user.encrypted_email_iv
|
|
392
|
+
end
|
|
331
393
|
end
|