attr_encrypted 1.3.3 → 3.0.1

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.
@@ -4,59 +4,100 @@ if defined?(ActiveRecord::Base)
4
4
  module ActiveRecord
5
5
  def self.extended(base) # :nodoc:
6
6
  base.class_eval do
7
+
8
+ # https://github.com/attr-encrypted/attr_encrypted/issues/68
9
+ def reload_with_attr_encrypted(*args, &block)
10
+ result = reload_without_attr_encrypted(*args, &block)
11
+ self.class.encrypted_attributes.keys.each do |attribute_name|
12
+ instance_variable_set("@#{attribute_name}", nil)
13
+ end
14
+ result
15
+ end
16
+ alias_method_chain :reload, :attr_encrypted
17
+
7
18
  attr_encrypted_options[:encode] = true
19
+
8
20
  class << self
9
- alias_method :attr_encryptor, :attr_encrypted
10
21
  alias_method_chain :method_missing, :attr_encrypted
11
- alias_method :undefine_attribute_methods, :reset_column_information if ::ActiveRecord::VERSION::STRING < "3"
12
22
  end
13
23
 
14
24
  def perform_attribute_assignment(method, new_attributes, *args)
15
25
  return if new_attributes.blank?
16
- attributes = new_attributes.respond_to?(:with_indifferent_access) ? new_attributes.with_indifferent_access : new_attributes.symbolize_keys
17
- encrypted_attributes = self.class.encrypted_attributes.keys
18
- self.send method, attributes.except(*encrypted_attributes), *args
19
- self.send method, attributes.slice(*encrypted_attributes), *args
26
+
27
+ send method, new_attributes.reject { |k, _| self.class.encrypted_attributes.key?(k.to_sym) }, *args
28
+ send method, new_attributes.reject { |k, _| !self.class.encrypted_attributes.key?(k.to_sym) }, *args
20
29
  end
21
30
  private :perform_attribute_assignment
22
31
 
23
- if ::ActiveRecord::VERSION::STRING < "3.0" || ::ActiveRecord::VERSION::STRING > "3.1"
32
+ if ::ActiveRecord::VERSION::STRING > "3.1"
24
33
  def assign_attributes_with_attr_encrypted(*args)
25
34
  perform_attribute_assignment :assign_attributes_without_attr_encrypted, *args
26
35
  end
27
36
  alias_method_chain :assign_attributes, :attr_encrypted
28
- else
29
- def attributes_with_attr_encrypted=(*args)
30
- perform_attribute_assignment :attributes_without_attr_encrypted=, *args
31
- end
32
- alias_method_chain :attributes=, :attr_encrypted
33
37
  end
38
+
39
+ def attributes_with_attr_encrypted=(*args)
40
+ perform_attribute_assignment :attributes_without_attr_encrypted=, *args
41
+ end
42
+ alias_method_chain :attributes=, :attr_encrypted
34
43
  end
35
44
  end
36
45
 
37
46
  protected
38
47
 
39
- # Ensures the attribute methods for db fields have been defined before calling the original
40
48
  # <tt>attr_encrypted</tt> method
41
49
  def attr_encrypted(*attrs)
42
- define_attribute_methods rescue nil
43
50
  super
44
- undefine_attribute_methods
45
- attrs.reject { |attr| attr.is_a?(Hash) }.each { |attr| alias_method "#{attr}_before_type_cast", attr }
51
+ options = attrs.extract_options!
52
+ attr = attrs.pop
53
+ options.merge! encrypted_attributes[attr]
54
+
55
+ define_method("#{attr}_changed?") do
56
+ if send("#{options[:attribute]}_changed?")
57
+ send(attr) != send("#{attr}_was")
58
+ end
59
+ end
60
+
61
+ define_method("#{attr}_was") do
62
+ attr_was_options = { operation: :decrypting }
63
+ attr_was_options[:iv]= send("#{options[:attribute]}_iv_was") if respond_to?("#{options[:attribute]}_iv_was")
64
+ attr_was_options[:salt]= send("#{options[:attribute]}_salt_was") if respond_to?("#{options[:attribute]}_salt_was")
65
+ encrypted_attributes[attr].merge!(attr_was_options)
66
+ evaluated_options = evaluated_attr_encrypted_options_for(attr)
67
+ [:iv, :salt, :operation].each { |key| encrypted_attributes[attr].delete(key) }
68
+ self.class.decrypt(attr, send("#{options[:attribute]}_was"), evaluated_options)
69
+ end
70
+
71
+ alias_method "#{attr}_before_type_cast", attr
72
+ end
73
+
74
+ def attribute_instance_methods_as_symbols
75
+ # We add accessor methods of the db columns to the list of instance
76
+ # methods returned to let ActiveRecord define the accessor methods
77
+ # for the db columns
78
+
79
+ # Use with_connection so the connection doesn't stay pinned to the thread.
80
+ connected = ::ActiveRecord::Base.connection_pool.with_connection(&:active?) rescue false
81
+
82
+ if connected && table_exists?
83
+ columns_hash.keys.inject(super) {|instance_methods, column_name| instance_methods.concat [column_name.to_sym, :"#{column_name}="]}
84
+ else
85
+ super
86
+ end
46
87
  end
47
88
 
48
- # Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
89
+ # Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
49
90
  # encrypted attributes
50
91
  #
51
92
  # NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
52
93
  #
53
- # This is useful for encrypting fields like email addresses. Your user's email addresses
94
+ # This is useful for encrypting fields like email addresses. Your user's email addresses
54
95
  # are encrypted in the database, but you can still look up a user by email for logging in
55
96
  #
56
97
  # Example
57
98
  #
58
99
  # class User < ActiveRecord::Base
59
- # attr_encrypted :email, :key => 'secret key'
100
+ # attr_encrypted :email, key: 'secret key'
60
101
  # end
61
102
  #
62
103
  # User.find_by_email_and_password('test@example.com', 'testing')
@@ -66,8 +107,9 @@ if defined?(ActiveRecord::Base)
66
107
  if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
67
108
  attribute_names = match.captures.last.split('_and_')
68
109
  attribute_names.each_with_index do |attribute, index|
69
- if attr_encrypted?(attribute)
110
+ if attr_encrypted?(attribute) && encrypted_attributes[attribute.to_sym][:mode] == :single_iv_and_salt
70
111
  args[index] = send("encrypt_#{attribute}", args[index])
112
+ warn "DEPRECATION WARNING: This feature will be removed in the next major release."
71
113
  attribute_names[index] = encrypted_attributes[attribute.to_sym][:attribute]
72
114
  end
73
115
  end
@@ -79,5 +121,6 @@ if defined?(ActiveRecord::Base)
79
121
  end
80
122
  end
81
123
 
124
+ ActiveRecord::Base.extend AttrEncrypted
82
125
  ActiveRecord::Base.extend AttrEncrypted::Adapters::ActiveRecord
83
- end
126
+ 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,9 +1,9 @@
1
1
  module AttrEncrypted
2
2
  # Contains information about this gem's version
3
3
  module Version
4
- MAJOR = 1
5
- MINOR = 3
6
- PATCH = 3
4
+ MAJOR = 3
5
+ MINOR = 0
6
+ PATCH = 1
7
7
 
8
8
  # Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
9
9
  #
@@ -1,15 +1,16 @@
1
- require File.expand_path('../test_helper', __FILE__)
1
+ require_relative 'test_helper'
2
2
 
3
- ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
3
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
4
4
 
5
5
  def create_tables
6
6
  silence_stream(STDOUT) do
7
- ActiveRecord::Schema.define(:version => 1) do
7
+ ActiveRecord::Schema.define(version: 1) do
8
8
  create_table :people do |t|
9
9
  t.string :encrypted_email
10
10
  t.string :password
11
11
  t.string :encrypted_credentials
12
12
  t.binary :salt
13
+ t.binary :key_iv
13
14
  t.string :encrypted_email_salt
14
15
  t.string :encrypted_credentials_salt
15
16
  t.string :encrypted_email_iv
@@ -23,8 +24,19 @@ def create_tables
23
24
  create_table :users do |t|
24
25
  t.string :login
25
26
  t.string :encrypted_password
27
+ t.string :encrypted_password_iv
26
28
  t.boolean :is_admin
27
29
  end
30
+ create_table :prime_ministers do |t|
31
+ t.string :encrypted_name
32
+ t.string :encrypted_name_iv
33
+ end
34
+ create_table :addresses do |t|
35
+ t.binary :encrypted_street
36
+ t.binary :encrypted_street_iv
37
+ t.binary :encrypted_zipcode
38
+ t.string :mode
39
+ end
28
40
  end
29
41
  end
30
42
  end
@@ -33,7 +45,6 @@ end
33
45
  create_tables
34
46
 
35
47
  ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
36
- ActiveRecord::Base.logger = Logger.new(nil) if ::ActiveRecord::VERSION::STRING < "3.0"
37
48
 
38
49
  if ::ActiveRecord::VERSION::STRING > "4.0"
39
50
  module Rack
@@ -47,22 +58,17 @@ end
47
58
 
48
59
  class Person < ActiveRecord::Base
49
60
  self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
50
- attr_encrypted :email, :key => SECRET_KEY
51
- attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY) }, :marshal => true
61
+ attr_encrypted :email, key: SECRET_KEY
62
+ attr_encrypted :credentials, key: Proc.new { |user| Encryptor.encrypt(value: user.salt, key: SECRET_KEY, iv: user.key_iv) }, marshal: true
52
63
 
53
- if ActiveRecord::VERSION::STRING < "3"
54
- def after_initialize
55
- initialize_salt_and_credentials
56
- end
57
- else
58
- after_initialize :initialize_salt_and_credentials
59
- end
64
+ after_initialize :initialize_salt_and_credentials
60
65
 
61
66
  protected
62
67
 
63
68
  def initialize_salt_and_credentials
69
+ self.key_iv ||= SecureRandom.random_bytes(12)
64
70
  self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15]
65
- self.credentials ||= { :username => 'example', :password => 'test' }
71
+ self.credentials ||= { username: 'example', password: 'test' }
66
72
  end
67
73
  end
68
74
 
@@ -70,48 +76,64 @@ class PersonWithValidation < Person
70
76
  validates_presence_of :email
71
77
  end
72
78
 
79
+ class PersonWithProcMode < Person
80
+ attr_encrypted :email, key: SECRET_KEY, mode: Proc.new { :per_attribute_iv_and_salt }
81
+ attr_encrypted :credentials, key: SECRET_KEY, mode: Proc.new { :single_iv_and_salt }, insecure_mode: true
82
+ end
83
+
73
84
  class Account < ActiveRecord::Base
74
85
  attr_accessor :key
75
- attr_encrypted :password, :key => Proc.new {|account| account.key}
86
+ attr_encrypted :password, key: Proc.new {|account| account.key}
76
87
  end
77
88
 
78
89
  class PersonWithSerialization < ActiveRecord::Base
79
90
  self.table_name = 'people'
80
- attr_encrypted :email, :key => 'a secret key'
91
+ attr_encrypted :email, key: SECRET_KEY
81
92
  serialize :password
82
93
  end
83
94
 
84
95
  class UserWithProtectedAttribute < ActiveRecord::Base
85
96
  self.table_name = 'users'
86
- attr_encrypted :password, :key => 'a secret key'
97
+ attr_encrypted :password, key: SECRET_KEY
87
98
  attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0"
88
99
  end
89
100
 
90
101
  class PersonUsingAlias < ActiveRecord::Base
91
102
  self.table_name = 'people'
92
- attr_encryptor :email, :key => 'a secret key'
103
+ attr_encryptor :email, key: SECRET_KEY
93
104
  end
94
105
 
95
- class ActiveRecordTest < Test::Unit::TestCase
106
+ class PrimeMinister < ActiveRecord::Base
107
+ attr_encrypted :name, marshal: true, key: SECRET_KEY
108
+ end
109
+
110
+ class Address < ActiveRecord::Base
111
+ self.attr_encrypted_options[:marshal] = false
112
+ self.attr_encrypted_options[:encode] = false
113
+ attr_encrypted :street, encode_iv: false, key: SECRET_KEY
114
+ attr_encrypted :zipcode, key: SECRET_KEY, mode: Proc.new { |address| address.mode.to_sym }, insecure_mode: true
115
+ end
116
+
117
+ class ActiveRecordTest < Minitest::Test
96
118
 
97
119
  def setup
98
120
  ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
99
121
  create_tables
100
- Account.create!(:key => SECRET_KEY, :password => "password")
122
+ Account.create!(key: SECRET_KEY, password: "password")
101
123
  end
102
124
 
103
125
  def test_should_encrypt_email
104
- @person = Person.create :email => 'test@example.com'
105
- assert_not_nil @person.encrypted_email
106
- assert_not_equal @person.email, @person.encrypted_email
107
- assert_equal @person.email, Person.find(:first).email
126
+ @person = Person.create(email: 'test@example.com')
127
+ refute_nil @person.encrypted_email
128
+ refute_equal @person.email, @person.encrypted_email
129
+ assert_equal @person.email, Person.first.email
108
130
  end
109
131
 
110
132
  def test_should_marshal_and_encrypt_credentials
111
133
  @person = Person.create
112
- assert_not_nil @person.encrypted_credentials
113
- assert_not_equal @person.credentials, @person.encrypted_credentials
114
- assert_equal @person.credentials, Person.find(:first).credentials
134
+ refute_nil @person.encrypted_credentials
135
+ refute_equal @person.credentials, @person.encrypted_credentials
136
+ assert_equal @person.credentials, Person.first.credentials
115
137
  end
116
138
 
117
139
  def test_should_encode_by_default
@@ -125,67 +147,103 @@ class ActiveRecordTest < Test::Unit::TestCase
125
147
  end
126
148
 
127
149
  def test_should_encrypt_decrypt_with_iv
128
- @person = Person.create :email => 'test@example.com'
150
+ @person = Person.create(email: 'test@example.com')
129
151
  @person2 = Person.find(@person.id)
130
- assert_not_nil @person2.encrypted_email_iv
152
+ refute_nil @person2.encrypted_email_iv
131
153
  assert_equal 'test@example.com', @person2.email
132
154
  end
133
155
 
134
156
  def test_should_ensure_attributes_can_be_deserialized
135
- @person = PersonWithSerialization.new :email => 'test@example.com', :password => %w(an array of strings)
157
+ @person = PersonWithSerialization.new(email: 'test@example.com', password: %w(an array of strings))
136
158
  @person.save
137
159
  assert_equal @person.password, %w(an array of strings)
138
160
  end
139
161
 
140
162
  def test_should_create_an_account_regardless_of_arguments_order
141
- Account.create!(:key => SECRET_KEY, :password => "password")
142
- Account.create!(:password => "password" , :key => SECRET_KEY)
163
+ Account.create!(key: SECRET_KEY, password: "password")
164
+ Account.create!(password: "password" , key: SECRET_KEY)
165
+ end
166
+
167
+ def test_should_set_attributes_regardless_of_arguments_order
168
+ # minitest does not implement `assert_nothing_raised` https://github.com/seattlerb/minitest/issues/112
169
+ Account.new.attributes = { password: "password", key: SECRET_KEY }
170
+ end
171
+
172
+ def test_should_preserve_hash_key_type
173
+ hash = { foo: 'bar' }
174
+ account = Account.create!(key: hash)
175
+ assert_equal account.key, hash
176
+ end
177
+
178
+ def test_should_create_changed_predicate
179
+ person = Person.create!(email: 'test@example.com')
180
+ refute person.email_changed?
181
+ person.email = 'test@example.com'
182
+ refute person.email_changed?
183
+ person.email = nil
184
+ assert person.email_changed?
185
+ person.email = 'test2@example.com'
186
+ assert person.email_changed?
187
+ end
188
+
189
+ def test_should_create_was_predicate
190
+ original_email = 'test@example.com'
191
+ person = Person.create!(email: original_email)
192
+ assert_equal original_email, person.email_was
193
+ person.email = 'test2@example.com'
194
+ assert_equal original_email, person.email_was
195
+ old_pm_name = "Winston Churchill"
196
+ pm = PrimeMinister.create!(name: old_pm_name)
197
+ assert_equal old_pm_name, pm.name_was
198
+ old_zipcode = "90210"
199
+ address = Address.create!(zipcode: old_zipcode, mode: "single_iv_and_salt")
200
+ assert_equal old_zipcode, address.zipcode_was
143
201
  end
144
202
 
145
203
  if ::ActiveRecord::VERSION::STRING > "4.0"
146
204
  def test_should_assign_attributes
147
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
148
- @user.attributes = ActionController::Parameters.new(:login => 'modified', :is_admin => true).permit(:login)
205
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
206
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
149
207
  assert_equal 'modified', @user.login
150
208
  end
151
209
 
152
210
  def test_should_not_assign_protected_attributes
153
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
154
- @user.attributes = ActionController::Parameters.new(:login => 'modified', :is_admin => true).permit(:login)
211
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
212
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login)
155
213
  assert !@user.is_admin?
156
214
  end
157
215
 
158
216
  def test_should_raise_exception_if_not_permitted
159
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
160
- assert_raise ActiveModel::ForbiddenAttributesError do
161
- @user.attributes = ActionController::Parameters.new(:login => 'modified', :is_admin => true)
217
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
218
+ assert_raises ActiveModel::ForbiddenAttributesError do
219
+ @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true)
162
220
  end
163
221
  end
164
222
 
165
223
  def test_should_raise_exception_on_init_if_not_permitted
166
- assert_raise ActiveModel::ForbiddenAttributesError do
167
- @user = UserWithProtectedAttribute.new ActionController::Parameters.new(:login => 'modified', :is_admin => true)
224
+ assert_raises ActiveModel::ForbiddenAttributesError do
225
+ @user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true)
168
226
  end
169
227
  end
170
228
  else
171
229
  def test_should_assign_attributes
172
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
173
- @user.attributes = {:login => 'modified', :is_admin => true}
230
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
231
+ @user.attributes = { login: 'modified', is_admin: true }
174
232
  assert_equal 'modified', @user.login
175
233
  end
176
234
 
177
235
  def test_should_not_assign_protected_attributes
178
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
179
- @user.attributes = {:login => 'modified', :is_admin => true}
236
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
237
+ @user.attributes = { login: 'modified', is_admin: true }
180
238
  assert !@user.is_admin?
181
239
  end
182
240
 
183
241
  def test_should_assign_protected_attributes
184
- @user = UserWithProtectedAttribute.new :login => 'login', :is_admin => false
242
+ @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
185
243
  if ::ActiveRecord::VERSION::STRING > "3.1"
186
- @user.send :assign_attributes, {:login => 'modified', :is_admin => true}, :without_protection => true
244
+ @user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true)
187
245
  else
188
- @user.send :attributes=, {:login => 'modified', :is_admin => true}, false
246
+ @user.send(:attributes=, { login: 'modified', is_admin: true }, false)
189
247
  end
190
248
  assert @user.is_admin?
191
249
  end
@@ -196,6 +254,22 @@ class ActiveRecordTest < Test::Unit::TestCase
196
254
  assert_nil(@person.attributes = nil)
197
255
  end
198
256
 
257
+ def test_should_allow_proc_based_mode
258
+ @person = PersonWithProcMode.create(email: 'test@example.com', credentials: 'password123')
259
+
260
+ # Email is :per_attribute_iv_and_salt
261
+ assert_equal @person.class.encrypted_attributes[:email][:mode].class, Proc
262
+ assert_equal @person.class.encrypted_attributes[:email][:mode].call, :per_attribute_iv_and_salt
263
+ refute_nil @person.encrypted_email_salt
264
+ refute_nil @person.encrypted_email_iv
265
+
266
+ # Credentials is :single_iv_and_salt
267
+ assert_equal @person.class.encrypted_attributes[:credentials][:mode].class, Proc
268
+ assert_equal @person.class.encrypted_attributes[:credentials][:mode].call, :single_iv_and_salt
269
+ assert_nil @person.encrypted_credentials_salt
270
+ assert_nil @person.encrypted_credentials_iv
271
+ end
272
+
199
273
  if ::ActiveRecord::VERSION::STRING > "3.1"
200
274
  def test_should_allow_assign_attributes_with_nil
201
275
  @person = Person.new
@@ -203,15 +277,41 @@ class ActiveRecordTest < Test::Unit::TestCase
203
277
  end
204
278
  end
205
279
 
206
- class TestAlias < Test::Unit::TestCase
207
- def test_that_alias_encrypts_column
208
- user = PersonUsingAlias.new
209
- user.email = 'test@example.com'
210
- user.save
280
+ def test_that_alias_encrypts_column
281
+ user = PersonUsingAlias.new
282
+ user.email = 'test@example.com'
283
+ user.save
211
284
 
212
- assert_not_nil user.encrypted_email
213
- assert_not_equal user.email, user.encrypted_email
214
- assert_equal user.email, PersonUsingAlias.find(:first).email
215
- end
285
+ refute_nil user.encrypted_email
286
+ refute_equal user.email, user.encrypted_email
287
+ assert_equal user.email, PersonUsingAlias.first.email
288
+ end
289
+
290
+ # See https://github.com/attr-encrypted/attr_encrypted/issues/68
291
+ def test_should_invalidate_virtual_attributes_on_reload
292
+ old_pm_name = 'Winston Churchill'
293
+ new_pm_name = 'Neville Chamberlain'
294
+ pm = PrimeMinister.create!(name: old_pm_name)
295
+ assert_equal old_pm_name, pm.name
296
+ pm.name = new_pm_name
297
+ assert_equal new_pm_name, pm.name
298
+
299
+ result = pm.reload
300
+ assert_equal pm, result
301
+ assert_equal old_pm_name, pm.name
302
+ end
303
+
304
+ def test_should_save_encrypted_data_as_binary
305
+ street = '123 Elm'
306
+ address = Address.create!(street: street)
307
+ refute_equal address.encrypted_street, street
308
+ assert_equal Address.first.street, street
309
+ end
310
+
311
+ def test_should_evaluate_proc_based_mode
312
+ street = '123 Elm'
313
+ zipcode = '12345'
314
+ address = Address.new(street: street, zipcode: zipcode, mode: :single_iv_and_salt)
315
+ assert_nil address.encrypted_zipcode_iv
216
316
  end
217
317
  end