symmetric-encryption 4.1.1 → 4.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,605 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- ActiveRecord::Base.configurations = YAML.safe_load(ERB.new(IO.read('test/config/database.yml')).result)
4
- ActiveRecord::Base.establish_connection(:test)
5
-
6
- # @formatter:off
7
- ActiveRecord::Schema.define version: 0 do
8
- create_table :users, force: true do |t|
9
- t.string :encrypted_bank_account_number
10
- t.string :encrypted_social_security_number
11
- t.string :encrypted_string_value
12
- t.text :encrypted_long_string_value
13
- t.text :encrypted_binary_string_value
14
- t.text :encrypted_data_yaml
15
- t.text :encrypted_data_json
16
- t.string :name
17
- t.string :encrypted_unsupported_option
18
-
19
- t.string :encrypted_integer_value
20
- t.string :encrypted_float_value
21
- t.string :encrypted_decimal_value
22
- t.string :encrypted_datetime_value
23
- t.string :encrypted_time_value
24
- t.string :encrypted_date_value
25
- t.string :encrypted_true_value
26
- t.string :encrypted_false_value
27
-
28
- t.string :encrypted_text
29
- t.string :encrypted_number
30
- end
31
-
32
- create_table :unique_users, force: true do |t|
33
- t.string :encrypted_email
34
- t.string :encrypted_username
35
- end
36
- end
37
-
38
- class User < ActiveRecord::Base
39
- attr_encrypted :bank_account_number
40
- attr_encrypted :social_security_number
41
- attr_encrypted :string_value, random_iv: true
42
- attr_encrypted :long_string_value, random_iv: true, compress: true
43
- attr_encrypted :binary_string_value, random_iv: true, compress: true
44
- attr_encrypted :data_yaml, random_iv: true, compress: true, type: :yaml
45
- attr_encrypted :data_json, random_iv: true, compress: true, type: :json
46
-
47
- attr_encrypted :integer_value, type: :integer
48
- attr_encrypted :float_value, type: :float
49
- attr_encrypted :decimal_value, type: :decimal
50
- attr_encrypted :datetime_value, type: :datetime
51
- attr_encrypted :time_value, type: :time
52
- attr_encrypted :date_value, type: :date
53
- attr_encrypted :true_value, type: :boolean
54
- attr_encrypted :false_value, type: :boolean
55
-
56
- validates :encrypted_bank_account_number, symmetric_encryption: true
57
- validates :encrypted_social_security_number, symmetric_encryption: true
58
-
59
- attr_encrypted :text, type: :string
60
- attr_encrypted :number, type: :integer
61
-
62
- validates :text, format: {with: /\A[a-zA-Z ]+\z/, message: 'only allows letters'}, presence: true
63
- validates :number, presence: true
64
- end
65
-
66
- class UniqueUser < ActiveRecord::Base
67
- attr_encrypted :email
68
- attr_encrypted :username
69
-
70
- validates_uniqueness_of :encrypted_email, allow_blank: true, if: :encrypted_email_changed?
71
- validates_uniqueness_of :encrypted_username, allow_blank: true, if: :encrypted_username_changed?
72
-
73
- validates :username,
74
- length: {in: 3..20},
75
- format: {with: /\A[\w\-]+\z/},
76
- allow_blank: true
77
- end
78
- # @formatter:on
79
-
80
- #
81
- # Unit Test for attr_encrypted extensions in ActiveRecord
82
- #
83
- class ActiveRecordTest < Minitest::Test
84
- describe 'ActiveRecord' do
85
- INTEGER_VALUE = 12
86
- FLOAT_VALUE = 88.12345
87
- DECIMAL_VALUE = BigDecimal('22.51')
88
- DATETIME_VALUE = DateTime.new(2001, 11, 26, 20, 55, 54, '-5')
89
- TIME_VALUE = Time.new(2013, 1, 1, 22, 30, 0, '-04:00')
90
- DATE_VALUE = Date.new(1927, 4, 2)
91
- STRING_VALUE = 'A string containing some data to be encrypted with a random initialization vector'.freeze
92
- LONG_STRING_VALUE = 'A string containing some data to be encrypted with a random initialization vector and compressed since it takes up so much space in plain text form'.freeze
93
- BINARY_STRING_VALUE = "Non-UTF8 Binary \x92 string".force_encoding('BINARY')
94
-
95
- let :bank_account_number do
96
- '1234567890'
97
- end
98
-
99
- let :bank_account_number_encrypted do
100
- 'QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=='
101
- end
102
-
103
- let :social_security_number do
104
- '987654321'
105
- end
106
-
107
- let :social_security_number_encrypted do
108
- 'QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=='
109
- end
110
-
111
- let :person_name do
112
- 'Joe Bloggs'
113
- end
114
-
115
- let :hash_data do
116
- {a: 'A', b: 'B'}
117
- end
118
-
119
- let :user do
120
- User.new(
121
- # Encrypted Attribute
122
- bank_account_number: bank_account_number,
123
- # Encrypted Attribute
124
- social_security_number: social_security_number,
125
- name: person_name,
126
- # data type specific fields
127
- string_value: STRING_VALUE,
128
- long_string_value: LONG_STRING_VALUE,
129
- binary_string_value: BINARY_STRING_VALUE,
130
- integer_value: INTEGER_VALUE,
131
- float_value: FLOAT_VALUE,
132
- decimal_value: DECIMAL_VALUE,
133
- datetime_value: DATETIME_VALUE,
134
- time_value: TIME_VALUE,
135
- date_value: DATE_VALUE,
136
- true_value: true,
137
- false_value: false,
138
- data_yaml: hash_data.dup,
139
- data_json: hash_data.dup,
140
- text: 'hello',
141
- number: '21'
142
- )
143
- end
144
-
145
- it 'has encrypted methods' do
146
- assert_equal true, user.respond_to?(:encrypted_bank_account_number)
147
- assert_equal true, user.respond_to?(:bank_account_number)
148
- assert_equal true, user.respond_to?(:encrypted_social_security_number)
149
- assert_equal true, user.respond_to?(:social_security_number)
150
- assert_equal true, user.respond_to?(:data_yaml)
151
- assert_equal true, user.respond_to?(:data_json)
152
- assert_equal false, user.respond_to?(:encrypted_name)
153
- assert_equal true, user.respond_to?(:encrypted_bank_account_number_changed?)
154
- assert_equal true, user.respond_to?(:bank_account_number_changed?)
155
- end
156
-
157
- it 'has unencrypted values' do
158
- assert_equal bank_account_number, user.bank_account_number
159
- assert_equal social_security_number, user.social_security_number
160
- end
161
-
162
- it 'has encrypted values' do
163
- assert_equal bank_account_number_encrypted, user.encrypted_bank_account_number
164
- assert_equal social_security_number_encrypted, user.encrypted_social_security_number
165
- end
166
-
167
- describe ':random_iv' do
168
- it 'false' do
169
- user.social_security_number = social_security_number
170
- assert first_value = user.social_security_number
171
- # Assign the same value
172
- user.social_security_number = social_security_number
173
- assert_equal first_value, user.social_security_number
174
- end
175
-
176
- it 'true' do
177
- user.string_value = STRING_VALUE
178
- assert first_value = user.encrypted_string_value
179
- user.string_value = 'blah'
180
- user.string_value = STRING_VALUE
181
- refute_equal first_value, user.encrypted_string_value
182
- end
183
-
184
- it 'true and compress: true' do
185
- user.string_value = STRING_VALUE
186
- user.long_string_value = STRING_VALUE
187
-
188
- refute_equal user.encrypted_long_string_value, user.encrypted_string_value
189
- end
190
-
191
- describe 'changed?' do
192
- it 'true for a new instance' do
193
- assert user.string_value_changed?
194
- end
195
-
196
- it 'clears after save' do
197
- user.save!
198
- refute user.string_value_changed?
199
- end
200
-
201
- it 'does not change when equal' do
202
- user.save!
203
- before = user.encrypted_string_value
204
- user.string_value = STRING_VALUE
205
- refute user.string_value_changed?
206
- assert_equal before, user.encrypted_string_value
207
- end
208
- end
209
- end
210
-
211
- describe 'attribute=' do
212
- it 'handles nil' do
213
- user.string_value = nil
214
- assert_nil user.string_value
215
- assert_nil user.encrypted_string_value
216
- user.save!
217
- user.reload
218
- assert_nil user.string_value
219
- assert_nil user.encrypted_string_value
220
- end
221
-
222
- it 'handles empty string' do
223
- user.string_value = ''
224
- assert_equal '', user.string_value
225
- assert_equal '', user.encrypted_string_value
226
- user.save!
227
- user.reload
228
- assert_equal '', user.string_value
229
- assert_equal '', user.encrypted_string_value
230
- end
231
-
232
- it 'encrypt' do
233
- user = User.new
234
- user.bank_account_number = bank_account_number
235
- assert_equal bank_account_number, user.bank_account_number
236
- assert_equal bank_account_number_encrypted, user.encrypted_bank_account_number
237
- end
238
-
239
- it 'all paths it lead to the same result' do
240
- assert_equal bank_account_number_encrypted, (user.encrypted_social_security_number = bank_account_number_encrypted)
241
- assert_equal bank_account_number, user.social_security_number
242
- assert_equal bank_account_number_encrypted, user.encrypted_social_security_number
243
- end
244
-
245
- it 'all paths it lead to the same result 2' do
246
- assert_equal bank_account_number, (user.social_security_number = bank_account_number)
247
- assert_equal bank_account_number_encrypted, user.encrypted_social_security_number
248
- assert_equal bank_account_number, user.social_security_number
249
- end
250
-
251
- it 'all paths it lead to the same result, check uninitialized' do
252
- user = User.new
253
- assert_nil user.social_security_number
254
- assert_equal bank_account_number, (user.social_security_number = bank_account_number)
255
- assert_equal bank_account_number, user.social_security_number
256
- assert_equal bank_account_number_encrypted, user.encrypted_social_security_number
257
-
258
- user.social_security_number = nil
259
- assert_nil user.social_security_number
260
- assert_nil user.encrypted_social_security_number
261
- end
262
- end
263
-
264
- describe '.new' do
265
- it 'allows unencrypted values to be passed to the constructor' do
266
- user = User.new(bank_account_number: bank_account_number, social_security_number: social_security_number)
267
- assert_equal bank_account_number, user.bank_account_number
268
- assert_equal social_security_number, user.social_security_number
269
- assert_equal bank_account_number_encrypted, user.encrypted_bank_account_number
270
- assert_equal social_security_number_encrypted, user.encrypted_social_security_number
271
- end
272
- end
273
-
274
- describe '.encrypted_attributes' do
275
- it 'returns encrypted attributes for the class' do
276
- expect = {social_security_number: :encrypted_social_security_number, bank_account_number: :encrypted_bank_account_number}
277
- result = User.encrypted_attributes
278
- expect.each_pair { |k, _v| assert_equal expect[k], result[k] }
279
- end
280
- end
281
-
282
- describe '.encrypted_keys' do
283
- it 'return encrypted keys for the class' do
284
- expect = %i[social_security_number bank_account_number]
285
- result = User.encrypted_keys
286
- expect.each { |val| assert result.include?(val) }
287
-
288
- # Also check encrypted_attribute?
289
- expect.each { |val| assert User.encrypted_attribute?(val) }
290
- end
291
- end
292
-
293
- describe '.encrypted_columns' do
294
- it 'return encrypted columns for the class' do
295
- expect = %i[encrypted_social_security_number encrypted_bank_account_number]
296
- result = User.encrypted_columns
297
- expect.each { |val| assert result.include?(val) }
298
-
299
- # Also check encrypted_column?
300
- expect.each { |val| assert User.encrypted_column?(val) }
301
- end
302
- end
303
-
304
- describe '#valid?' do
305
- before do
306
- assert user.valid?
307
- end
308
-
309
- it 'fails invalid data' do
310
- user.encrypted_bank_account_number = '123'
311
- assert_equal false, user.valid?
312
- assert_equal ['must be a value encrypted using SymmetricEncryption.encrypt'], user.errors[:encrypted_bank_account_number]
313
- end
314
-
315
- it 'passes encrypted data' do
316
- user.encrypted_bank_account_number = SymmetricEncryption.encrypt('123')
317
- assert user.valid?
318
- end
319
-
320
- it 'passes valid data' do
321
- user.bank_account_number = '123'
322
- assert user.valid?
323
- end
324
-
325
- it 'passes nil encrypted data' do
326
- user.encrypted_bank_account_number = nil
327
- assert user.valid?
328
- end
329
-
330
- it 'passes empty string encrypted data' do
331
- user.encrypted_bank_account_number = ''
332
- assert user.valid?
333
- end
334
-
335
- it 'passes nil data' do
336
- user.bank_account_number = nil
337
- assert user.valid?
338
- end
339
-
340
- it 'passes empty string data' do
341
- user.bank_account_number = ''
342
- assert user.valid?
343
- end
344
-
345
- it 'validate un-encrypted string data' do
346
- assert user.valid?
347
- user.text = '123'
348
- assert_equal false, user.valid?
349
- assert_equal ['only allows letters'], user.errors[:text]
350
- user.text = nil
351
- assert_equal false, user.valid?
352
- assert_equal ['only allows letters', "can't be blank"], user.errors[:text]
353
- user.text = ''
354
- assert_equal false, user.valid?
355
- assert_equal ['only allows letters', "can't be blank"], user.errors[:text]
356
- end
357
-
358
- it 'validate un-encrypted integer data with coercion' do
359
- assert user.valid?
360
- user.number = '123'
361
- assert user.valid?
362
- assert_equal 123, user.number
363
- assert user.valid?
364
- user.number = ''
365
- assert_equal false, user.valid?
366
- assert_equal '', user.number
367
- assert_equal ["can't be blank"], user.errors[:number]
368
- user.number = nil
369
- assert_nil user.number
370
- assert_nil user.encrypted_number
371
- assert_equal false, user.valid?
372
- assert_equal ["can't be blank"], user.errors[:number]
373
- end
374
- end
375
-
376
- describe 'with saved user' do
377
- before do
378
- user.save!
379
- end
380
-
381
- after do
382
- user&.destroy
383
- end
384
-
385
- it 'return correct data type before save' do
386
- u = User.new(integer_value: '5')
387
- assert_equal 5, u.integer_value
388
- assert u.integer_value.is_a?(Integer)
389
- end
390
-
391
- it 'handle gsub! for non-encrypted_field' do
392
- user.name.tr!('a', 'v')
393
- new_name = person_name.tr('a', 'v')
394
- assert_equal new_name, user.name
395
- user.reload
396
- assert_equal new_name, user.name
397
- end
398
-
399
- it 'prevent gsub! on non-encrypted value of encrypted_field' do
400
- # can't modify frozen String
401
- assert_raises RuntimeError do
402
- user.bank_account_number.tr!('5', '4')
403
- end
404
- end
405
-
406
- describe '#reload' do
407
- it 'reverts changes' do
408
- new_bank_account_number = '444444444'
409
- user.bank_account_number = new_bank_account_number
410
- assert_equal new_bank_account_number, user.bank_account_number
411
-
412
- # Reload User model from the database
413
- user.reload
414
- assert_equal bank_account_number_encrypted, user.encrypted_bank_account_number
415
- assert_equal bank_account_number, user.bank_account_number
416
- end
417
-
418
- it 'reverts changes to encrypted field' do
419
- new_bank_account_number = '111111111'
420
- new_encrypted_bank_account_number = SymmetricEncryption.encrypt(new_bank_account_number)
421
- user.encrypted_bank_account_number = new_encrypted_bank_account_number
422
- assert_equal new_encrypted_bank_account_number, user.encrypted_bank_account_number
423
- assert_equal new_bank_account_number, user.bank_account_number
424
-
425
- # Reload User model from the database
426
- user.reload
427
- assert_equal bank_account_number_encrypted, user.encrypted_bank_account_number
428
- assert_equal bank_account_number, user.bank_account_number
429
- end
430
- end
431
-
432
- describe 'data types' do
433
- before do
434
- @user_clone = User.find(user.id)
435
- end
436
-
437
- [
438
- # @formatter:off
439
- {attribute: :integer_value, klass: Integer, value: INTEGER_VALUE, new_value: 98},
440
- {attribute: :float_value, klass: Float, value: FLOAT_VALUE, new_value: 45.4321},
441
- {attribute: :decimal_value, klass: BigDecimal, value: DECIMAL_VALUE, new_value: BigDecimal('99.95'), coercible: '22.51'},
442
- {attribute: :datetime_value, klass: DateTime, value: DATETIME_VALUE, new_value: DateTime.new(1998, 10, 21, 8, 33, 28, '+5'), coercible: DATETIME_VALUE.to_time},
443
- {attribute: :time_value, klass: Time, value: TIME_VALUE, new_value: Time.new(2000, 1, 1, 22, 30, 0, '-04:00')},
444
- {attribute: :date_value, klass: Date, value: DATE_VALUE, new_value: Date.new(2027, 4, 2), coercible: DATE_VALUE.to_time},
445
- {attribute: :true_value, klass: TrueClass, value: true, new_value: false},
446
- {attribute: :false_value, klass: FalseClass, value: false, new_value: true},
447
- {attribute: :string_value, klass: String, value: STRING_VALUE, new_value: 'Hello World'},
448
- {attribute: :long_string_value, klass: String, value: LONG_STRING_VALUE, new_value: 'A Really long Hello World'},
449
- {attribute: :binary_string_value, klass: String, value: BINARY_STRING_VALUE, new_value: "A new Non-UTF8 Binary \x92 string".force_encoding('BINARY')},
450
- # @formatter:on
451
- ].each do |value_test|
452
- describe "#{value_test[:klass]} values" do
453
- before do
454
- @attribute = value_test[:attribute]
455
- @klass = value_test[:klass]
456
- @value = value_test[:value]
457
- @coercible = value_test[:coercible] || @value.to_s
458
- @new_value = value_test[:new_value]
459
- end
460
-
461
- it 'return correct data type' do
462
- val = @user_clone.send(@attribute)
463
- # Need to dup since minitest attempts to modify the decrypted value which is frozen
464
- val = val.dup if val.duplicable?
465
- assert_equal @value, val, @user_clone.attributes.ai
466
- assert user.send(@attribute).is_a?(@klass)
467
- end
468
-
469
- it 'coerce data type before save' do
470
- u = User.new(@attribute => @value)
471
- assert_equal @value, u.send(@attribute)
472
- assert u.send(@attribute).is_a?(@klass), "Value supposed to be coerced into #{@klass}, but is #{u.send(@attribute).class.name}"
473
- end
474
-
475
- it 'permit replacing value with nil' do
476
- @user_clone.send("#{@attribute}=".to_sym, nil)
477
- @user_clone.save!
478
-
479
- user.reload
480
- assert_nil user.send(@attribute)
481
- assert_nil user.send("encrypted_#{@attribute}".to_sym)
482
- end
483
-
484
- it 'permit replacing value with an empty string' do
485
- @user_clone.send("#{@attribute}=".to_sym, '')
486
- @user_clone.save!
487
-
488
- user.reload
489
- assert_equal '', user.send(@attribute)
490
- assert_equal '', user.send("encrypted_#{@attribute}".to_sym)
491
- end
492
-
493
- it 'permit replacing value' do
494
- @user_clone.send("#{@attribute}=".to_sym, @new_value)
495
- @user_clone.save!
496
-
497
- user.reload
498
- val = user.send(@attribute)
499
- # Need to dup since minitest attempts to modify the decrypted value which is frozen
500
- val = val.dup if val.duplicable?
501
- assert_equal @new_value, val
502
- end
503
- end
504
- end
505
-
506
- describe 'JSON Serialization' do
507
- let :hash_data do
508
- {'a' => 'A', 'b' => 'B'}
509
- end
510
-
511
- it 'return correct data type' do
512
- assert_equal hash_data, @user_clone.data_json
513
- assert user.clone.data_json.is_a?(Hash)
514
- end
515
-
516
- it 'not coerce data type (leaves as hash) before save' do
517
- u = User.new(data_json: hash_data)
518
- assert_equal hash_data, u.data_json
519
- assert u.data_json.is_a?(Hash)
520
- end
521
-
522
- it 'permit replacing value with nil' do
523
- @user_clone.data_json = nil
524
- @user_clone.save!
525
-
526
- user.reload
527
- assert_nil user.data_json
528
- assert_nil user.encrypted_data_json
529
- end
530
-
531
- it 'permit replacing value' do
532
- new_value = hash_data.clone
533
- new_value['c'] = 'C'
534
- @user_clone.data_json = new_value
535
- @user_clone.save!
536
-
537
- user.reload
538
- assert_equal new_value, user.data_json
539
- end
540
- end
541
-
542
- describe 'YAML Serialization' do
543
- it 'return correct data type' do
544
- assert_equal hash_data, @user_clone.data_yaml
545
- assert user.clone.data_yaml.is_a?(Hash)
546
- end
547
-
548
- it 'not coerce data type (leaves as hash) before save' do
549
- u = User.new(data_yaml: hash_data)
550
- assert_equal hash_data, u.data_yaml
551
- assert u.data_yaml.is_a?(Hash)
552
- end
553
-
554
- it 'permit replacing value with nil' do
555
- @user_clone.data_yaml = nil
556
- @user_clone.save!
557
-
558
- user.reload
559
- assert_nil user.data_yaml
560
- assert_nil user.encrypted_data_yaml
561
- end
562
-
563
- it 'permit replacing value' do
564
- new_value = hash_data.clone
565
- new_value[:c] = 'C'
566
- @user_clone.data_yaml = new_value
567
- @user_clone.save!
568
-
569
- user.reload
570
- assert_equal new_value, user.data_yaml
571
- end
572
- end
573
- end
574
-
575
- describe 'changed?' do
576
- it 'return false if it was not changed' do
577
- assert_equal false, user.encrypted_bank_account_number_changed?
578
- assert_equal false, user.bank_account_number_changed?
579
- end
580
-
581
- it 'return true if it was changed' do
582
- user.bank_account_number = '15424623'
583
- assert user.encrypted_bank_account_number_changed?
584
- assert user.bank_account_number_changed?
585
- end
586
- end
587
- end
588
-
589
- describe 'uniqueness' do
590
- before do
591
- UniqueUser.destroy_all
592
- @email = 'whatever@not-unique.com'
593
- @username = 'gibby007'
594
- user = UniqueUser.create!(email: @email)
595
- @email_user = UniqueUser.create!(username: @username)
596
- end
597
-
598
- it 'does not allow duplicate values' do
599
- duplicate = UniqueUser.new(email: @email)
600
- assert_equal false, duplicate.valid?
601
- assert_equal 'has already been taken', duplicate.errors.messages[:encrypted_email].first
602
- end
603
- end
604
- end
605
- end