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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +6 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +79 -0
- data/Gemfile +3 -0
- data/README.md +444 -0
- data/Rakefile +4 -15
- data/attr_encrypted.gemspec +63 -0
- data/certs/saghaulor.pem +21 -0
- data/checksum/attr_encrypted-3.0.0.gem.sha256 +1 -0
- data/checksum/attr_encrypted-3.0.0.gem.sha512 +1 -0
- data/lib/attr_encrypted.rb +196 -105
- data/lib/attr_encrypted/adapters/active_record.rb +64 -21
- data/lib/attr_encrypted/adapters/data_mapper.rb +1 -0
- data/lib/attr_encrypted/adapters/sequel.rb +1 -0
- data/lib/attr_encrypted/version.rb +3 -3
- data/test/active_record_test.rb +157 -57
- data/test/attr_encrypted_test.rb +117 -55
- data/test/compatibility_test.rb +20 -37
- data/test/data_mapper_test.rb +6 -6
- data/test/legacy_active_record_test.rb +16 -12
- data/test/legacy_attr_encrypted_test.rb +31 -30
- data/test/legacy_compatibility_test.rb +22 -31
- data/test/legacy_data_mapper_test.rb +11 -8
- data/test/legacy_sequel_test.rb +13 -9
- data/test/run.sh +12 -52
- data/test/sequel_test.rb +6 -6
- data/test/test_helper.rb +28 -16
- metadata +121 -70
- metadata.gz.sig +0 -0
- data/README.rdoc +0 -302
@@ -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
|
-
|
17
|
-
|
18
|
-
|
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
|
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
|
-
|
45
|
-
|
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, :
|
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
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module AttrEncrypted
|
2
2
|
# Contains information about this gem's version
|
3
3
|
module Version
|
4
|
-
MAJOR =
|
5
|
-
MINOR =
|
6
|
-
PATCH =
|
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
|
#
|
data/test/active_record_test.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
|
1
|
+
require_relative 'test_helper'
|
2
2
|
|
3
|
-
ActiveRecord::Base.establish_connection
|
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(:
|
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, :
|
51
|
-
attr_encrypted :credentials, :
|
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
|
-
|
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 ||= { :
|
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, :
|
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, :
|
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, :
|
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, :
|
103
|
+
attr_encryptor :email, key: SECRET_KEY
|
93
104
|
end
|
94
105
|
|
95
|
-
class
|
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!(:
|
122
|
+
Account.create!(key: SECRET_KEY, password: "password")
|
101
123
|
end
|
102
124
|
|
103
125
|
def test_should_encrypt_email
|
104
|
-
@person = Person.create
|
105
|
-
|
106
|
-
|
107
|
-
assert_equal @person.email, Person.
|
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
|
-
|
113
|
-
|
114
|
-
assert_equal @person.credentials, Person.
|
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
|
150
|
+
@person = Person.create(email: 'test@example.com')
|
129
151
|
@person2 = Person.find(@person.id)
|
130
|
-
|
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
|
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!(:
|
142
|
-
Account.create!(:
|
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
|
148
|
-
@user.attributes = ActionController::Parameters.new(:
|
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
|
154
|
-
@user.attributes = ActionController::Parameters.new(:
|
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
|
160
|
-
|
161
|
-
@user.attributes = ActionController::Parameters.new(:
|
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
|
-
|
167
|
-
@user = UserWithProtectedAttribute.new ActionController::Parameters.new(:
|
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
|
173
|
-
@user.attributes = {:
|
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
|
179
|
-
@user.attributes = {:
|
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
|
242
|
+
@user = UserWithProtectedAttribute.new(login: 'login', is_admin: false)
|
185
243
|
if ::ActiveRecord::VERSION::STRING > "3.1"
|
186
|
-
@user.send
|
244
|
+
@user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true)
|
187
245
|
else
|
188
|
-
@user.send
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|