symmetric-encryption 3.8.3 → 3.9.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/Rakefile +16 -16
  4. data/examples/symmetric-encryption.yml +33 -38
  5. data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +10 -14
  6. data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +28 -25
  7. data/lib/symmetric_encryption.rb +14 -6
  8. data/lib/symmetric_encryption/cipher.rb +151 -130
  9. data/lib/symmetric_encryption/config.rb +0 -1
  10. data/lib/symmetric_encryption/encoder.rb +79 -0
  11. data/lib/symmetric_encryption/extensions/active_record/base.rb +94 -134
  12. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +3 -89
  13. data/lib/symmetric_encryption/key_encryption_key.rb +32 -0
  14. data/lib/symmetric_encryption/railtie.rb +3 -3
  15. data/lib/symmetric_encryption/symmetric_encryption.rb +41 -8
  16. data/lib/symmetric_encryption/utils/re_encrypt_config_files.rb +82 -0
  17. data/lib/symmetric_encryption/version.rb +1 -1
  18. data/test/active_record_test.rb +149 -140
  19. data/test/cipher_test.rb +98 -6
  20. data/test/config/{mongoid_v5.yml → mongoid.yml} +0 -0
  21. data/test/config/symmetric-encryption.yml +4 -10
  22. data/test/config/test_new.key +2 -2
  23. data/test/encoder_test.rb +61 -0
  24. data/test/mongoid_test.rb +12 -22
  25. data/test/reader_test.rb +16 -11
  26. data/test/symmetric_encryption_test.rb +23 -3
  27. data/test/test_db.sqlite3 +0 -0
  28. data/test/test_helper.rb +2 -16
  29. data/test/writer_test.rb +1 -5
  30. metadata +11 -12
  31. data/test/config/mongoid_v2.yml +0 -6
  32. data/test/config/mongoid_v3.yml +0 -9
  33. data/test/mongo_mapper_test.rb +0 -599
@@ -8,7 +8,7 @@ module SymmetricEncryption #:nodoc:
8
8
  # module MyApplication
9
9
  # class Application < Rails::Application
10
10
  # config.symmetric_encryption.cipher = SymmetricEncryption::Cipher.new(
11
- # key: '1234567890ABCDEF1234567890ABCDEF',
11
+ # key: '1234567890ABCDEF',
12
12
  # iv: '1234567890ABCDEF',
13
13
  # cipher_name: 'aes-128-cbc'
14
14
  # )
@@ -26,8 +26,8 @@ module SymmetricEncryption #:nodoc:
26
26
  # @example symmetric-encryption.yml
27
27
  #
28
28
  # development:
29
- # cipher_name: aes-256-cbc
30
- # key: 1234567890ABCDEF1234567890ABCDEF
29
+ # cipher_name: aes-128-cbc
30
+ # key: 1234567890ABCDEF
31
31
  # iv: 1234567890ABCDEF
32
32
  #
33
33
  # Loaded before Active Record initializes since database.yml can have encrypted
@@ -33,7 +33,7 @@ module SymmetricEncryption
33
33
  # Example: For testing purposes the following test cipher can be used:
34
34
  #
35
35
  # SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new(
36
- # key: '1234567890ABCDEF1234567890ABCDEF',
36
+ # key: '1234567890ABCDEF',
37
37
  # iv: '1234567890ABCDEF',
38
38
  # cipher: 'aes-128-cbc'
39
39
  # )
@@ -253,7 +253,7 @@ module SymmetricEncryption
253
253
  # environment:
254
254
  # Which environments config to load. Usually: production, development, etc.
255
255
  # Default: Rails.env
256
- def self.load!(filename=nil, environment=nil)
256
+ def self.load!(filename = nil, environment = nil)
257
257
  Config.load!(filename, environment)
258
258
  end
259
259
 
@@ -261,18 +261,51 @@ module SymmetricEncryption
261
261
  #
262
262
  # Note: Only the current Encryption key settings are used
263
263
  #
264
- # Creates Symmetric Key .key
265
- # and initialization vector .iv
266
- # which is encrypted with the above Public key
264
+ # Creates Symmetric Key .key and initialization vector .iv
265
+ # which is encrypted with the key encryption key.
267
266
  #
268
267
  # Existing key files will be renamed if present
269
- def self.generate_symmetric_key_files(filename=nil, environment=nil)
268
+ def self.generate_symmetric_key_files(filename = nil, environment = nil)
270
269
  config = Config.read_config(filename, environment)
271
270
 
272
271
  # Only regenerating the first configured cipher
273
272
  cipher_config = config[:ciphers].first
274
- key_config = {environment: environment, private_rsa_key: config[:private_rsa_key]}
275
- Cipher.generate_random_keys(key_config.merge(cipher_config))
273
+
274
+ # Delete unused config keys to generate new random keys
275
+ [:version, :always_add_header].each do |key|
276
+ cipher_config.delete(key)
277
+ end
278
+
279
+ key_config = {private_rsa_key: config[:private_rsa_key]}
280
+ cipher_cfg = Cipher.generate_random_keys(key_config.merge(cipher_config))
281
+
282
+ puts
283
+ if encoded_encrypted_key = cipher_cfg[:encrypted_key]
284
+ puts 'If running in Heroku, add the environment specific key:'
285
+ puts "heroku config:add #{environment.upcase}_KEY1=#{encoded_encrypted_key}\n"
286
+ end
287
+
288
+ if encoded_encrypted_iv = cipher_cfg[:encrypted_iv]
289
+ puts 'If running in Heroku, add the environment specific key:'
290
+ puts "heroku config:add #{environment.upcase}_IV1=#{encoded_encrypted_iv}"
291
+ end
292
+
293
+ if key = cipher_cfg[:key]
294
+ puts "Please add the key: #{key} to your config file"
295
+ end
296
+
297
+ if iv = cipher_cfg[:iv]
298
+ puts "Please add the iv: #{iv} to your config file"
299
+ end
300
+
301
+ if file_name = cipher_cfg[:key_filename]
302
+ puts("Please copy #{file_name} to the other servers in #{environment}.")
303
+ end
304
+
305
+ if file_name = cipher_cfg[:iv_filename]
306
+ puts("Please copy #{file_name} to the other servers in #{environment}.")
307
+ end
308
+ cipher_cfg
276
309
  end
277
310
 
278
311
  # Generate a 22 character random password
@@ -0,0 +1,82 @@
1
+ # Used for re-encrypting encrypted passwords stored in configuration files.
2
+ #
3
+ # Search for `SymmetricEncryption.try_decrypt` in config files and replace the
4
+ # encrypted value with one encrypted using the new encryption key.
5
+ #
6
+ # Example:
7
+ # re_encrypt = SymmetricEncryption::Utils::ReEncryptConfigFiles.new(version: 4)
8
+ # re_encrypt.process_directory('../../**/*.yml')
9
+ module SymmetricEncryption
10
+ module Utils
11
+ class ReEncryptConfigFiles
12
+ DEFAULT_REGEXP = /\A(.*)SymmetricEncryption.try_decrypt[\s\(\"\'].([\w@=+\/\\]+)[\'\"](.*)\Z/
13
+
14
+ attr_accessor :cipher, :path, :search_regexp
15
+
16
+ # Parameters:
17
+ # version: [Integer]
18
+ # Version of the encryption key to use when re-encrypting the value.
19
+ # Default: Default cipher ( first in the list of configured ciphers )
20
+ def initialize(params={})
21
+ params = params.dup
22
+ version = params.delete(:version)
23
+ @path = params.delete(:path)
24
+ @search_regexp = params.delete(:search_regexp) || DEFAULT_REGEXP
25
+ @cipher = SymmetricEncryption.cipher(version)
26
+ raise(ArgumentError, "Undefined encryption key version: #{version}") if @cipher.nil?
27
+ raise(ArgumentError, "Unknown parameters: #{params.inspect}") if params.size > 0
28
+ end
29
+
30
+ # Re-encrypt the supplied enctrypted value with the new cipher
31
+ def re_encrypt(encrypted)
32
+ if unencrypted = SymmetricEncryption.try_decrypt(encrypted)
33
+ cipher.encrypt(unencrypted)
34
+ else
35
+ encrypted
36
+ end
37
+ end
38
+
39
+ # Process a single file.
40
+ #
41
+ # Returns [true|false] whether the file was modified
42
+ def process_file(file_name)
43
+ match = false
44
+ lines = File.read(file_name)
45
+ output_lines = ''
46
+ lines.each_line do |line|
47
+ if result = line.match(search_regexp)
48
+ before_str = result[1]
49
+ encrypted = result[2]
50
+ after_str = result[3]
51
+ after_str = after_str[1..-1] if after_str.starts_with?(')')
52
+ new_value = re_encrypt(encrypted)
53
+ if new_value != encrypted
54
+ match = true
55
+ output_lines << "#{before_str}SymmetricEncryption.try_decrypt('#{new_value}')#{after_str}\n"
56
+ else
57
+ output_lines << line
58
+ end
59
+ else
60
+ output_lines << line
61
+ end
62
+ end
63
+ if match
64
+ File.open(file_name, 'wb') { |file| file.write(output_lines) }
65
+ end
66
+ match
67
+ end
68
+
69
+ # Process a directory of files.
70
+ #
71
+ # Parameters:
72
+ # path: [String]
73
+ # Search path to look for files in.
74
+ # Example: '../../**/*.yml'
75
+ def process_directory(path)
76
+ Dir[path].each do |file_name|
77
+ process_file(file_name)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module SymmetricEncryption #:nodoc
2
- VERSION = '3.8.3'
2
+ VERSION = '3.9.0'
3
3
  end
@@ -1,6 +1,5 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- ActiveRecord::Base.logger = SemanticLogger[ActiveRecord]
4
3
  ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
5
4
  ActiveRecord::Base.establish_connection(:test)
6
5
 
@@ -138,7 +137,7 @@ class ActiveRecordTest < Minitest::Test
138
137
  )
139
138
  end
140
139
 
141
- it 'have encrypted methods' do
140
+ it 'has encrypted methods' do
142
141
  assert_equal true, @user.respond_to?(:encrypted_bank_account_number)
143
142
  assert_equal true, @user.respond_to?(:bank_account_number)
144
143
  assert_equal true, @user.respond_to?(:encrypted_social_security_number)
@@ -150,153 +149,155 @@ class ActiveRecordTest < Minitest::Test
150
149
  assert_equal true, @user.respond_to?(:bank_account_number_changed?)
151
150
  end
152
151
 
153
- it 'have unencrypted values' do
152
+ it 'has unencrypted values' do
154
153
  assert_equal @bank_account_number, @user.bank_account_number
155
154
  assert_equal @social_security_number, @user.social_security_number
156
155
  end
157
156
 
158
- it 'have encrypted values' do
157
+ it 'has encrypted values' do
159
158
  assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
160
159
  assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
161
160
  end
162
161
 
163
- it 'support same iv' do
164
- @user.social_security_number = @social_security_number
165
- assert first_value = @user.social_security_number
166
- # Assign the same value
167
- @user.social_security_number = @social_security_number
168
- assert_equal first_value, @user.social_security_number
169
- end
170
-
171
- it 'support a random iv' do
172
- @user.string_value = STRING_VALUE
173
- assert first_value = @user.encrypted_string_value
174
- # Assign the same value
175
- @user.string_value = STRING_VALUE.dup
176
- assert first_value != @user.encrypted_string_value
177
- end
162
+ describe ':random_iv' do
163
+ it 'false' do
164
+ @user.social_security_number = @social_security_number
165
+ assert first_value = @user.social_security_number
166
+ # Assign the same value
167
+ @user.social_security_number = @social_security_number
168
+ assert_equal first_value, @user.social_security_number
169
+ end
178
170
 
179
- it 'support a random iv and compress' do
180
- @user.string_value = STRING_VALUE
181
- @user.long_string_value = STRING_VALUE
171
+ it 'true' do
172
+ @user.string_value = STRING_VALUE
173
+ assert first_value = @user.encrypted_string_value
174
+ # Assign the same value
175
+ @user.string_value = STRING_VALUE.dup
176
+ assert first_value != @user.encrypted_string_value
177
+ end
182
178
 
183
- refute_equal @user.encrypted_long_string_value, @user.encrypted_string_value
184
- end
179
+ it 'true and compress: true' do
180
+ @user.string_value = STRING_VALUE
181
+ @user.long_string_value = STRING_VALUE
185
182
 
186
- it 'encrypt' do
187
- user = User.new
188
- user.bank_account_number = @bank_account_number
189
- assert_equal @bank_account_number, user.bank_account_number
190
- assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
183
+ refute_equal @user.encrypted_long_string_value, @user.encrypted_string_value
184
+ end
191
185
  end
192
186
 
193
- it 'allow lookups using unencrypted or encrypted column name' do
194
- if ActiveRecord::VERSION::STRING.to_f < 4.1
195
- @user.save!
196
-
197
- inq = User.find_by_bank_account_number(@bank_account_number)
198
- assert_equal @bank_account_number, inq.bank_account_number
199
- assert_equal @bank_account_number_encrypted, inq.encrypted_bank_account_number
200
-
201
- @user.delete
187
+ describe 'attribute=' do
188
+ it 'encrypt' do
189
+ user = User.new
190
+ user.bank_account_number = @bank_account_number
191
+ assert_equal @bank_account_number, user.bank_account_number
192
+ assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
202
193
  end
203
- end
204
194
 
205
- it 'all paths it lead to the same result' do
206
- assert_equal @bank_account_number_encrypted, (@user.encrypted_social_security_number = @bank_account_number_encrypted)
207
- assert_equal @bank_account_number, @user.social_security_number
208
- assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
209
- end
195
+ it 'all paths it lead to the same result' do
196
+ assert_equal @bank_account_number_encrypted, (@user.encrypted_social_security_number = @bank_account_number_encrypted)
197
+ assert_equal @bank_account_number, @user.social_security_number
198
+ assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
199
+ end
210
200
 
211
- it 'all paths it lead to the same result 2' do
212
- assert_equal @bank_account_number, (@user.social_security_number = @bank_account_number)
213
- assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
214
- assert_equal @bank_account_number, @user.social_security_number
215
- end
201
+ it 'all paths it lead to the same result 2' do
202
+ assert_equal @bank_account_number, (@user.social_security_number = @bank_account_number)
203
+ assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
204
+ assert_equal @bank_account_number, @user.social_security_number
205
+ end
216
206
 
217
- it 'all paths it lead to the same result, check uninitialized' do
218
- user = User.new
219
- assert_equal nil, user.social_security_number
220
- assert_equal @bank_account_number, (user.social_security_number = @bank_account_number)
221
- assert_equal @bank_account_number, user.social_security_number
222
- assert_equal @bank_account_number_encrypted, user.encrypted_social_security_number
207
+ it 'all paths it lead to the same result, check uninitialized' do
208
+ user = User.new
209
+ assert_nil user.social_security_number
210
+ assert_equal @bank_account_number, (user.social_security_number = @bank_account_number)
211
+ assert_equal @bank_account_number, user.social_security_number
212
+ assert_equal @bank_account_number_encrypted, user.encrypted_social_security_number
223
213
 
224
- assert_equal nil, (user.social_security_number = nil)
225
- assert_equal nil, user.social_security_number
226
- assert_equal nil, user.encrypted_social_security_number
214
+ assert_nil (user.social_security_number = nil)
215
+ assert_nil user.social_security_number
216
+ assert_nil user.encrypted_social_security_number
217
+ end
227
218
  end
228
219
 
229
- it 'allow unencrypted values to be passed to the constructor' do
230
- user = User.new(bank_account_number: @bank_account_number, social_security_number: @social_security_number)
231
- assert_equal @bank_account_number, user.bank_account_number
232
- assert_equal @social_security_number, user.social_security_number
233
- assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
234
- assert_equal @social_security_number_encrypted, user.encrypted_social_security_number
220
+ describe '.new' do
221
+ it 'allows unencrypted values to be passed to the constructor' do
222
+ user = User.new(bank_account_number: @bank_account_number, social_security_number: @social_security_number)
223
+ assert_equal @bank_account_number, user.bank_account_number
224
+ assert_equal @social_security_number, user.social_security_number
225
+ assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
226
+ assert_equal @social_security_number_encrypted, user.encrypted_social_security_number
227
+ end
235
228
  end
236
229
 
237
- it 'return encrypted attributes for the class' do
238
- expect = {social_security_number: :encrypted_social_security_number, bank_account_number: :encrypted_bank_account_number}
239
- result = User.encrypted_attributes
240
- expect.each_pair { |k, v| assert_equal expect[k], result[k] }
230
+ describe '.encrypted_attributes' do
231
+ it 'returns encrypted attributes for the class' do
232
+ expect = {social_security_number: :encrypted_social_security_number, bank_account_number: :encrypted_bank_account_number}
233
+ result = User.encrypted_attributes
234
+ expect.each_pair { |k, v| assert_equal expect[k], result[k] }
235
+ end
241
236
  end
242
237
 
243
- it 'return encrypted keys for the class' do
244
- expect = [:social_security_number, :bank_account_number]
245
- result = User.encrypted_keys
246
- expect.each { |val| assert result.include?(val) }
238
+ describe '.encrypted_keys' do
239
+ it 'return encrypted keys for the class' do
240
+ expect = [:social_security_number, :bank_account_number]
241
+ result = User.encrypted_keys
242
+ expect.each { |val| assert result.include?(val) }
247
243
 
248
- # Also check encrypted_attribute?
249
- expect.each { |val| assert User.encrypted_attribute?(val) }
244
+ # Also check encrypted_attribute?
245
+ expect.each { |val| assert User.encrypted_attribute?(val) }
246
+ end
250
247
  end
251
248
 
252
- it 'return encrypted columns for the class' do
253
- expect = [:encrypted_social_security_number, :encrypted_bank_account_number]
254
- result = User.encrypted_columns
255
- expect.each { |val| assert result.include?(val) }
249
+ describe '.encrypted_columns' do
250
+ it 'return encrypted columns for the class' do
251
+ expect = [:encrypted_social_security_number, :encrypted_bank_account_number]
252
+ result = User.encrypted_columns
253
+ expect.each { |val| assert result.include?(val) }
256
254
 
257
- # Also check encrypted_column?
258
- expect.each { |val| assert User.encrypted_column?(val) }
255
+ # Also check encrypted_column?
256
+ expect.each { |val| assert User.encrypted_column?(val) }
257
+ end
259
258
  end
260
259
 
261
- it 'validate encrypted data' do
262
- assert @user.valid?
263
- @user.encrypted_bank_account_number = '123'
264
- assert_equal false, @user.valid?
265
- assert_equal ['must be a value encrypted using SymmetricEncryption.encrypt'], @user.errors[:encrypted_bank_account_number]
266
- @user.encrypted_bank_account_number = SymmetricEncryption.encrypt('123')
267
- assert @user.valid?
268
- @user.bank_account_number = '123'
269
- assert @user.valid?
270
- end
260
+ describe '#valid?' do
261
+ it 'validate encrypted data' do
262
+ assert @user.valid?
263
+ @user.encrypted_bank_account_number = '123'
264
+ assert_equal false, @user.valid?
265
+ assert_equal ['must be a value encrypted using SymmetricEncryption.encrypt'], @user.errors[:encrypted_bank_account_number]
266
+ @user.encrypted_bank_account_number = SymmetricEncryption.encrypt('123')
267
+ assert @user.valid?
268
+ @user.bank_account_number = '123'
269
+ assert @user.valid?
270
+ end
271
271
 
272
- it 'validate un-encrypted string data' do
273
- assert @user.valid?
274
- @user.text = '123'
275
- assert_equal false, @user.valid?
276
- assert_equal ['only allows letters'], @user.errors[:text]
277
- @user.text = nil
278
- assert_equal false, @user.valid?
279
- assert_equal ['only allows letters', "can't be blank"], @user.errors[:text]
280
- @user.text = ''
281
- assert_equal false, @user.valid?
282
- assert_equal ['only allows letters', "can't be blank"], @user.errors[:text]
283
- end
272
+ it 'validate un-encrypted string data' do
273
+ assert @user.valid?
274
+ @user.text = '123'
275
+ assert_equal false, @user.valid?
276
+ assert_equal ['only allows letters'], @user.errors[:text]
277
+ @user.text = nil
278
+ assert_equal false, @user.valid?
279
+ assert_equal ['only allows letters', "can't be blank"], @user.errors[:text]
280
+ @user.text = ''
281
+ assert_equal false, @user.valid?
282
+ assert_equal ['only allows letters', "can't be blank"], @user.errors[:text]
283
+ end
284
284
 
285
- it 'validate un-encrypted integer data with coercion' do
286
- assert @user.valid?
287
- @user.number = '123'
288
- assert @user.valid?
289
- assert_equal 123, @user.number
290
- assert @user.valid?
291
- @user.number = ''
292
- assert_equal false, @user.valid?
293
- assert_equal nil, @user.number
294
- assert_equal ["can't be blank"], @user.errors[:number]
295
- @user.number = nil
296
- assert_equal nil, @user.number
297
- assert_equal nil, @user.encrypted_number
298
- assert_equal false, @user.valid?
299
- assert_equal ["can't be blank"], @user.errors[:number]
285
+ it 'validate un-encrypted integer data with coercion' do
286
+ assert @user.valid?
287
+ @user.number = '123'
288
+ assert @user.valid?
289
+ assert_equal 123, @user.number
290
+ assert @user.valid?
291
+ @user.number = ''
292
+ assert_equal false, @user.valid?
293
+ assert_nil @user.number
294
+ assert_equal ["can't be blank"], @user.errors[:number]
295
+ @user.number = nil
296
+ assert_nil @user.number
297
+ assert_nil @user.encrypted_number
298
+ assert_equal false, @user.valid?
299
+ assert_equal ["can't be blank"], @user.errors[:number]
300
+ end
300
301
  end
301
302
 
302
303
  describe 'with saved user' do
@@ -329,28 +330,30 @@ class ActiveRecordTest < Minitest::Test
329
330
  end
330
331
  end
331
332
 
332
- it 'revert changes on reload' do
333
- new_bank_account_number = '444444444'
334
- @user.bank_account_number = new_bank_account_number
335
- assert_equal new_bank_account_number, @user.bank_account_number
336
-
337
- # Reload User model from the database
338
- @user.reload
339
- assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
340
- assert_equal @bank_account_number, @user.bank_account_number
341
- end
333
+ describe '#reload' do
334
+ it 'reverts changes' do
335
+ new_bank_account_number = '444444444'
336
+ @user.bank_account_number = new_bank_account_number
337
+ assert_equal new_bank_account_number, @user.bank_account_number
342
338
 
343
- it 'revert changes to encrypted field on reload' do
344
- new_bank_account_number = '111111111'
345
- new_encrypted_bank_account_number = SymmetricEncryption.encrypt(new_bank_account_number)
346
- @user.encrypted_bank_account_number = new_encrypted_bank_account_number
347
- assert_equal new_encrypted_bank_account_number, @user.encrypted_bank_account_number
348
- assert_equal new_bank_account_number, @user.bank_account_number
339
+ # Reload User model from the database
340
+ @user.reload
341
+ assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
342
+ assert_equal @bank_account_number, @user.bank_account_number
343
+ end
349
344
 
350
- # Reload User model from the database
351
- @user.reload
352
- assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
353
- assert_equal @bank_account_number, @user.bank_account_number
345
+ it 'reverts changes to encrypted field' do
346
+ new_bank_account_number = '111111111'
347
+ new_encrypted_bank_account_number = SymmetricEncryption.encrypt(new_bank_account_number)
348
+ @user.encrypted_bank_account_number = new_encrypted_bank_account_number
349
+ assert_equal new_encrypted_bank_account_number, @user.encrypted_bank_account_number
350
+ assert_equal new_bank_account_number, @user.bank_account_number
351
+
352
+ # Reload User model from the database
353
+ @user.reload
354
+ assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
355
+ assert_equal @bank_account_number, @user.bank_account_number
356
+ end
354
357
  end
355
358
 
356
359
  describe 'data types' do
@@ -383,8 +386,11 @@ class ActiveRecordTest < Minitest::Test
383
386
  end
384
387
 
385
388
  it 'return correct data type' do
386
- assert_equal @value, @user_clone.send(@attribute), @user_clone.attributes.ai
387
- assert @user.clone.send(@attribute).kind_of?(@klass)
389
+ val = @user_clone.send(@attribute)
390
+ # Need to dup since minitest attempts to modify the decrypted value which is frozen
391
+ val = val.dup if val.duplicable?
392
+ assert_equal @value, val, @user_clone.attributes.ai
393
+ assert @user.send(@attribute).kind_of?(@klass)
388
394
  end
389
395
 
390
396
  it 'coerce data type before save' do
@@ -425,7 +431,10 @@ class ActiveRecordTest < Minitest::Test
425
431
  @user_clone.save!
426
432
 
427
433
  @user.reload
428
- assert_equal @new_value, @user.send(@attribute)
434
+ val = @user.send(@attribute)
435
+ # Need to dup since minitest attempts to modify the decrypted value which is frozen
436
+ val = val.dup if val.duplicable?
437
+ assert_equal @new_value, val
429
438
  end
430
439
  end
431
440
  end