attr_encrypted 1.4.0 → 2.0.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.
@@ -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