symmetric-encryption 3.7.2 → 3.8.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.
- checksums.yaml +4 -4
- data/README.md +65 -83
- data/Rakefile +4 -4
- data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +3 -3
- data/lib/rails/generators/symmetric_encryption/heroku_config/heroku_config_generator.rb +3 -3
- data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +2 -2
- data/lib/symmetric_encryption.rb +7 -1
- data/lib/symmetric_encryption/cipher.rb +180 -50
- data/lib/symmetric_encryption/coerce.rb +75 -0
- data/lib/symmetric_encryption/config.rb +88 -0
- data/lib/symmetric_encryption/extensions/active_record/base.rb +2 -2
- data/lib/symmetric_encryption/extensions/mongoid/encrypted.rb +2 -2
- data/lib/symmetric_encryption/generator.rb +5 -1
- data/lib/symmetric_encryption/railtie.rb +3 -3
- data/lib/symmetric_encryption/railties/symmetric_encryption.rake +6 -6
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
- data/lib/symmetric_encryption/reader.rb +16 -14
- data/lib/symmetric_encryption/symmetric_encryption.rb +30 -285
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +13 -13
- data/test/active_record_test.rb +126 -73
- data/test/cipher_test.rb +42 -42
- data/test/mongo_mapper_test.rb +171 -114
- data/test/mongoid_test.rb +173 -115
- data/test/reader_test.rb +63 -63
- data/test/symmetric_encryption_test.rb +81 -80
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +1 -2
- data/test/writer_test.rb +20 -20
- metadata +13 -13
- data/lib/_test_empty +0 -0
@@ -100,9 +100,9 @@ module SymmetricEncryption
|
|
100
100
|
# end
|
101
101
|
def self.open(filename_or_stream, options={}, &block)
|
102
102
|
raise(ArgumentError, 'options must be a hash') unless options.respond_to?(:each_pair)
|
103
|
-
mode
|
103
|
+
mode = options.fetch(:mode, 'wb')
|
104
104
|
compress = options.fetch(:compress, false)
|
105
|
-
ios
|
105
|
+
ios = filename_or_stream.is_a?(String) ? ::File.open(filename_or_stream, mode) : filename_or_stream
|
106
106
|
|
107
107
|
begin
|
108
108
|
file = self.new(ios, options)
|
@@ -114,11 +114,11 @@ module SymmetricEncryption
|
|
114
114
|
end
|
115
115
|
|
116
116
|
# Encrypt data before writing to the supplied stream
|
117
|
-
def initialize(ios,options={})
|
118
|
-
@ios
|
119
|
-
header
|
120
|
-
random_key
|
121
|
-
random_iv
|
117
|
+
def initialize(ios, options={})
|
118
|
+
@ios = ios
|
119
|
+
header = options.fetch(:header, true)
|
120
|
+
random_key = options.fetch(:random_key, true)
|
121
|
+
random_iv = options.fetch(:random_iv, random_key)
|
122
122
|
raise(ArgumentError, 'When :random_key is true, :random_iv must also be true') if random_key && !random_iv
|
123
123
|
# Compress is only used at this point for setting the flag in the header
|
124
124
|
compress = options.fetch(:compress, false)
|
@@ -137,21 +137,21 @@ module SymmetricEncryption
|
|
137
137
|
@stream_cipher.encrypt
|
138
138
|
|
139
139
|
key = random_key ? @stream_cipher.random_key : cipher.send(:key)
|
140
|
-
iv
|
140
|
+
iv = random_iv ? @stream_cipher.random_iv : cipher.send(:iv)
|
141
141
|
|
142
142
|
@stream_cipher.key = key
|
143
|
-
@stream_cipher.iv
|
143
|
+
@stream_cipher.iv = iv if iv
|
144
144
|
|
145
145
|
# Write the Encryption header including the random iv, key, and cipher
|
146
146
|
if header
|
147
147
|
@ios.write(Cipher.build_header(
|
148
148
|
cipher.version,
|
149
149
|
compress,
|
150
|
-
random_iv
|
150
|
+
random_iv ? iv : nil,
|
151
151
|
random_key ? key : nil,
|
152
152
|
cipher_name))
|
153
153
|
end
|
154
|
-
@size
|
154
|
+
@size = 0
|
155
155
|
@closed = false
|
156
156
|
end
|
157
157
|
|
@@ -182,8 +182,8 @@ module SymmetricEncryption
|
|
182
182
|
def write(data)
|
183
183
|
return unless data
|
184
184
|
|
185
|
-
bytes
|
186
|
-
@size
|
185
|
+
bytes = data.to_s
|
186
|
+
@size += bytes.size
|
187
187
|
partial = @stream_cipher.update(bytes)
|
188
188
|
@ios.write(partial) if partial.length > 0
|
189
189
|
data.length
|
data/test/active_record_test.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
ActiveRecord::Base.logger
|
3
|
+
ActiveRecord::Base.logger = SemanticLogger[ActiveRecord]
|
4
4
|
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
5
5
|
ActiveRecord::Base.establish_connection(:test)
|
6
6
|
|
7
|
+
#@formatter:off
|
7
8
|
ActiveRecord::Schema.define version: 0 do
|
8
9
|
create_table :users, force: true do |t|
|
9
10
|
t.string :encrypted_bank_account_number
|
@@ -27,6 +28,11 @@ ActiveRecord::Schema.define version: 0 do
|
|
27
28
|
t.string :encrypted_text
|
28
29
|
t.string :encrypted_number
|
29
30
|
end
|
31
|
+
|
32
|
+
create_table :unique_users, force: true do |t|
|
33
|
+
t.string :encrypted_email
|
34
|
+
t.string :encrypted_username
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
class User < ActiveRecord::Base
|
@@ -52,13 +58,27 @@ class User < ActiveRecord::Base
|
|
52
58
|
attr_encrypted :text, type: :string
|
53
59
|
attr_encrypted :number, type: :integer
|
54
60
|
|
55
|
-
validates :text, format: { with: /\A[a-zA-Z ]+\z/, message:
|
61
|
+
validates :text, format: { with: /\A[a-zA-Z ]+\z/, message: 'only allows letters' }, presence: true
|
56
62
|
validates :number, presence: true
|
57
63
|
end
|
58
64
|
|
65
|
+
class UniqueUser < ActiveRecord::Base
|
66
|
+
attr_encrypted :email
|
67
|
+
attr_encrypted :username
|
68
|
+
|
69
|
+
validates_uniqueness_of :encrypted_email, allow_blank: true, if: :encrypted_email_changed?
|
70
|
+
validates_uniqueness_of :encrypted_username, allow_blank: true, if: :encrypted_username_changed?
|
71
|
+
|
72
|
+
validates :username,
|
73
|
+
length: {in: 3..20},
|
74
|
+
format: {with: /\A[\w\d\-[[:alnum:]]]+\z/},
|
75
|
+
allow_blank: true
|
76
|
+
end
|
77
|
+
#@formatter:on
|
78
|
+
|
59
79
|
# Initialize the database connection
|
60
80
|
config_file = File.join(File.dirname(__FILE__), 'config', 'database.yml')
|
61
|
-
raise
|
81
|
+
raise 'database config not found. Create a config file at: test/config/database.yml' unless File.exists? config_file
|
62
82
|
|
63
83
|
cfg = YAML.load(ERB.new(File.new(config_file).read).result)['test']
|
64
84
|
raise("Environment 'test' not defined in test/config/database.yml") unless cfg
|
@@ -69,7 +89,7 @@ User.establish_connection(cfg)
|
|
69
89
|
# Unit Test for attr_encrypted extensions in ActiveRecord
|
70
90
|
#
|
71
91
|
class ActiveRecordTest < Minitest::Test
|
72
|
-
|
92
|
+
describe 'ActiveRecord' do
|
73
93
|
INTEGER_VALUE = 12
|
74
94
|
FLOAT_VALUE = 88.12345
|
75
95
|
DECIMAL_VALUE = BigDecimal.new("22.51")
|
@@ -77,19 +97,19 @@ class ActiveRecordTest < Minitest::Test
|
|
77
97
|
TIME_VALUE = Time.new(2013, 01, 01, 22, 30, 00, "-04:00")
|
78
98
|
DATE_VALUE = Date.new(1927, 04, 02)
|
79
99
|
|
80
|
-
|
81
|
-
@bank_account_number
|
82
|
-
@bank_account_number_encrypted =
|
100
|
+
before do
|
101
|
+
@bank_account_number = '1234567890'
|
102
|
+
@bank_account_number_encrypted = 'QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=='
|
83
103
|
|
84
|
-
@social_security_number
|
85
|
-
@social_security_number_encrypted =
|
104
|
+
@social_security_number = '987654321'
|
105
|
+
@social_security_number_encrypted = 'QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=='
|
86
106
|
|
87
|
-
@string
|
88
|
-
@long_string =
|
107
|
+
@string = 'A string containing some data to be encrypted with a random initialization vector'
|
108
|
+
@long_string = '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'
|
89
109
|
|
90
110
|
@name = 'Joe Bloggs'
|
91
111
|
|
92
|
-
@h = {
|
112
|
+
@h = {a: 'A', b: 'B'}
|
93
113
|
|
94
114
|
@user = User.new(
|
95
115
|
# Encrypted Attribute
|
@@ -113,7 +133,7 @@ class ActiveRecordTest < Minitest::Test
|
|
113
133
|
)
|
114
134
|
end
|
115
135
|
|
116
|
-
|
136
|
+
it 'have encrypted methods' do
|
117
137
|
assert_equal true, @user.respond_to?(:encrypted_bank_account_number)
|
118
138
|
assert_equal true, @user.respond_to?(:bank_account_number)
|
119
139
|
assert_equal true, @user.respond_to?(:encrypted_social_security_number)
|
@@ -121,19 +141,21 @@ class ActiveRecordTest < Minitest::Test
|
|
121
141
|
assert_equal true, @user.respond_to?(:data_yaml)
|
122
142
|
assert_equal true, @user.respond_to?(:data_json)
|
123
143
|
assert_equal false, @user.respond_to?(:encrypted_name)
|
144
|
+
assert_equal true, @user.respond_to?(:encrypted_bank_account_number_changed?)
|
145
|
+
assert_equal true, @user.respond_to?(:bank_account_number_changed?)
|
124
146
|
end
|
125
147
|
|
126
|
-
|
148
|
+
it 'have unencrypted values' do
|
127
149
|
assert_equal @bank_account_number, @user.bank_account_number
|
128
150
|
assert_equal @social_security_number, @user.social_security_number
|
129
151
|
end
|
130
152
|
|
131
|
-
|
153
|
+
it 'have encrypted values' do
|
132
154
|
assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
|
133
155
|
assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
|
134
156
|
end
|
135
157
|
|
136
|
-
|
158
|
+
it 'support same iv' do
|
137
159
|
@user.social_security_number = @social_security_number
|
138
160
|
assert first_value = @user.social_security_number
|
139
161
|
# Assign the same value
|
@@ -141,7 +163,7 @@ class ActiveRecordTest < Minitest::Test
|
|
141
163
|
assert_equal first_value, @user.social_security_number
|
142
164
|
end
|
143
165
|
|
144
|
-
|
166
|
+
it 'support a random iv' do
|
145
167
|
@user.string = @string
|
146
168
|
assert first_value = @user.encrypted_string
|
147
169
|
# Assign the same value
|
@@ -149,21 +171,21 @@ class ActiveRecordTest < Minitest::Test
|
|
149
171
|
assert_equal true, first_value != @user.encrypted_string
|
150
172
|
end
|
151
173
|
|
152
|
-
|
153
|
-
@user.string
|
174
|
+
it 'support a random iv and compress' do
|
175
|
+
@user.string = @long_string
|
154
176
|
@user.long_string = @long_string
|
155
177
|
|
156
178
|
assert_equal true, (@user.encrypted_long_string.length.to_f / @user.encrypted_string.length) < 0.8
|
157
179
|
end
|
158
180
|
|
159
|
-
|
160
|
-
user
|
181
|
+
it 'encrypt' do
|
182
|
+
user = User.new
|
161
183
|
user.bank_account_number = @bank_account_number
|
162
184
|
assert_equal @bank_account_number, user.bank_account_number
|
163
185
|
assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
|
164
186
|
end
|
165
187
|
|
166
|
-
|
188
|
+
it 'allow lookups using unencrypted or encrypted column name' do
|
167
189
|
if ActiveRecord::VERSION::STRING.to_f < 4.1
|
168
190
|
@user.save!
|
169
191
|
|
@@ -175,19 +197,19 @@ class ActiveRecordTest < Minitest::Test
|
|
175
197
|
end
|
176
198
|
end
|
177
199
|
|
178
|
-
|
200
|
+
it 'all paths it lead to the same result' do
|
179
201
|
assert_equal @bank_account_number_encrypted, (@user.encrypted_social_security_number = @bank_account_number_encrypted)
|
180
202
|
assert_equal @bank_account_number, @user.social_security_number
|
181
203
|
assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
|
182
204
|
end
|
183
205
|
|
184
|
-
|
206
|
+
it 'all paths it lead to the same result 2' do
|
185
207
|
assert_equal @bank_account_number, (@user.social_security_number = @bank_account_number)
|
186
208
|
assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
|
187
209
|
assert_equal @bank_account_number, @user.social_security_number
|
188
210
|
end
|
189
211
|
|
190
|
-
|
212
|
+
it 'all paths it lead to the same result, check uninitialized' do
|
191
213
|
user = User.new
|
192
214
|
assert_equal nil, user.social_security_number
|
193
215
|
assert_equal @bank_account_number, (user.social_security_number = @bank_account_number)
|
@@ -199,7 +221,7 @@ class ActiveRecordTest < Minitest::Test
|
|
199
221
|
assert_equal nil, user.encrypted_social_security_number
|
200
222
|
end
|
201
223
|
|
202
|
-
|
224
|
+
it 'allow unencrypted values to be passed to the constructor' do
|
203
225
|
user = User.new(bank_account_number: @bank_account_number, social_security_number: @social_security_number)
|
204
226
|
assert_equal @bank_account_number, user.bank_account_number
|
205
227
|
assert_equal @social_security_number, user.social_security_number
|
@@ -207,31 +229,31 @@ class ActiveRecordTest < Minitest::Test
|
|
207
229
|
assert_equal @social_security_number_encrypted, user.encrypted_social_security_number
|
208
230
|
end
|
209
231
|
|
210
|
-
|
232
|
+
it 'return encrypted attributes for the class' do
|
211
233
|
expect = {social_security_number: :encrypted_social_security_number, bank_account_number: :encrypted_bank_account_number}
|
212
234
|
result = User.encrypted_attributes
|
213
|
-
expect.each_pair {|k,v| assert_equal expect[k], result[k]}
|
235
|
+
expect.each_pair { |k, v| assert_equal expect[k], result[k] }
|
214
236
|
end
|
215
237
|
|
216
|
-
|
238
|
+
it 'return encrypted keys for the class' do
|
217
239
|
expect = [:social_security_number, :bank_account_number]
|
218
240
|
result = User.encrypted_keys
|
219
|
-
expect.each {|val| assert_equal true, result.include?(val)}
|
241
|
+
expect.each { |val| assert_equal true, result.include?(val) }
|
220
242
|
|
221
243
|
# Also check encrypted_attribute?
|
222
|
-
expect.each {|val| assert_equal true, User.encrypted_attribute?(val)}
|
244
|
+
expect.each { |val| assert_equal true, User.encrypted_attribute?(val) }
|
223
245
|
end
|
224
246
|
|
225
|
-
|
247
|
+
it 'return encrypted columns for the class' do
|
226
248
|
expect = [:encrypted_social_security_number, :encrypted_bank_account_number]
|
227
249
|
result = User.encrypted_columns
|
228
|
-
expect.each {|val| assert_equal true, result.include?(val)}
|
250
|
+
expect.each { |val| assert_equal true, result.include?(val) }
|
229
251
|
|
230
252
|
# Also check encrypted_column?
|
231
|
-
expect.each {|val| assert_equal true, User.encrypted_column?(val)}
|
253
|
+
expect.each { |val| assert_equal true, User.encrypted_column?(val) }
|
232
254
|
end
|
233
255
|
|
234
|
-
|
256
|
+
it 'validate encrypted data' do
|
235
257
|
assert_equal true, @user.valid?
|
236
258
|
@user.encrypted_bank_account_number = '123'
|
237
259
|
assert_equal false, @user.valid?
|
@@ -242,7 +264,7 @@ class ActiveRecordTest < Minitest::Test
|
|
242
264
|
assert_equal true, @user.valid?
|
243
265
|
end
|
244
266
|
|
245
|
-
|
267
|
+
it 'validate un-encrypted string data' do
|
246
268
|
assert_equal true, @user.valid?
|
247
269
|
@user.text = '123'
|
248
270
|
assert_equal false, @user.valid?
|
@@ -255,7 +277,7 @@ class ActiveRecordTest < Minitest::Test
|
|
255
277
|
assert_equal ["only allows letters", "can't be blank"], @user.errors[:text]
|
256
278
|
end
|
257
279
|
|
258
|
-
|
280
|
+
it 'validate un-encrypted integer data with coercion' do
|
259
281
|
assert_equal true, @user.valid?
|
260
282
|
@user.number = '123'
|
261
283
|
assert_equal true, @user.valid?
|
@@ -272,22 +294,22 @@ class ActiveRecordTest < Minitest::Test
|
|
272
294
|
assert_equal ["can't be blank"], @user.errors[:number]
|
273
295
|
end
|
274
296
|
|
275
|
-
|
276
|
-
|
297
|
+
describe "with saved user" do
|
298
|
+
before do
|
277
299
|
@user.save!
|
278
300
|
end
|
279
301
|
|
280
|
-
|
302
|
+
after do
|
281
303
|
@user.destroy
|
282
304
|
end
|
283
305
|
|
284
|
-
|
306
|
+
it "return correct data type before save" do
|
285
307
|
u = User.new(integer_value: "5")
|
286
308
|
assert_equal 5, u.integer_value
|
287
309
|
assert u.integer_value.kind_of?(Integer)
|
288
310
|
end
|
289
311
|
|
290
|
-
|
312
|
+
it "handle gsub! for non-encrypted_field" do
|
291
313
|
@user.name.gsub!('a', 'v')
|
292
314
|
new_name = @name.gsub('a', 'v')
|
293
315
|
assert_equal new_name, @user.name
|
@@ -295,15 +317,15 @@ class ActiveRecordTest < Minitest::Test
|
|
295
317
|
assert_equal new_name, @user.name
|
296
318
|
end
|
297
319
|
|
298
|
-
|
320
|
+
it "prevent gsub! on non-encrypted value of encrypted_field" do
|
299
321
|
# can't modify frozen String
|
300
322
|
assert_raises RuntimeError do
|
301
323
|
@user.bank_account_number.gsub!('5', '4')
|
302
324
|
end
|
303
325
|
end
|
304
326
|
|
305
|
-
|
306
|
-
new_bank_account_number
|
327
|
+
it "revert changes on reload" do
|
328
|
+
new_bank_account_number = '444444444'
|
307
329
|
@user.bank_account_number = new_bank_account_number
|
308
330
|
assert_equal new_bank_account_number, @user.bank_account_number
|
309
331
|
|
@@ -313,9 +335,9 @@ class ActiveRecordTest < Minitest::Test
|
|
313
335
|
assert_equal @bank_account_number, @user.bank_account_number
|
314
336
|
end
|
315
337
|
|
316
|
-
|
317
|
-
new_bank_account_number
|
318
|
-
new_encrypted_bank_account_number
|
338
|
+
it "revert changes to encrypted field on reload" do
|
339
|
+
new_bank_account_number = '111111111'
|
340
|
+
new_encrypted_bank_account_number = SymmetricEncryption.encrypt(new_bank_account_number)
|
319
341
|
@user.encrypted_bank_account_number = new_encrypted_bank_account_number
|
320
342
|
assert_equal new_encrypted_bank_account_number, @user.encrypted_bank_account_number
|
321
343
|
assert_equal new_bank_account_number, @user.bank_account_number
|
@@ -326,12 +348,13 @@ class ActiveRecordTest < Minitest::Test
|
|
326
348
|
assert_equal @bank_account_number, @user.bank_account_number
|
327
349
|
end
|
328
350
|
|
329
|
-
|
330
|
-
|
351
|
+
describe "data types" do
|
352
|
+
before do
|
331
353
|
@user_clone = User.find(@user.id)
|
332
354
|
end
|
333
355
|
|
334
356
|
[
|
357
|
+
#@formatter:off
|
335
358
|
{ attribute: :integer_value, klass: Integer, value: INTEGER_VALUE, new_value: 98 },
|
336
359
|
{ attribute: :float_value, klass: Float, value: FLOAT_VALUE, new_value: 45.4321 },
|
337
360
|
{ attribute: :decimal_value, klass: BigDecimal, value: DECIMAL_VALUE, new_value: BigDecimal.new("99.95"), coercible: "22.51"},
|
@@ -340,9 +363,10 @@ class ActiveRecordTest < Minitest::Test
|
|
340
363
|
{ attribute: :date_value, klass: Date, value: DATE_VALUE, new_value: Date.new(2027, 04, 02), coercible: DATE_VALUE.to_time },
|
341
364
|
{ attribute: :true_value, klass: TrueClass, value: true, new_value: false },
|
342
365
|
{ attribute: :false_value, klass: FalseClass, value: false, new_value: true },
|
366
|
+
#@formatter:on
|
343
367
|
].each do |value_test|
|
344
|
-
|
345
|
-
|
368
|
+
describe "#{value_test[:klass]} values" do
|
369
|
+
before do
|
346
370
|
@attribute = value_test[:attribute]
|
347
371
|
@klass = value_test[:klass]
|
348
372
|
@value = value_test[:value]
|
@@ -350,18 +374,18 @@ class ActiveRecordTest < Minitest::Test
|
|
350
374
|
@new_value = value_test[:new_value]
|
351
375
|
end
|
352
376
|
|
353
|
-
|
377
|
+
it "return correct data type" do
|
354
378
|
assert_equal @value, @user_clone.send(@attribute)
|
355
379
|
assert @user.clone.send(@attribute).kind_of?(@klass)
|
356
380
|
end
|
357
381
|
|
358
|
-
|
382
|
+
it "coerce data type before save" do
|
359
383
|
u = User.new(@attribute => @value)
|
360
384
|
assert_equal @value, u.send(@attribute)
|
361
385
|
assert u.send(@attribute).kind_of?(@klass), "Value supposed to be coerced into #{@klass}, but is #{u.send(@attribute).class.name}"
|
362
386
|
end
|
363
387
|
|
364
|
-
|
388
|
+
it "permit replacing value with nil" do
|
365
389
|
@user_clone.send("#{@attribute}=".to_sym, nil)
|
366
390
|
@user_clone.save!
|
367
391
|
|
@@ -370,7 +394,7 @@ class ActiveRecordTest < Minitest::Test
|
|
370
394
|
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
371
395
|
end
|
372
396
|
|
373
|
-
|
397
|
+
it "permit replacing value with an empty string" do
|
374
398
|
@user_clone.send("#{@attribute}=".to_sym, '')
|
375
399
|
@user_clone.save!
|
376
400
|
|
@@ -379,7 +403,7 @@ class ActiveRecordTest < Minitest::Test
|
|
379
403
|
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
380
404
|
end
|
381
405
|
|
382
|
-
|
406
|
+
it "permit replacing value with a blank string" do
|
383
407
|
@user_clone.send("#{@attribute}=".to_sym, ' ')
|
384
408
|
@user_clone.save!
|
385
409
|
|
@@ -388,7 +412,7 @@ class ActiveRecordTest < Minitest::Test
|
|
388
412
|
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
389
413
|
end
|
390
414
|
|
391
|
-
|
415
|
+
it "permit replacing value" do
|
392
416
|
@user_clone.send("#{@attribute}=".to_sym, @new_value)
|
393
417
|
@user_clone.save!
|
394
418
|
|
@@ -398,8 +422,8 @@ class ActiveRecordTest < Minitest::Test
|
|
398
422
|
end
|
399
423
|
end
|
400
424
|
|
401
|
-
|
402
|
-
|
425
|
+
describe "JSON Serialization" do
|
426
|
+
before do
|
403
427
|
# JSON Does not support symbols, so they will come back as strings
|
404
428
|
# Convert symbols to string in the test
|
405
429
|
@h.keys.each do |k|
|
@@ -408,18 +432,18 @@ class ActiveRecordTest < Minitest::Test
|
|
408
432
|
end
|
409
433
|
end
|
410
434
|
|
411
|
-
|
435
|
+
it "return correct data type" do
|
412
436
|
assert_equal @h, @user_clone.data_json
|
413
437
|
assert @user.clone.data_json.kind_of?(Hash)
|
414
438
|
end
|
415
439
|
|
416
|
-
|
440
|
+
it "not coerce data type (leaves as hash) before save" do
|
417
441
|
u = User.new(data_json: @h)
|
418
442
|
assert_equal @h, u.data_json
|
419
443
|
assert u.data_json.kind_of?(Hash)
|
420
444
|
end
|
421
445
|
|
422
|
-
|
446
|
+
it "permit replacing value with nil" do
|
423
447
|
@user_clone.data_json = nil
|
424
448
|
@user_clone.save!
|
425
449
|
|
@@ -428,9 +452,9 @@ class ActiveRecordTest < Minitest::Test
|
|
428
452
|
assert_nil @user.encrypted_data_json
|
429
453
|
end
|
430
454
|
|
431
|
-
|
432
|
-
new_value
|
433
|
-
new_value['c']
|
455
|
+
it "permit replacing value" do
|
456
|
+
new_value = @h.clone
|
457
|
+
new_value['c'] = 'C'
|
434
458
|
@user_clone.data_json = new_value
|
435
459
|
@user_clone.save!
|
436
460
|
|
@@ -439,19 +463,19 @@ class ActiveRecordTest < Minitest::Test
|
|
439
463
|
end
|
440
464
|
end
|
441
465
|
|
442
|
-
|
443
|
-
|
466
|
+
describe "YAML Serialization" do
|
467
|
+
it "return correct data type" do
|
444
468
|
assert_equal @h, @user_clone.data_yaml
|
445
469
|
assert @user.clone.data_yaml.kind_of?(Hash)
|
446
470
|
end
|
447
471
|
|
448
|
-
|
472
|
+
it "not coerce data type (leaves as hash) before save" do
|
449
473
|
u = User.new(data_yaml: @h)
|
450
474
|
assert_equal @h, u.data_yaml
|
451
475
|
assert u.data_yaml.kind_of?(Hash)
|
452
476
|
end
|
453
477
|
|
454
|
-
|
478
|
+
it "permit replacing value with nil" do
|
455
479
|
@user_clone.data_yaml = nil
|
456
480
|
@user_clone.save!
|
457
481
|
|
@@ -460,9 +484,9 @@ class ActiveRecordTest < Minitest::Test
|
|
460
484
|
assert_nil @user.encrypted_data_yaml
|
461
485
|
end
|
462
486
|
|
463
|
-
|
464
|
-
new_value
|
465
|
-
new_value[:c]
|
487
|
+
it "permit replacing value" do
|
488
|
+
new_value = @h.clone
|
489
|
+
new_value[:c] = 'C'
|
466
490
|
@user_clone.data_yaml = new_value
|
467
491
|
@user_clone.save!
|
468
492
|
|
@@ -470,8 +494,37 @@ class ActiveRecordTest < Minitest::Test
|
|
470
494
|
assert_equal new_value, @user.data_yaml
|
471
495
|
end
|
472
496
|
end
|
497
|
+
end
|
473
498
|
|
499
|
+
describe 'changed?' do
|
500
|
+
it 'return false if it was not changed' do
|
501
|
+
assert_equal false, @user.encrypted_bank_account_number_changed?
|
502
|
+
assert_equal false, @user.bank_account_number_changed?
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'return true if it was changed' do
|
506
|
+
@user.bank_account_number = '15424623'
|
507
|
+
assert_equal true, @user.encrypted_bank_account_number_changed?
|
508
|
+
assert_equal true, @user.bank_account_number_changed?
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe 'uniqueness' do
|
514
|
+
before do
|
515
|
+
UniqueUser.destroy_all
|
516
|
+
@email = 'whatever@not-unique.com'
|
517
|
+
@username = 'gibby007'
|
518
|
+
@user = UniqueUser.create!(email: @email)
|
519
|
+
@email_user = UniqueUser.create!(username: @username)
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'does not allow duplicate values' do
|
523
|
+
duplicate = UniqueUser.new(email: @email)
|
524
|
+
assert_equal false, duplicate.valid?
|
525
|
+
assert_equal 'has already been taken', duplicate.errors.messages[:encrypted_email].first
|
474
526
|
end
|
475
527
|
end
|
528
|
+
|
476
529
|
end
|
477
530
|
end
|