symmetric-encryption 4.1.1 → 4.1.2

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.
@@ -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