symmetric-encryption 3.8.3 → 3.9.0

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