shuber-attr_encrypted 1.0.1 → 1.0.2
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.
- data/CHANGELOG +2 -0
- data/README.markdown +13 -0
- data/lib/huberry/class.rb +52 -30
- data/test/attr_encrypted_test.rb +59 -0
- metadata +1 -1
data/CHANGELOG
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
* Add support for data mapper
|
6
6
|
* Attribute methods are now defined before calling attr_encrypted when using active record
|
7
7
|
* Add ability to specify your own encoding directives
|
8
|
+
* Update logic that evaluates symbol and proc options
|
9
|
+
* Add support for :if and :unless options
|
8
10
|
|
9
11
|
2009-01-07 - Sean Huber (shuber@huberry.com)
|
10
12
|
* Initial commit
|
data/README.markdown
CHANGED
@@ -104,6 +104,19 @@ You can pass a proc object as the `:key` option as well:
|
|
104
104
|
end
|
105
105
|
|
106
106
|
|
107
|
+
### Conditional encrypting ###
|
108
|
+
|
109
|
+
There may be times that you want to only encrypt when certain conditions are met. For example maybe you're using rails and you don't want to encrypt
|
110
|
+
attributes when you're in development mode. You can specify conditions like this:
|
111
|
+
|
112
|
+
class User
|
113
|
+
attr_encrypted :email, :key => 'a secret key', :unless => Rails.env.development?
|
114
|
+
end
|
115
|
+
|
116
|
+
You can specify both `:if` and `:unless` options. If you pass a symbol representing an instance method then the result of the method will be evaluated.
|
117
|
+
Any objects that respond to :call are evaluated as well.
|
118
|
+
|
119
|
+
|
107
120
|
### Custom encryptor ###
|
108
121
|
|
109
122
|
The [Huberry::Encryptor](http://github.com/shuber/encryptor) class is used by default. You may use your own custom encryptor by specifying
|
data/lib/huberry/class.rb
CHANGED
@@ -23,8 +23,8 @@ module Huberry
|
|
23
23
|
#
|
24
24
|
# :key => The encryption key. This option may not be required if you're using a custom encryptor. If you pass
|
25
25
|
# a symbol representing an instance method then the :key option will be replaced with the result of the
|
26
|
-
# method before being passed to the encryptor.
|
27
|
-
# will be passed directly to the encryptor.
|
26
|
+
# method before being passed to the encryptor. Objects that respond to :call are evaluated as well (including procs).
|
27
|
+
# Any other key types will be passed directly to the encryptor.
|
28
28
|
#
|
29
29
|
# :encode => If set to true, attributes will be encoded as well as encrypted. This is useful if you're
|
30
30
|
# planning on storing the encrypted attributes in a database. The default encoding is 'm*' (base64),
|
@@ -42,6 +42,13 @@ module Huberry
|
|
42
42
|
#
|
43
43
|
# :decrypt_method => The decrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :decrypt.
|
44
44
|
#
|
45
|
+
# :if => Attributes are only encrypted if this option evaluates to true. If you pass a symbol representing an instance
|
46
|
+
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
47
|
+
# Defaults to true.
|
48
|
+
#
|
49
|
+
# :unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance
|
50
|
+
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
51
|
+
# Defaults to false.
|
45
52
|
#
|
46
53
|
# You can specify your own default options
|
47
54
|
#
|
@@ -76,7 +83,9 @@ module Huberry
|
|
76
83
|
:encrypt_method => :encrypt,
|
77
84
|
:decrypt_method => :decrypt,
|
78
85
|
:encode => false,
|
79
|
-
:marshal => false
|
86
|
+
:marshal => false,
|
87
|
+
:if => true,
|
88
|
+
:unless => false
|
80
89
|
}.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
81
90
|
options[:encode] = 'm*' if options[:encode] == true
|
82
91
|
|
@@ -88,57 +97,70 @@ module Huberry
|
|
88
97
|
attr_accessor encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name)
|
89
98
|
|
90
99
|
define_class_method "encrypt_#{attribute}" do |value|
|
91
|
-
if
|
92
|
-
|
100
|
+
if options[:if] && !options[:unless]
|
101
|
+
if value.nil?
|
102
|
+
encrypted_value = nil
|
103
|
+
else
|
104
|
+
value = Marshal.dump(value) if options[:marshal]
|
105
|
+
encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value)
|
106
|
+
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
|
107
|
+
end
|
108
|
+
encrypted_value
|
93
109
|
else
|
94
|
-
value
|
95
|
-
encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value)
|
96
|
-
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
|
110
|
+
value
|
97
111
|
end
|
98
|
-
encrypted_value
|
99
112
|
end
|
100
113
|
|
101
114
|
define_class_method "decrypt_#{attribute}" do |encrypted_value|
|
102
|
-
if
|
103
|
-
|
115
|
+
if options[:if] && !options[:unless]
|
116
|
+
if encrypted_value.nil?
|
117
|
+
decrypted_value = nil
|
118
|
+
else
|
119
|
+
encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode]
|
120
|
+
decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value))
|
121
|
+
decrypted_value = Marshal.load(decrypted_value) if options[:marshal]
|
122
|
+
end
|
123
|
+
decrypted_value
|
104
124
|
else
|
105
|
-
encrypted_value
|
106
|
-
decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value))
|
107
|
-
decrypted_value = Marshal.load(decrypted_value) if options[:marshal]
|
125
|
+
encrypted_value
|
108
126
|
end
|
109
|
-
decrypted_value
|
110
127
|
end
|
111
128
|
|
112
129
|
define_method "#{attribute}" do
|
113
130
|
value = instance_variable_get("@#{attribute}")
|
114
131
|
encrypted_value = read_attribute(encrypted_attribute_name)
|
115
|
-
|
116
|
-
|
132
|
+
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
133
|
+
hash[option] = options[option]
|
134
|
+
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
135
|
+
hash
|
136
|
+
end
|
117
137
|
value = instance_variable_set("@#{attribute}", self.class.send("decrypt_#{attribute}".to_sym, encrypted_value)) if value.nil? && !encrypted_value.nil?
|
118
|
-
options
|
138
|
+
options.merge!(original_options)
|
119
139
|
value
|
120
140
|
end
|
121
141
|
|
122
142
|
define_method "#{attribute}=" do |value|
|
123
|
-
|
124
|
-
|
143
|
+
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
144
|
+
hash[option] = options[option]
|
145
|
+
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
146
|
+
hash
|
147
|
+
end
|
125
148
|
write_attribute(encrypted_attribute_name, self.class.send("encrypt_#{attribute}".to_sym, value))
|
126
|
-
options
|
149
|
+
options.merge!(original_options)
|
127
150
|
instance_variable_set("@#{attribute}", value)
|
128
151
|
end
|
129
152
|
end
|
130
153
|
end
|
131
154
|
|
132
|
-
# Evaluates
|
133
|
-
# If the
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
key.call(object)
|
155
|
+
# Evaluates an option specified as a symbol representing an instance method or a proc
|
156
|
+
# If the option is not a symbol or proc then the original option is returned
|
157
|
+
def evaluate_attr_encrypted_option(option, object)
|
158
|
+
if option.is_a?(Symbol) && object.respond_to?(option)
|
159
|
+
object.send(option)
|
160
|
+
elsif option.respond_to?(:call)
|
161
|
+
option.call(object)
|
140
162
|
else
|
141
|
-
|
163
|
+
option
|
142
164
|
end
|
143
165
|
end
|
144
166
|
end
|
data/test/attr_encrypted_test.rb
CHANGED
@@ -20,6 +20,10 @@ class User
|
|
20
20
|
attr_encrypted :with_encoding, :key => 'secret key', :encode => true
|
21
21
|
attr_encrypted :with_custom_encoding, :key => 'secret key', :encode => 'm'
|
22
22
|
attr_encrypted :with_marshaling, :key => 'secret key', :marshal => true
|
23
|
+
attr_encrypted :with_true_if, :key => 'secret key', :if => true
|
24
|
+
attr_encrypted :with_false_if, :key => 'secret key', :if => false
|
25
|
+
attr_encrypted :with_true_unless, :key => 'secret key', :unless => true
|
26
|
+
attr_encrypted :with_false_unless, :key => 'secret key', :unless => false
|
23
27
|
attr_accessor :salt
|
24
28
|
|
25
29
|
def initialize
|
@@ -32,6 +36,9 @@ class Admin < User
|
|
32
36
|
end
|
33
37
|
|
34
38
|
class SomeOtherClass
|
39
|
+
def self.call(object)
|
40
|
+
object.class
|
41
|
+
end
|
35
42
|
end
|
36
43
|
|
37
44
|
class AttrEncryptedTest < Test::Unit::TestCase
|
@@ -175,4 +182,56 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
175
182
|
assert SomeOtherClass.encrypted_attributes.empty?
|
176
183
|
end
|
177
184
|
|
185
|
+
def test_should_evaluate_a_symbol_option
|
186
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, :class, Object.new)
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_should_evaluate_a_proc_option
|
190
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, proc { |object| object.class }, Object.new)
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_should_evaluate_a_lambda_option
|
194
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, lambda { |object| object.class }, Object.new)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_should_evaluate_a_method_option
|
198
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call), Object.new)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_should_return_a_string_option
|
202
|
+
assert_equal 'Object', Object.send(:evaluate_attr_encrypted_option, 'Object', Object.new)
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_should_encrypt_with_true_if
|
206
|
+
@user = User.new
|
207
|
+
assert_nil @user.encrypted_with_true_if
|
208
|
+
@user.with_true_if = 'testing'
|
209
|
+
assert_not_nil @user.encrypted_with_true_if
|
210
|
+
assert_equal Huberry::Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.encrypted_with_true_if
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_should_not_encrypt_with_false_if
|
214
|
+
@user = User.new
|
215
|
+
assert_nil @user.encrypted_with_false_if
|
216
|
+
@user.with_false_if = 'testing'
|
217
|
+
assert_not_nil @user.encrypted_with_false_if
|
218
|
+
assert_equal 'testing', @user.encrypted_with_false_if
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_should_encrypt_with_false_unless
|
222
|
+
@user = User.new
|
223
|
+
assert_nil @user.encrypted_with_false_unless
|
224
|
+
@user.with_false_unless = 'testing'
|
225
|
+
assert_not_nil @user.encrypted_with_false_unless
|
226
|
+
assert_equal Huberry::Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.encrypted_with_false_unless
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_should_not_encrypt_with_true_unless
|
230
|
+
@user = User.new
|
231
|
+
assert_nil @user.encrypted_with_true_unless
|
232
|
+
@user.with_true_unless = 'testing'
|
233
|
+
assert_not_nil @user.encrypted_with_true_unless
|
234
|
+
assert_equal 'testing', @user.encrypted_with_true_unless
|
235
|
+
end
|
236
|
+
|
178
237
|
end
|