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 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