powerhome-attr_encrypted 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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