attr_encrypted 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,9 +18,7 @@ if defined?(ActiveRecord::Base)
18
18
  attr_encrypted_options[:encode] = true
19
19
 
20
20
  class << self
21
- alias_method :attr_encryptor, :attr_encrypted
22
21
  alias_method_chain :method_missing, :attr_encrypted
23
- alias_method :undefine_attribute_methods, :reset_column_information if ::ActiveRecord::VERSION::STRING < "3"
24
22
  end
25
23
 
26
24
  def perform_attribute_assignment(method, new_attributes, *args)
@@ -31,7 +29,7 @@ if defined?(ActiveRecord::Base)
31
29
  end
32
30
  private :perform_attribute_assignment
33
31
 
34
- if ::ActiveRecord::VERSION::STRING < "3.0" || ::ActiveRecord::VERSION::STRING > "3.1"
32
+ if ::ActiveRecord::VERSION::STRING > "3.1"
35
33
  def assign_attributes_with_attr_encrypted(*args)
36
34
  perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
37
35
  end
@@ -69,10 +67,10 @@ if defined?(ActiveRecord::Base)
69
67
  # We add accessor methods of the db columns to the list of instance
70
68
  # methods returned to let ActiveRecord define the accessor methods
71
69
  # for the db columns
72
-
70
+
73
71
  # Use with_connection so the connection doesn't stay pinned to the thread.
74
72
  connected = ::ActiveRecord::Base.connection_pool.with_connection(&:active?) rescue false
75
-
73
+
76
74
  if connected && table_exists?
77
75
  columns_hash.keys.inject(super) {|instance_methods, column_name| instance_methods.concat [column_name.to_sym, :"#{column_name}="]}
78
76
  else
@@ -85,13 +83,13 @@ if defined?(ActiveRecord::Base)
85
83
  #
86
84
  # NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
87
85
  #
88
- # This is useful for encrypting fields like email addresses. Your user's email addresses
86
+ # This is useful for encrypting fields like email addresses. Your user's email addresses
89
87
  # are encrypted in the database, but you can still look up a user by email for logging in
90
88
  #
91
89
  # Example
92
90
  #
93
91
  # class User < ActiveRecord::Base
94
- # attr_encrypted :email, :key => 'secret key'
92
+ # attr_encrypted :email, key: 'secret key'
95
93
  # end
96
94
  #
97
95
  # User.find_by_email_and_password('test@example.com', 'testing')
@@ -101,8 +99,9 @@ if defined?(ActiveRecord::Base)
101
99
  if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
102
100
  attribute_names = match.captures.last.split('_and_')
103
101
  attribute_names.each_with_index do |attribute, index|
104
- if attr_encrypted?(attribute)
102
+ if attr_encrypted?(attribute) && encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
105
103
  args[index] = send("encrypt_#{attribute}", args[index])
104
+ warn "DEPRECATION WARNING: This feature will be removed in the next major release."
106
105
  attribute_names[index] = encrypted_attributes[attribute.to_sym][:attribute]
107
106
  end
108
107
  end
@@ -114,5 +113,6 @@ if defined?(ActiveRecord::Base)
114
113
  end
115
114
  end
116
115
 
116
+ ActiveRecord::Base.extend AttrEncrypted
117
117
  ActiveRecord::Base.extend AttrEncrypted::Adapters::ActiveRecord
118
118
  end
@@ -11,6 +11,7 @@ if defined?(DataMapper)
11
11
 
12
12
  def included_with_attr_encrypted(base)
13
13
  included_without_attr_encrypted(base)
14
+ base.extend AttrEncrypted
14
15
  base.attr_encrypted_options[:encode] = true
15
16
  end
16
17
  end
@@ -9,5 +9,6 @@ if defined?(Sequel)
9
9
  end
10
10
  end
11
11
 
12
+ Sequel::Model.extend AttrEncrypted
12
13
  Sequel::Model.extend AttrEncrypted::Adapters::Sequel
13
14
  end
@@ -1,8 +1,8 @@
1
1
  module AttrEncrypted
2
2
  # Contains information about this gem's version
3
3
  module Version
4
- MAJOR = 1
5
- MINOR = 4
4
+ MAJOR = 2
5
+ MINOR = 0
6
6
  PATCH = 0
7
7
 
8
8
  # Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
@@ -1,4 +1,4 @@
1
- require File.expand_path('../test_helper', __FILE__)
1
+ require_relative 'test_helper'
2
2
 
3
3
  ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
4
4
 
@@ -28,6 +28,12 @@ def create_tables
28
28
  create_table :prime_ministers do |t|
29
29
  t.string :encrypted_name
30
30
  end
31
+ create_table :addresses do |t|
32
+ t.binary :encrypted_street
33
+ t.binary :encrypted_street_iv
34
+ t.binary :encrypted_zipcode
35
+ t.string :mode
36
+ end
31
37
  end
32
38
  end
33
39
  end
@@ -36,7 +42,6 @@ end
36
42
  create_tables
37
43
 
38
44
  ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
39
- ActiveRecord::Base.logger = Logger.new(nil) if ::ActiveRecord::VERSION::STRING < "3.0"
40
45
 
41
46
  if ::ActiveRecord::VERSION::STRING > "4.0"
42
47
  module Rack
@@ -51,15 +56,9 @@ end
51
56
  class Person < ActiveRecord::Base
52
57
  self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
53
58
  attr_encrypted :email, :key => SECRET_KEY
54
- attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY) }, :marshal => true
59
+ attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY, iv: SecureRandom.random_bytes(12)) }, :marshal => true
55
60
 
56
- if ActiveRecord::VERSION::STRING < "3"
57
- def after_initialize
58
- initialize_salt_and_credentials
59
- end
60
- else
61
- after_initialize :initialize_salt_and_credentials
62
- end
61
+ after_initialize :initialize_salt_and_credentials
63
62
 
64
63
  protected
65
64
 
@@ -75,7 +74,7 @@ end
75
74
 
76
75
  class PersonWithProcMode < Person
77
76
  attr_encrypted :email, :key => SECRET_KEY, :mode => Proc.new { :per_attribute_iv_and_salt }
78
- attr_encrypted :credentials, :key => SECRET_KEY, :mode => Proc.new { :single_iv_and_salt }
77
+ attr_encrypted :credentials, :key => SECRET_KEY, :mode => Proc.new { :single_iv_and_salt }, insecure_mode: true
79
78
  end
80
79
 
81
80
  class Account < ActiveRecord::Base
@@ -85,23 +84,30 @@ end
85
84
 
86
85
  class PersonWithSerialization < ActiveRecord::Base
87
86
  self.table_name = 'people'
88
- attr_encrypted :email, :key => 'a secret key'
87
+ attr_encrypted :email, :key => SECRET_KEY
89
88
  serialize :password
90
89
  end
91
90
 
92
91
  class UserWithProtectedAttribute < ActiveRecord::Base
93
92
  self.table_name = 'users'
94
- attr_encrypted :password, :key => 'a secret key'
93
+ attr_encrypted :password, :key => SECRET_KEY
95
94
  attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
96
95
  end
97
96
 
98
97
  class PersonUsingAlias < ActiveRecord::Base
99
98
  self.table_name = 'people'
100
- attr_encryptor :email, :key => 'a secret key'
99
+ attr_encryptor :email, :key => SECRET_KEY
101
100
  end
102
101
 
103
102
  class PrimeMinister < ActiveRecord::Base
104
- attr_encrypted :name, :marshal => true, :key => 'SECRET_KEY'
103
+ attr_encrypted :name, :marshal => true, :key => SECRET_KEY
104
+ end
105
+
106
+ class Address < ActiveRecord::Base
107
+ self.attr_encrypted_options[:marshal] = false
108
+ self.attr_encrypted_options[:encode] = false
109
+ attr_encrypted :street, encode_iv: false, key: SECRET_KEY
110
+ attr_encrypted :zipcode, key: SECRET_KEY, mode: Proc.new { |address| address.mode.to_sym }, insecure_mode: true
105
111
  end
106
112
 
107
113
  class ActiveRecordTest < Minitest::Test
@@ -116,14 +122,14 @@ class ActiveRecordTest < Minitest::Test
116
122
  @person = Person.create :email => 'test@example.com'
117
123
  refute_nil @person.encrypted_email
118
124
  refute_equal @person.email, @person.encrypted_email
119
- assert_equal @person.email, Person.find(:first).email
125
+ assert_equal @person.email, Person.first.email
120
126
  end
121
127
 
122
128
  def test_should_marshal_and_encrypt_credentials
123
129
  @person = Person.create
124
130
  refute_nil @person.encrypted_credentials
125
131
  refute_equal @person.credentials, @person.encrypted_credentials
126
- assert_equal @person.credentials, Person.find(:first).credentials
132
+ assert_equal @person.credentials, Person.first.credentials
127
133
  end
128
134
 
129
135
  def test_should_encode_by_default
@@ -155,6 +161,7 @@ class ActiveRecordTest < Minitest::Test
155
161
  end
156
162
 
157
163
  def test_should_set_attributes_regardless_of_arguments_order
164
+ # minitest does not implement `assert_nothing_raised` https://github.com/seattlerb/minitest/issues/112
158
165
  Account.new.attributes = { :password => "password" , :key => SECRET_KEY }
159
166
  end
160
167
 
@@ -263,7 +270,7 @@ class ActiveRecordTest < Minitest::Test
263
270
 
264
271
  refute_nil user.encrypted_email
265
272
  refute_equal user.email, user.encrypted_email
266
- assert_equal user.email, PersonUsingAlias.find(:first).email
273
+ assert_equal user.email, PersonUsingAlias.first.email
267
274
  end
268
275
 
269
276
  # See https://github.com/attr-encrypted/attr_encrypted/issues/68
@@ -278,4 +285,19 @@ class ActiveRecordTest < Minitest::Test
278
285
  assert_equal pm, result
279
286
  assert_equal 'Winston Churchill', pm.name
280
287
  end
288
+
289
+ def test_should_save_encrypted_data_as_binary
290
+ street = '123 Elm'
291
+ address = Address.new(street: street)
292
+ address.save!
293
+ refute_equal address.encrypted_street, street
294
+ assert_equal Address.first.street, street
295
+ end
296
+
297
+ def test_should_evaluate_proc_based_mode
298
+ street = '123 Elm'
299
+ zipcode = '12345'
300
+ address = Address.new(street: street, zipcode: zipcode, mode: :single_iv_and_salt)
301
+ assert_nil address.encrypted_zipcode_iv
302
+ end
281
303
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
- require File.expand_path('../test_helper', __FILE__)
2
+ require_relative 'test_helper'
3
3
 
4
4
  class SillyEncryptor
5
5
  def self.silly_encrypt(options)
@@ -12,6 +12,7 @@ class SillyEncryptor
12
12
  end
13
13
 
14
14
  class User
15
+ extend AttrEncrypted
15
16
  self.attr_encrypted_options[:key] = Proc.new { |user| SECRET_KEY } # default key
16
17
  self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
17
18
 
@@ -33,7 +34,8 @@ class User
33
34
  attr_accessor :salt
34
35
  attr_accessor :should_encrypt
35
36
 
36
- def initialize
37
+ def initialize(email: nil)
38
+ self.email = email
37
39
  self.salt = Time.now.to_i.to_s
38
40
  self.should_encrypt = true
39
41
  end
@@ -48,19 +50,40 @@ class Admin < User
48
50
  end
49
51
 
50
52
  class SomeOtherClass
53
+ extend AttrEncrypted
51
54
  def self.call(object)
52
55
  object.class
53
56
  end
54
57
  end
55
58
 
59
+ class YetAnotherClass
60
+ extend AttrEncrypted
61
+ self.attr_encrypted_options[:encode_iv] = false
62
+
63
+ attr_encrypted :email, :key => SECRET_KEY
64
+ attr_encrypted :phone_number, :key => SECRET_KEY, mode: Proc.new { |thing| thing.mode }, encode_iv: Proc.new { |thing| thing.encode_iv }, encode_salt: Proc.new { |thing| thing.encode_salt }
65
+
66
+ def initialize(email: nil, encode_iv: 'm', encode_salt: 'm', mode: :per_attribute_iv_and_salt)
67
+ self.email = email
68
+ @encode_iv = encode_iv
69
+ @encode_salt = encode_salt
70
+ @mode = mode
71
+ end
72
+
73
+ attr_reader :encode_iv, :encode_salt, :mode
74
+ end
75
+
56
76
  class AttrEncryptedTest < Minitest::Test
77
+ def setup
78
+ @iv = SecureRandom.random_bytes(12)
79
+ end
57
80
 
58
81
  def test_should_store_email_in_encrypted_attributes
59
82
  assert User.encrypted_attributes.include?(:email)
60
83
  end
61
84
 
62
85
  def test_should_not_store_salt_in_encrypted_attributes
63
- assert !User.encrypted_attributes.include?(:salt)
86
+ refute User.encrypted_attributes.include?(:salt)
64
87
  end
65
88
 
66
89
  def test_attr_encrypted_should_return_true_for_email
@@ -88,16 +111,16 @@ class AttrEncryptedTest < Minitest::Test
88
111
  end
89
112
 
90
113
  def test_should_not_encrypt_nil_value
91
- assert_nil User.encrypt_email(nil)
114
+ assert_nil User.encrypt_email(nil, iv: @iv)
92
115
  end
93
116
 
94
117
  def test_should_not_encrypt_empty_string
95
- assert_equal '', User.encrypt_email('')
118
+ assert_equal '', User.encrypt_email('', iv: @iv)
96
119
  end
97
120
 
98
121
  def test_should_encrypt_email
99
- refute_nil User.encrypt_email('test@example.com')
100
- refute_equal 'test@example.com', User.encrypt_email('test@example.com')
122
+ refute_nil User.encrypt_email('test@example.com', iv: @iv)
123
+ refute_equal 'test@example.com', User.encrypt_email('test@example.com', iv: @iv)
101
124
  end
102
125
 
103
126
  def test_should_encrypt_email_when_modifying_the_attr_writer
@@ -105,48 +128,52 @@ class AttrEncryptedTest < Minitest::Test
105
128
  assert_nil @user.encrypted_email
106
129
  @user.email = 'test@example.com'
107
130
  refute_nil @user.encrypted_email
108
- assert_equal User.encrypt_email('test@example.com'), @user.encrypted_email
131
+ iv = @user.encrypted_email_iv.unpack('m').first
132
+ salt = @user.encrypted_email_salt[1..-1].unpack('m').first
133
+ assert_equal User.encrypt_email('test@example.com', iv: iv, salt: salt), @user.encrypted_email
109
134
  end
110
135
 
111
136
  def test_should_not_decrypt_nil_value
112
- assert_nil User.decrypt_email(nil)
137
+ assert_nil User.decrypt_email(nil, iv: @iv)
113
138
  end
114
139
 
115
140
  def test_should_not_decrypt_empty_string
116
- assert_equal '', User.decrypt_email('')
141
+ assert_equal '', User.decrypt_email('', iv: @iv)
117
142
  end
118
143
 
119
144
  def test_should_decrypt_email
120
- encrypted_email = User.encrypt_email('test@example.com')
145
+ encrypted_email = User.encrypt_email('test@example.com', iv: @iv)
121
146
  refute_equal 'test@test.com', encrypted_email
122
- assert_equal 'test@example.com', User.decrypt_email(encrypted_email)
147
+ assert_equal 'test@example.com', User.decrypt_email(encrypted_email, iv: @iv)
123
148
  end
124
149
 
125
150
  def test_should_decrypt_email_when_reading
126
151
  @user = User.new
127
152
  assert_nil @user.email
128
- @user.encrypted_email = User.encrypt_email('test@example.com')
153
+ iv = @user.encrypted_email_iv.unpack('m').first
154
+ salt = @user.encrypted_email_salt[1..-1].unpack('m').first
155
+ @user.encrypted_email = User.encrypt_email('test@example.com', iv: iv, salt: salt)
129
156
  assert_equal 'test@example.com', @user.email
130
157
  end
131
158
 
132
159
  def test_should_encrypt_with_encoding
133
- assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
160
+ assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
134
161
  end
135
162
 
136
163
  def test_should_decrypt_with_encoding
137
- encrypted = User.encrypt_with_encoding('test')
138
- assert_equal 'test', User.decrypt_with_encoding(encrypted)
139
- assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
164
+ encrypted = User.encrypt_with_encoding('test', iv: @iv)
165
+ assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
166
+ assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
140
167
  end
141
168
 
142
169
  def test_should_encrypt_with_custom_encoding
143
- assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
170
+ assert_equal User.encrypt_with_encoding('test', iv: @iv), [User.encrypt_without_encoding('test', iv: @iv)].pack('m')
144
171
  end
145
172
 
146
173
  def test_should_decrypt_with_custom_encoding
147
- encrypted = User.encrypt_with_encoding('test')
148
- assert_equal 'test', User.decrypt_with_encoding(encrypted)
149
- assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
174
+ encrypted = User.encrypt_with_encoding('test', iv: @iv)
175
+ assert_equal 'test', User.decrypt_with_encoding(encrypted, iv: @iv)
176
+ assert_equal User.decrypt_with_encoding(encrypted, iv: @iv), User.decrypt_without_encoding(encrypted.unpack('m').first, iv: @iv)
150
177
  end
151
178
 
152
179
  def test_should_encrypt_with_marshaling
@@ -164,7 +191,7 @@ class AttrEncryptedTest < Minitest::Test
164
191
  assert_nil @user.ssn_encrypted
165
192
  @user.ssn = 'testing'
166
193
  refute_nil @user.ssn_encrypted
167
- encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt )
194
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt.unpack("m").first )
168
195
  assert_equal encrypted, @user.ssn_encrypted
169
196
  end
170
197
 
@@ -173,7 +200,7 @@ class AttrEncryptedTest < Minitest::Test
173
200
  assert_nil @user.crypted_password_test
174
201
  @user.password = 'testing'
175
202
  refute_nil @user.crypted_password_test
176
- encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
203
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
177
204
  assert_equal encrypted, @user.crypted_password_test
178
205
  end
179
206
 
@@ -182,7 +209,7 @@ class AttrEncryptedTest < Minitest::Test
182
209
  assert_nil @user.crypted_password_test
183
210
  @user.password = 'testing'
184
211
  refute_nil @user.crypted_password_test
185
- encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
212
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt.unpack("m").first)
186
213
  assert_equal encrypted, @user.crypted_password_test
187
214
  end
188
215
 
@@ -201,23 +228,24 @@ class AttrEncryptedTest < Minitest::Test
201
228
  end
202
229
 
203
230
  def test_should_evaluate_a_symbol_option
204
- assert_equal Object, Object.new.send(:evaluate_attr_encrypted_option, :class)
231
+ assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, :class)
205
232
  end
206
233
 
207
234
  def test_should_evaluate_a_proc_option
208
- assert_equal Object, Object.new.send(:evaluate_attr_encrypted_option, proc { |object| object.class })
235
+ assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, proc { |object| object.class })
209
236
  end
210
237
 
211
238
  def test_should_evaluate_a_lambda_option
212
- assert_equal Object, Object.new.send(:evaluate_attr_encrypted_option, lambda { |object| object.class })
239
+ assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, lambda { |object| object.class })
213
240
  end
214
241
 
215
242
  def test_should_evaluate_a_method_option
216
- assert_equal Object, Object.new.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call))
243
+ assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call))
217
244
  end
218
245
 
219
246
  def test_should_return_a_string_option
220
- assert_equal 'Object', Object.new.send(:evaluate_attr_encrypted_option, 'Object')
247
+ class_string = 'SomeOtherClass'
248
+ assert_equal class_string, SomeOtherClass.new.send(:evaluate_attr_encrypted_option, class_string)
221
249
  end
222
250
 
223
251
  def test_should_encrypt_with_true_if
@@ -225,7 +253,7 @@ class AttrEncryptedTest < Minitest::Test
225
253
  assert_nil @user.encrypted_with_true_if
226
254
  @user.with_true_if = 'testing'
227
255
  refute_nil @user.encrypted_with_true_if
228
- encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt)
256
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt.unpack("m").first)
229
257
  assert_equal encrypted, @user.encrypted_with_true_if
230
258
  end
231
259
 
@@ -242,7 +270,7 @@ class AttrEncryptedTest < Minitest::Test
242
270
  assert_nil @user.encrypted_with_false_unless
243
271
  @user.with_false_unless = 'testing'
244
272
  refute_nil @user.encrypted_with_false_unless
245
- encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt)
273
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt.unpack("m").first)
246
274
  assert_equal encrypted, @user.encrypted_with_false_unless
247
275
  end
248
276
 
@@ -261,11 +289,6 @@ class AttrEncryptedTest < Minitest::Test
261
289
  def test_should_always_reset_options
262
290
  @user = User.new
263
291
  @user.with_if_changed = "encrypt_stuff"
264
- @user.stubs(:instance_variable_get).returns(nil)
265
- @user.stubs(:instance_variable_set).raises("BadStuff")
266
- assert_raises RuntimeError do
267
- @user.with_if_changed
268
- end
269
292
 
270
293
  @user = User.new
271
294
  @user.should_encrypt = false
@@ -275,9 +298,9 @@ class AttrEncryptedTest < Minitest::Test
275
298
  end
276
299
 
277
300
  def test_should_cast_values_as_strings_before_encrypting
278
- string_encrypted_email = User.encrypt_email('3')
279
- assert_equal string_encrypted_email, User.encrypt_email(3)
280
- assert_equal '3', User.decrypt_email(string_encrypted_email)
301
+ string_encrypted_email = User.encrypt_email('3', iv: @iv)
302
+ assert_equal string_encrypted_email, User.encrypt_email(3, iv: @iv)
303
+ assert_equal '3', User.decrypt_email(string_encrypted_email, iv: @iv)
281
304
  end
282
305
 
283
306
  def test_should_create_query_accessor
@@ -296,12 +319,18 @@ class AttrEncryptedTest < Minitest::Test
296
319
  refute_equal @user.encrypted_email_iv, @user.crypted_password_test_iv
297
320
  end
298
321
 
322
+ def test_should_generate_iv_per_attribute_by_default
323
+ thing = YetAnotherClass.new(email: 'thing@thing.com')
324
+ refute_nil thing.encrypted_email_iv
325
+ end
326
+
299
327
  def test_should_vary_iv_per_instance
300
328
  @user1 = User.new
301
329
  @user1.email = 'email@example.com'
302
330
  @user2 = User.new
303
331
  @user2.email = 'email@example.com'
304
332
  refute_equal @user1.encrypted_email_iv, @user2.encrypted_email_iv
333
+ refute_equal @user1.encrypted_email, @user2.encrypted_email
305
334
  end
306
335
 
307
336
  def test_should_vary_salt_per_attribute
@@ -319,6 +348,11 @@ class AttrEncryptedTest < Minitest::Test
319
348
  refute_equal @user1.encrypted_email_salt, @user2.encrypted_email_salt
320
349
  end
321
350
 
351
+ def test_should_not_generate_salt_per_attribute_by_default
352
+ thing = YetAnotherClass.new(email: 'thing@thing.com')
353
+ assert_nil thing.encrypted_email_salt
354
+ end
355
+
322
356
  def test_should_decrypt_second_record
323
357
  @user1 = User.new
324
358
  @user1.email = 'test@example.com'
@@ -328,4 +362,32 @@ class AttrEncryptedTest < Minitest::Test
328
362
 
329
363
  assert_equal 'test@example.com', @user1.decrypt(:email, @user1.encrypted_email)
330
364
  end
365
+
366
+ def test_should_specify_the_default_algorithm
367
+ assert YetAnotherClass.encrypted_attributes[:email][:algorithm]
368
+ assert_equal YetAnotherClass.encrypted_attributes[:email][:algorithm], 'aes-256-gcm'
369
+ end
370
+
371
+ def test_should_not_encode_iv_when_encode_iv_is_false
372
+ email = 'thing@thing.com'
373
+ thing = YetAnotherClass.new(email: email)
374
+ refute thing.encrypted_email_iv =~ base64_encoding_regex
375
+ assert_equal thing.email, email
376
+ end
377
+
378
+ def test_should_base64_encode_iv_by_default
379
+ phone_number = '555-555-5555'
380
+ thing = YetAnotherClass.new
381
+ thing.phone_number = phone_number
382
+ assert thing.encrypted_phone_number_iv =~ base64_encoding_regex
383
+ assert_equal thing.phone_number, phone_number
384
+ end
385
+
386
+ def test_should_generate_unique_iv_for_every_encrypt_operation
387
+ user = User.new
388
+ user.email = 'initial_value@test.com'
389
+ original_iv = user.encrypted_email_iv
390
+ user.email = 'revised_value@test.com'
391
+ refute_equal original_iv, user.encrypted_email_iv
392
+ end
331
393
  end