powerhome-attr_encrypted 1.0.1

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +67 -0
  4. data/CHANGELOG.md +98 -0
  5. data/Gemfile +3 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +465 -0
  8. data/Rakefile +25 -0
  9. data/attr_encrypted.gemspec +63 -0
  10. data/certs/saghaulor.pem +21 -0
  11. data/checksum/attr_encrypted-3.0.0.gem.sha256 +1 -0
  12. data/checksum/attr_encrypted-3.0.0.gem.sha512 +1 -0
  13. data/checksum/attr_encrypted-3.0.1.gem.sha256 +1 -0
  14. data/checksum/attr_encrypted-3.0.1.gem.sha512 +1 -0
  15. data/checksum/attr_encrypted-3.0.2.gem.sha256 +1 -0
  16. data/checksum/attr_encrypted-3.0.2.gem.sha512 +1 -0
  17. data/checksum/attr_encrypted-3.0.3.gem.sha256 +1 -0
  18. data/checksum/attr_encrypted-3.0.3.gem.sha512 +1 -0
  19. data/checksum/attr_encrypted-3.1.0.gem.sha256 +1 -0
  20. data/checksum/attr_encrypted-3.1.0.gem.sha512 +1 -0
  21. data/lib/attr_encrypted.rb +473 -0
  22. data/lib/attr_encrypted/adapters/active_record.rb +157 -0
  23. data/lib/attr_encrypted/adapters/data_mapper.rb +24 -0
  24. data/lib/attr_encrypted/adapters/sequel.rb +16 -0
  25. data/lib/attr_encrypted/version.rb +19 -0
  26. data/test/active_record_test.rb +365 -0
  27. data/test/attr_encrypted_test.rb +490 -0
  28. data/test/compatibility_test.rb +109 -0
  29. data/test/data_mapper_test.rb +59 -0
  30. data/test/legacy_active_record_test.rb +120 -0
  31. data/test/legacy_attr_encrypted_test.rb +300 -0
  32. data/test/legacy_compatibility_test.rb +95 -0
  33. data/test/legacy_data_mapper_test.rb +57 -0
  34. data/test/legacy_sequel_test.rb +54 -0
  35. data/test/run.sh +12 -0
  36. data/test/sequel_test.rb +55 -0
  37. data/test/test_helper.rb +61 -0
  38. metadata +294 -0
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveRecord::Base)
4
+ module AttrEncrypted
5
+ module Adapters
6
+ module ActiveRecord
7
+ def self.extended(base) # :nodoc:
8
+ base.class_eval do
9
+
10
+ # https://github.com/attr-encrypted/attr_encrypted/issues/68
11
+ alias_method :reload_without_attr_encrypted, :reload
12
+ def reload(*args, &block)
13
+ result = reload_without_attr_encrypted(*args, &block)
14
+ self.class.encrypted_attributes.keys.each do |attribute_name|
15
+ instance_variable_set("@#{attribute_name}", nil)
16
+ end
17
+ result
18
+ end
19
+
20
+ attr_encrypted_options[:encode] = true
21
+
22
+ class << self
23
+ alias_method :method_missing_without_attr_encrypted, :method_missing
24
+ alias_method :method_missing, :method_missing_with_attr_encrypted
25
+ end
26
+
27
+ def perform_attribute_assignment(method, new_attributes, *args)
28
+ return if new_attributes.blank?
29
+
30
+ send method, new_attributes.reject { |k, _| self.class.encrypted_attributes.key?(k.to_sym) }, *args
31
+ send method, new_attributes.reject { |k, _| !self.class.encrypted_attributes.key?(k.to_sym) }, *args
32
+ end
33
+ private :perform_attribute_assignment
34
+
35
+ if ::ActiveRecord::VERSION::STRING > "3.1"
36
+ alias_method :assign_attributes_without_attr_encrypted, :assign_attributes
37
+ def assign_attributes(*args)
38
+ perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
39
+ end
40
+ end
41
+
42
+ alias_method :attributes_without_attr_encrypted=, :attributes=
43
+ def attributes=(*args)
44
+ perform_attribute_assignment :attributes_without_attr_encrypted=, *args
45
+ end
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ # <tt>attr_encrypted</tt> method
52
+ def attr_encrypted(*attrs)
53
+ super
54
+ options = attrs.extract_options!
55
+ attr = attrs.pop
56
+ attribute attr if ::ActiveRecord::VERSION::STRING >= "5.1.0"
57
+ options.merge! encrypted_attributes[attr]
58
+
59
+ define_method("#{attr}_was") do
60
+ attribute_was(attr)
61
+ end
62
+
63
+ if ::ActiveRecord::VERSION::STRING >= "4.1"
64
+ define_method("#{attr}_changed?") do |options = {}|
65
+ attribute_changed?(attr, options)
66
+ end
67
+ else
68
+ define_method("#{attr}_changed?") do
69
+ attribute_changed?(attr)
70
+ end
71
+ end
72
+
73
+ define_method("#{attr}_change") do
74
+ attribute_change(attr)
75
+ end
76
+
77
+ define_method("#{attr}_with_dirtiness=") do |value|
78
+ ##
79
+ # In ActiveRecord 5.2+, due to changes to the way virtual
80
+ # attributes are handled, @attributes[attr].value is nil which
81
+ # breaks attribute_was. Setting it here returns us to the expected
82
+ # behavior.
83
+ if ::ActiveRecord::VERSION::STRING >= "5.2"
84
+ # This is needed support attribute_was before a record has
85
+ # been saved
86
+ set_attribute_was(attr, __send__(attr)) if value != __send__(attr)
87
+ # This is needed to support attribute_was after a record has
88
+ # been saved
89
+ @attributes.write_from_user(attr.to_s, value) if value != __send__(attr)
90
+ end
91
+ ##
92
+ attribute_will_change!(attr) if value != __send__(attr)
93
+ __send__("#{attr}_without_dirtiness=", value)
94
+ end
95
+
96
+ alias_method "#{attr}_without_dirtiness=", "#{attr}="
97
+ alias_method "#{attr}=", "#{attr}_with_dirtiness="
98
+
99
+ alias_method "#{attr}_before_type_cast", attr
100
+ end
101
+
102
+ def attribute_instance_methods_as_symbols
103
+ # We add accessor methods of the db columns to the list of instance
104
+ # methods returned to let ActiveRecord define the accessor methods
105
+ # for the db columns
106
+
107
+ if connected? && table_exists?
108
+ columns_hash.keys.inject(super) {|instance_methods, column_name| instance_methods.concat [column_name.to_sym, :"#{column_name}="]}
109
+ else
110
+ super
111
+ end
112
+ end
113
+
114
+ def attribute_instance_methods_as_symbols_available?
115
+ connected? && table_exists?
116
+ end
117
+
118
+ # Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
119
+ # encrypted attributes
120
+ #
121
+ # NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
122
+ #
123
+ # This is useful for encrypting fields like email addresses. Your user's email addresses
124
+ # are encrypted in the database, but you can still look up a user by email for logging in
125
+ #
126
+ # Example
127
+ #
128
+ # class User < ActiveRecord::Base
129
+ # attr_encrypted :email, key: 'secret key'
130
+ # end
131
+ #
132
+ # User.find_by_email_and_password('test@example.com', 'testing')
133
+ # # results in a call to
134
+ # User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
135
+ def method_missing_with_attr_encrypted(method, *args, &block)
136
+ if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
137
+ attribute_names = match.captures.last.split('_and_')
138
+ attribute_names.each_with_index do |attribute, index|
139
+ if attr_encrypted?(attribute) && encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
140
+ args[index] = send("encrypt_#{attribute}", args[index])
141
+ warn "DEPRECATION WARNING: This feature will be removed in the next major release."
142
+ attribute_names[index] = encrypted_attributes[attribute.to_sym][:attribute]
143
+ end
144
+ end
145
+ method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
146
+ end
147
+ method_missing_without_attr_encrypted(method, *args, &block)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ ActiveSupport.on_load(:active_record) do
154
+ extend AttrEncrypted
155
+ extend AttrEncrypted::Adapters::ActiveRecord
156
+ end
157
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(DataMapper)
4
+ module AttrEncrypted
5
+ module Adapters
6
+ module DataMapper
7
+ def self.extended(base) # :nodoc:
8
+ class << base
9
+ alias_method :included_without_attr_encrypted, :included
10
+ alias_method :included, :included_with_attr_encrypted
11
+ end
12
+ end
13
+
14
+ def included_with_attr_encrypted(base)
15
+ included_without_attr_encrypted(base)
16
+ base.extend AttrEncrypted
17
+ base.attr_encrypted_options[:encode] = true
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ DataMapper::Resource.extend AttrEncrypted::Adapters::DataMapper
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Sequel)
4
+ module AttrEncrypted
5
+ module Adapters
6
+ module Sequel
7
+ def self.extended(base) # :nodoc:
8
+ base.attr_encrypted_options[:encode] = true
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ Sequel::Model.extend AttrEncrypted
15
+ Sequel::Model.extend AttrEncrypted::Adapters::Sequel
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AttrEncrypted
4
+ # Contains information about this gem's version
5
+ module Version
6
+ MAJOR = 1
7
+ MINOR = 0
8
+ PATCH = 1
9
+
10
+ # Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
11
+ #
12
+ # Example
13
+ #
14
+ # Version.string # '1.0.2'
15
+ def self.string
16
+ [MAJOR, MINOR, PATCH].join('.')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,365 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'test_helper'
4
+
5
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
6
+
7
+ def create_tables
8
+ ActiveRecord::Schema.define(version: 1) do
9
+ self.verbose = false
10
+ create_table :people do |t|
11
+ t.string :encrypted_email
12
+ t.string :password
13
+ t.string :encrypted_credentials
14
+ t.binary :salt
15
+ t.binary :key_iv
16
+ t.string :encrypted_email_salt
17
+ t.string :encrypted_credentials_salt
18
+ t.string :encrypted_email_iv
19
+ t.string :encrypted_credentials_iv
20
+ end
21
+ create_table :accounts do |t|
22
+ t.string :encrypted_password
23
+ t.string :encrypted_password_iv
24
+ t.string :encrypted_password_salt
25
+ t.string :key
26
+ end
27
+ create_table :users do |t|
28
+ t.string :login
29
+ t.string :encrypted_password
30
+ t.string :encrypted_password_iv
31
+ t.boolean :is_admin
32
+ end
33
+ create_table :prime_ministers do |t|
34
+ t.string :encrypted_name
35
+ t.string :encrypted_name_iv
36
+ end
37
+ create_table :addresses do |t|
38
+ t.binary :encrypted_street
39
+ t.binary :encrypted_street_iv
40
+ t.binary :encrypted_zipcode
41
+ t.string :mode
42
+ end
43
+ end
44
+ end
45
+
46
+ ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
47
+
48
+ if ::ActiveRecord::VERSION::STRING > "4.0"
49
+ module Rack
50
+ module Test
51
+ class UploadedFile; end
52
+ end
53
+ end
54
+
55
+ require 'action_controller/metal/strong_parameters'
56
+ end
57
+
58
+ class Person < ActiveRecord::Base
59
+ self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
60
+ attr_encrypted :email, key: SECRET_KEY
61
+ attr_encrypted :credentials, key: Proc.new { |user| Encryptor.encrypt(value: user.salt, key: SECRET_KEY, iv: user.key_iv) }, marshal: true
62
+
63
+ after_initialize :initialize_salt_and_credentials
64
+
65
+ protected
66
+
67
+ def initialize_salt_and_credentials
68
+ self.key_iv ||= SecureRandom.random_bytes(12)
69
+ self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15]
70
+ self.credentials ||= { username: 'example', password: 'test' }
71
+ end
72
+ end
73
+
74
+ class PersonWithValidation < Person
75
+ validates_presence_of :email
76
+ end
77
+
78
+ class PersonWithProcMode < Person
79
+ attr_encrypted :email, key: SECRET_KEY, mode: Proc.new { :per_attribute_iv_and_salt }
80
+ attr_encrypted :credentials, key: SECRET_KEY, mode: Proc.new { :single_iv_and_salt }, insecure_mode: true
81
+ end
82
+
83
+ class Account < ActiveRecord::Base
84
+ ACCOUNT_ENCRYPTION_KEY = SecureRandom.urlsafe_base64(24)
85
+ attr_encrypted :password, key: :password_encryption_key
86
+
87
+ def encrypting?(attr)
88
+ encrypted_attributes[attr][:operation] == :encrypting
89
+ end
90
+
91
+ def password_encryption_key
92
+ if encrypting?(:password)
93
+ self.key = ACCOUNT_ENCRYPTION_KEY
94
+ else
95
+ self.key
96
+ end
97
+ end
98
+ end
99
+
100
+ class PersonWithSerialization < ActiveRecord::Base
101
+ self.table_name = 'people'
102
+ attr_encrypted :email, key: SECRET_KEY
103
+ serialize :password
104
+ end
105
+
106
+ class UserWithProtectedAttribute < ActiveRecord::Base
107
+ self.table_name = 'users'
108
+ attr_encrypted :password, key: SECRET_KEY
109
+ attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
110
+ end
111
+
112
+ class PersonUsingAlias < ActiveRecord::Base
113
+ self.table_name = 'people'
114
+ attr_encryptor :email, key: SECRET_KEY
115
+ end
116
+
117
+ class PrimeMinister < ActiveRecord::Base
118
+ attr_encrypted :name, marshal: true, key: SECRET_KEY
119
+ end
120
+
121
+ class Address < ActiveRecord::Base
122
+ self.attr_encrypted_options[:marshal] = false
123
+ self.attr_encrypted_options[:encode] = false
124
+ attr_encrypted :street, encode_iv: false, key: SECRET_KEY
125
+ attr_encrypted :zipcode, key: SECRET_KEY, mode: Proc.new { |address| address.mode.to_sym }, insecure_mode: true
126
+ end
127
+
128
+ class ActiveRecordTest < Minitest::Test
129
+ def setup
130
+ drop_all_tables
131
+ create_tables
132
+ end
133
+
134
+ def test_should_encrypt_email
135
+ @person = Person.create(email: 'test@example.com')
136
+ refute_nil @person.encrypted_email
137
+ refute_equal @person.email, @person.encrypted_email
138
+ assert_equal @person.email, Person.first.email
139
+ end
140
+
141
+ def test_should_marshal_and_encrypt_credentials
142
+ @person = Person.create
143
+ refute_nil @person.encrypted_credentials
144
+ refute_equal @person.credentials, @person.encrypted_credentials
145
+ assert_equal @person.credentials, Person.first.credentials
146
+ end
147
+
148
+ def test_should_encode_by_default
149
+ assert Person.attr_encrypted_options[:encode]
150
+ end
151
+
152
+ def test_should_validate_presence_of_email
153
+ @person = PersonWithValidation.new
154
+ assert !@person.valid?
155
+ assert !@person.errors[:email].empty? || @person.errors.on(:email)
156
+ end
157
+
158
+ def test_should_encrypt_decrypt_with_iv
159
+ @person = Person.create(email: 'test@example.com')
160
+ @person2 = Person.find(@person.id)
161
+ refute_nil @person2.encrypted_email_iv
162
+ assert_equal 'test@example.com', @person2.email
163
+ end
164
+
165
+ def test_should_ensure_attributes_can_be_deserialized
166
+ @person = PersonWithSerialization.new(email: 'test@example.com', password: %w(an array of strings))
167
+ @person.save
168
+ assert_equal @person.password, %w(an array of strings)
169
+ end
170
+
171
+ def test_should_create_an_account_regardless_of_arguments_order
172
+ Account.create!(key: SECRET_KEY, password: "password")
173
+ Account.create!(password: "password" , key: SECRET_KEY)
174
+ end
175
+
176
+ def test_should_set_attributes_regardless_of_arguments_order
177
+ # minitest does not implement `assert_nothing_raised` https://github.com/seattlerb/minitest/issues/112
178
+ Account.new.attributes = { password: "password", key: SECRET_KEY }
179
+ end
180
+
181
+ def test_should_create_changed_predicate
182
+ person = Person.create!(email: 'test@example.com')
183
+ refute person.email_changed?
184
+ person.email = 'test@example.com'
185
+ refute person.email_changed?
186
+ person.email = nil
187
+ assert person.email_changed?
188
+ person.email = 'test2@example.com'
189
+ assert person.email_changed?
190
+ end
191
+
192
+ def test_should_create_was_predicate
193
+ original_email = 'test@example.com'
194
+ person = Person.create!(email: original_email)
195
+ assert_equal original_email, person.email_was
196
+ person.email = 'test2@example.com'
197
+ assert_equal original_email, person.email_was
198
+ old_pm_name = "Winston Churchill"
199
+ pm = PrimeMinister.create!(name: old_pm_name)
200
+ assert_equal old_pm_name, pm.name_was
201
+ old_zipcode = "90210"
202
+ address = Address.create!(zipcode: old_zipcode, mode: "single_iv_and_salt")
203
+ assert_equal old_zipcode, address.zipcode_was
204
+ end
205
+
206
+ def test_attribute_was_works_when_options_for_old_encrypted_value_are_different_than_options_for_new_encrypted_value
207
+ pw = 'password'
208
+ crypto_key = SecureRandom.urlsafe_base64(24)
209
+ old_iv = SecureRandom.random_bytes(12)
210
+ account = Account.create
211
+ encrypted_value = Encryptor.encrypt(value: pw, iv: old_iv, key: crypto_key)
212
+ Account.where(id: account.id).update_all(key: crypto_key, encrypted_password_iv: [old_iv].pack('m'), encrypted_password: [encrypted_value].pack('m'))
213
+ account = Account.find(account.id)
214
+ assert_equal pw, account.password
215
+ account.password = pw.reverse
216
+ assert_equal pw, account.password_was
217
+ account.save
218
+ account.reload
219
+ assert_equal Account::ACCOUNT_ENCRYPTION_KEY, account.key
220
+ assert_equal pw.reverse, account.password
221
+ end
222
+
223
+ # ActiveRecord 5.2 specific methods
224
+ if ::ActiveRecord::VERSION::STRING >= "5.2"
225
+ def test_should_create_will_save_change_to_predicate
226
+ person = Person.create!(email: 'test@example.com')
227
+ refute person.will_save_change_to_email?
228
+ person.email = 'test@example.com'
229
+ refute person.will_save_change_to_email?
230
+ person.email = 'test2@example.com'
231
+ assert person.will_save_change_to_email?
232
+ end
233
+
234
+ def test_should_create_saved_change_to_predicate
235
+ person = Person.create!(email: 'test@example.com')
236
+ assert person.saved_change_to_email?
237
+ person.reload
238
+ person.email = 'test@example.com'
239
+ refute person.saved_change_to_email?
240
+ person.email = nil
241
+ refute person.saved_change_to_email?
242
+ person.email = 'test2@example.com'
243
+ refute person.saved_change_to_email?
244
+ person.save
245
+ assert person.saved_change_to_email?
246
+ end
247
+ end
248
+
249
+ if ::ActiveRecord::VERSION::STRING > "4.0"
250
+ def test_should_assign_attributes
251
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
252
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
253
+ assert_equal 'modified', @user.login
254
+ end
255
+
256
+ def test_should_not_assign_protected_attributes
257
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
258
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
259
+ assert !@user.is_admin?
260
+ end
261
+
262
+ def test_should_raise_exception_if_not_permitted
263
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
264
+ assert_raises ActiveModel::ForbiddenAttributesError do
265
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true)
266
+ end
267
+ end
268
+
269
+ def test_should_raise_exception_on_init_if_not_permitted
270
+ assert_raises ActiveModel::ForbiddenAttributesError do
271
+ @user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true)
272
+ end
273
+ end
274
+ else
275
+ def test_should_assign_attributes
276
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
277
+ @user.attributes = { login: 'modified', is_admin: true }
278
+ assert_equal 'modified', @user.login
279
+ end
280
+
281
+ def test_should_not_assign_protected_attributes
282
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
283
+ @user.attributes = { login: 'modified', is_admin: true }
284
+ assert !@user.is_admin?
285
+ end
286
+
287
+ def test_should_assign_protected_attributes
288
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
289
+ if ::ActiveRecord::VERSION::STRING > "3.1"
290
+ @user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true)
291
+ else
292
+ @user.send(:attributes=, { login: 'modified', is_admin: true }, false)
293
+ end
294
+ assert @user.is_admin?
295
+ end
296
+ end
297
+
298
+ def test_should_allow_assignment_of_nil_attributes
299
+ @person = Person.new
300
+ assert_nil(@person.attributes = nil)
301
+ end
302
+
303
+ def test_should_allow_proc_based_mode
304
+ @person = PersonWithProcMode.create(email: 'test@example.com', credentials: 'password123')
305
+
306
+ # Email is :per_attribute_iv_and_salt
307
+ assert_equal @person.class.encrypted_attributes[:email][:mode].class, Proc
308
+ assert_equal @person.class.encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
309
+ refute_nil @person.encrypted_email_salt
310
+ refute_nil @person.encrypted_email_iv
311
+
312
+ # Credentials is :single_iv_and_salt
313
+ assert_equal @person.class.encrypted_attributes[:credentials][:mode].class, Proc
314
+ assert_equal @person.class.encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
315
+ assert_nil @person.encrypted_credentials_salt
316
+ assert_nil @person.encrypted_credentials_iv
317
+ end
318
+
319
+ if ::ActiveRecord::VERSION::STRING > "3.1"
320
+ def test_should_allow_assign_attributes_with_nil
321
+ @person = Person.new
322
+ assert_nil(@person.assign_attributes nil)
323
+ end
324
+ end
325
+
326
+ def test_that_alias_encrypts_column
327
+ user = PersonUsingAlias.new
328
+ user.email = 'test@example.com'
329
+ user.save
330
+
331
+ refute_nil user.encrypted_email
332
+ refute_equal user.email, user.encrypted_email
333
+ assert_equal user.email, PersonUsingAlias.first.email
334
+ end
335
+
336
+ # See https://github.com/attr-encrypted/attr_encrypted/issues/68
337
+ def test_should_invalidate_virtual_attributes_on_reload
338
+ old_pm_name = 'Winston Churchill'
339
+ new_pm_name = 'Neville Chamberlain'
340
+ pm = PrimeMinister.create!(name: old_pm_name)
341
+ assert_equal old_pm_name, pm.name
342
+ pm.name = new_pm_name
343
+ assert_equal new_pm_name, pm.name
344
+
345
+ result = pm.reload
346
+ assert_equal pm, result
347
+ assert_equal old_pm_name, pm.name
348
+ end
349
+
350
+ def test_should_save_encrypted_data_as_binary
351
+ street = '123 Elm'
352
+ address = Address.create!(street: street)
353
+ refute_equal address.encrypted_street, street
354
+ assert_equal Address.first.street, street
355
+ end
356
+
357
+ def test_should_evaluate_proc_based_mode
358
+ street = '123 Elm'
359
+ zipcode = '12345'
360
+ address = Address.create(street: street, zipcode: zipcode, mode: :single_iv_and_salt)
361
+ address.reload
362
+ refute_equal address.encrypted_zipcode, zipcode
363
+ assert_equal address.zipcode, zipcode
364
+ end
365
+ end