shuber-attr_encrypted 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|