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 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. Proc objects are evaluated as well. Any other key types
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 value.nil?
92
- encrypted_value = nil
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 = Marshal.dump(value) if options[:marshal]
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 encrypted_value.nil?
103
- decrypted_value = nil
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 = encrypted_value.unpack(options[:encode]).to_s if options[:encode]
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
- original_key = options[:key]
116
- options[:key] = self.class.send :evaluate_attr_encrypted_key, options[:key], self
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[:key] = original_key
138
+ options.merge!(original_options)
119
139
  value
120
140
  end
121
141
 
122
142
  define_method "#{attribute}=" do |value|
123
- original_key = options[:key]
124
- options[:key] = self.class.send :evaluate_attr_encrypted_key, options[:key], self
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[:key] = original_key
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 encryption keys specified as symbols (representing instance methods) or procs
133
- # If the key is not a symbol or proc then the original key is returned
134
- def evaluate_attr_encrypted_key(key, object)
135
- case key
136
- when Symbol
137
- object.send(key)
138
- when Proc
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
- key
163
+ option
142
164
  end
143
165
  end
144
166
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shuber-attr_encrypted
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Huber