encrypted_attributes 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,14 @@
1
1
  == master
2
2
 
3
+ == 0.4.0 / 2009-05-02
4
+
5
+ * Replace dynamic :salt option with :embed_salt option and utilizing the #encrypts block
6
+ * Allow a block to be used for dynamically defining encryption options
7
+ * Add :before / :after callbacks for hooking into the encryption process
8
+ * Allow the callback when encryption occurs to be customized [Sergio Salvatore]
9
+ * Define source attribute accessor if different than target and not defined [Sergio Salvatore]
10
+ * Update tests to work with Rails 2.3
11
+
3
12
  == 0.3.0 / 2008-12-14
4
13
 
5
14
  * Remove the PluginAWeek namespace
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2008 Aaron Pfeifer
1
+ Copyright (c) 2006-2009 Aaron Pfeifer
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -25,7 +25,7 @@ Source
25
25
 
26
26
  Encrypting attributes can be repetitive especially when doing so throughout
27
27
  various models and various projects. encrypted_attributes, in association
28
- with the encrypted_strings plugin, helps make encrypting ActiveRecord
28
+ with the encrypted_strings library, helps make encrypting ActiveRecord
29
29
  attributes easier by automating the process.
30
30
 
31
31
  The options that +encrypts+ takes includes all of the encryption options for
@@ -35,46 +35,46 @@ into the +encrypts+ method. Examples of this are show in the Usage section.
35
35
 
36
36
  == Usage
37
37
 
38
- === SHA Encryption
38
+ === Encryption Modes
39
39
 
40
- For SHA encryption, you can either use the default salt, a custom salt, or
41
- generate one based on the attributes of the model.
40
+ SHA, symmetric, and asymmetric encryption modes are supported (default is SHA):
42
41
 
43
- With the default salt:
44
42
  class User < ActiveRecord::Base
45
- encrypts :password
43
+ encrypts :password, :salt => 'secret'
44
+ # encrypts :password, :mode => :symmetric, :password => 'secret'
45
+ # encrypts :password, :mode => :asymmetric, :public_key_file => '/keys/public', :private_key_file => '/keys/private'
46
46
  end
47
47
 
48
- With a custom salt based on the record's values:
49
- class User < ActiveRecord::Base
50
- encrypts :password, :salt => :create_salt
51
-
52
- private
53
- def create_salt
54
- "#{login}_salt"
55
- end
56
- end
48
+ === Dynamic Configuration
57
49
 
58
- The salt and password values are combined and stored in the attribute being
59
- encrypted. Therefore, there's no need to add a second column for storing the
60
- salt value.
61
-
62
- === Symmetric Encryption
50
+ The encryption configuration can be dynamically set like so:
63
51
 
64
52
  class User < ActiveRecord::Base
65
- encrypts :password, :mode => :symmetric, :password => 'secret'
53
+ encrypts :password, :mode => :sha do |user|
54
+ {:salt => "#{user.login}-#{Time.now}", :embed_salt => true}
55
+ end
66
56
  end
67
57
 
68
- === Asymmetric Encryption
58
+ In this case, the salt and password values are combined and stored in the
59
+ attribute being encrypted. Therefore, there's no need to add a second column
60
+ for storing the salt value.
61
+
62
+ To store the dynamic salt in a separate column:
69
63
 
70
64
  class User < ActiveRecord::Base
71
- encrypts :password, :mode => :asymmetric, :public_key_file => '/keys/public', :private_key_file => '/keys/private'
65
+ encrypts :password, :mode => :sha, :before => :create_salt do |user|
66
+ {:salt => user.salt}
67
+ end
68
+
69
+ def create_salt
70
+ self.salt = "#{login}-#{Time.now}"
71
+ end
72
72
  end
73
73
 
74
74
  === Targeted Encryption
75
75
 
76
76
  If you want to store the encrypted value in a different attribute than the
77
- attribute being encrypted, you can do so like so:
77
+ attribute being encrypted:
78
78
 
79
79
  class User < ActiveRecord::Base
80
80
  encrypts :password, :to => :crypted_password
data/Rakefile CHANGED
@@ -5,9 +5,10 @@ require 'rake/contrib/sshpublisher'
5
5
 
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = 'encrypted_attributes'
8
- s.version = '0.3.0'
8
+ s.version = '0.4.0'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'Adds support for automatically encrypting ActiveRecord attributes'
11
+ s.description = s.summary
11
12
 
12
13
  s.files = FileList['{lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
13
14
  s.require_path = 'lib'
@@ -1,47 +1,29 @@
1
1
  module EncryptedAttributes
2
- # Adds support for dynamically generated salts
2
+ # Adds support for embedding salts in the encrypted value
3
3
  class ShaCipher < EncryptedStrings::ShaCipher
4
4
  # Encrypts a string using a Secure Hash Algorithm (SHA), specifically SHA-1.
5
5
  #
6
- # The <tt>:salt</tt> configuration option can be any one of the following types:
7
- # * +symbol+ - Calls the method on the object whose value is being encrypted
8
- # * +proc+ - A block that will be invoked, providing it with the object whose value is being encrypted
9
- # * +string+ - The actual salt value to use
10
- def initialize(object, value, operation, options = {}) #:nodoc:
11
- if operation == :write
12
- # Figure out the actual salt value
13
- if salt = options[:salt]
14
- options[:salt] =
15
- case salt
16
- when Symbol
17
- object.send(salt)
18
- when Proc
19
- salt.call(object)
20
- else
21
- salt
22
- end
23
- end
24
-
25
- # Track whether or not the salt was generated dynamically
26
- @dynamic_salt = salt != options[:salt]
27
-
28
- super(options)
29
- else
30
- # The salt is at the end of the value if it's dynamic
6
+ # Configuration options:
7
+ # * <tt>:salt</tt> - Random bytes used as one of the inputs for generating
8
+ # the encrypted string
9
+ # * <tt>:embed_salt</tt> - Whether to embed the salt directly within the
10
+ # encrypted value. Default is false. This is useful for storing both
11
+ # the salt and the encrypted value in the same attribute.
12
+ def initialize(value, options = {}) #:nodoc:
13
+ if @embed_salt = options.delete(:embed_salt)
14
+ # The salt is at the end of the value
31
15
  salt = value[40..-1]
32
- if @dynamic_salt = !salt.blank?
33
- options[:salt] = salt
34
- end
35
-
36
- super(options)
16
+ options[:salt] = salt unless salt.blank?
37
17
  end
18
+
19
+ super(options)
38
20
  end
39
21
 
40
- # Encrypts the data, appending the salt to the end of the string if it
41
- # was created dynamically
22
+ # Encrypts the data, embedding the salt at the end of the string if
23
+ # configured to do so
42
24
  def encrypt(data)
43
25
  encrypted_data = super
44
- encrypted_data << salt if @dynamic_salt
26
+ encrypted_data << salt if @embed_salt
45
27
  encrypted_data
46
28
  end
47
29
  end
@@ -3,24 +3,37 @@ require 'encrypted_attributes/sha_cipher'
3
3
 
4
4
  module EncryptedAttributes
5
5
  module MacroMethods
6
- # Encrypts the specified attribute.
6
+ # Encrypts the given attribute.
7
7
  #
8
8
  # Configuration options:
9
- # * +mode+ - The mode of encryption to use. Default is sha. See EncryptedStrings for other possible modes.
10
- # * +to+ - The attribute to write the encrypted value to. Default is the same attribute being encrypted.
11
- # * +if+ - Specifies a method, proc or string to call to determine if the encryption should occur. The method, proc or string should return or evaluate to a true or false value.
12
- # * +unless+ - Specifies a method, proc or string to call to determine if the encryption should not occur. The method, proc or string should return or evaluate to a true or false value.
9
+ # * <tt>:mode</tt> - The mode of encryption to use. Default is <tt>:sha</tt>.
10
+ # See EncryptedStrings for other possible modes.
11
+ # * <tt>:to</tt> - The attribute to write the encrypted value to. Default
12
+ # is the same attribute being encrypted.
13
+ # * <tt>:before</tt> - The callback to invoke every time *before* the
14
+ # attribute is encrypted
15
+ # * <tt>:after</tt> - The callback to invoke every time *after* the
16
+ # attribute is encrypted
17
+ # * <tt>:on</tt> - The ActiveRecord callback to use when triggering the
18
+ # encryption. By default, this will encrypt on <tt>before_validation</tt>.
19
+ # See ActiveRecord::Callbacks for a list of possible callbacks.
20
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
21
+ # if the encryption should occur. The method, proc or string should return
22
+ # or evaluate to a true or false value.
23
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
24
+ # determine if the encryption should not occur. The method, proc or string
25
+ # should return or evaluate to a true or false value.
13
26
  #
14
27
  # For additional configuration options used during the actual encryption,
15
28
  # see the individual cipher class for the specified mode.
16
29
  #
17
30
  # == Encryption timeline
18
31
  #
19
- # Attributes are encrypted immediately before a record is validated.
20
- # This means that you can still validate the presence of the encrypted
21
- # attribute, but other things like password length cannot be validated
22
- # without either (a) decrypting the value first or (b) using a different
23
- # encryption target. For example,
32
+ # By default, attributes are encrypted immediately before a record is
33
+ # validated. This means that you can still validate the presence of the
34
+ # encrypted attribute, but other things like password length cannot be
35
+ # validated without either (a) decrypting the value first or (b) using a
36
+ # different encryption target. For example,
24
37
  #
25
38
  # class User < ActiveRecord::Base
26
39
  # encrypts :password, :to => :crypted_password
@@ -50,7 +63,7 @@ module EncryptedAttributes
50
63
  #
51
64
  # class User < ActiveRecord::Base
52
65
  # encrypts :password
53
- # # encrypts :password, :salt => :create_salt
66
+ # # encrypts :password, :salt => 'secret'
54
67
  # end
55
68
  #
56
69
  # Symmetric encryption:
@@ -66,9 +79,34 @@ module EncryptedAttributes
66
79
  # encrypts :password, :mode => :asymmetric
67
80
  # # encrypts :password, :mode => :asymmetric, :public_key_file => '/keys/public', :private_key_file => '/keys/private'
68
81
  # end
69
- def encrypts(attr_name, options = {})
82
+ #
83
+ # == Dynamic configuration
84
+ #
85
+ # For better security, the encryption options (such as the salt value)
86
+ # can be based on values in each individual record. In order to
87
+ # dynamically configure the encryption options so that individual records
88
+ # can be referenced, an optional block can be specified.
89
+ #
90
+ # For example,
91
+ #
92
+ # class User < ActiveRecord::Base
93
+ # encrypts :password, :mode => :sha, :before => :create_salt do |user|
94
+ # {:salt => user.salt}
95
+ # end
96
+ #
97
+ # private
98
+ # def create_salt
99
+ # self.salt = "#{login}-#{Time.now}"
100
+ # end
101
+ # end
102
+ #
103
+ # In the above example, the SHA encryption's <tt>salt</tt> is configured
104
+ # dynamically based on the user's login and the time at which it was
105
+ # encrypted. This helps improve the security of the user's password.
106
+ def encrypts(attr_name, options = {}, &config)
107
+ config ||= options
70
108
  attr_name = attr_name.to_s
71
- to_attr_name = options.delete(:to) || attr_name
109
+ to_attr_name = (options.delete(:to) || attr_name).to_s
72
110
 
73
111
  # Figure out what cipher is being configured for the attribute
74
112
  mode = options.delete(:mode) || :sha
@@ -79,15 +117,27 @@ module EncryptedAttributes
79
117
  cipher_class = EncryptedStrings.const_get(class_name)
80
118
  end
81
119
 
82
- # Set the encrypted value right before validation takes place
83
- before_validation(:if => options.delete(:if), :unless => options.delete(:unless)) do |record|
84
- record.send(:write_encrypted_attribute, attr_name, to_attr_name, cipher_class, options)
120
+ # Define encryption hooks
121
+ define_callbacks("before_encrypt_#{attr_name}", "after_encrypt_#{attr_name}")
122
+ send("before_encrypt_#{attr_name}", options.delete(:before)) if options.include?(:before)
123
+ send("after_encrypt_#{attr_name}", options.delete(:after)) if options.include?(:after)
124
+
125
+ # Set the encrypted value on the configured callback
126
+ callback = options.delete(:on) || :before_validation
127
+ send(callback, :if => options.delete(:if), :unless => options.delete(:unless)) do |record|
128
+ record.send(:write_encrypted_attribute, attr_name, to_attr_name, cipher_class, config)
85
129
  true
86
130
  end
87
131
 
132
+ # Define virtual source attribute
133
+ if attr_name != to_attr_name && !column_names.include?(attr_name)
134
+ attr_reader attr_name unless method_defined?(attr_name)
135
+ attr_writer attr_name unless method_defined?("#{attr_name}=")
136
+ end
137
+
88
138
  # Define the reader when reading the encrypted attribute from the database
89
139
  define_method(to_attr_name) do
90
- read_encrypted_attribute(to_attr_name, cipher_class, options)
140
+ read_encrypted_attribute(to_attr_name, cipher_class, config)
91
141
  end
92
142
 
93
143
  unless included_modules.include?(EncryptedAttributes::InstanceMethods)
@@ -106,8 +156,10 @@ module EncryptedAttributes
106
156
  # Only encrypt values that actually have content and have not already
107
157
  # been encrypted
108
158
  unless value.blank? || value.encrypted?
159
+ callback("before_encrypt_#{attr_name}")
160
+
109
161
  # Create the cipher configured for this attribute
110
- cipher = create_cipher(cipher_class, options, :write, value)
162
+ cipher = create_cipher(cipher_class, options, value)
111
163
 
112
164
  # Encrypt the value
113
165
  value = cipher.encrypt(value)
@@ -115,6 +167,8 @@ module EncryptedAttributes
115
167
 
116
168
  # Update the value based on the target attribute
117
169
  send("#{to_attr_name}=", value)
170
+
171
+ callback("after_encrypt_#{attr_name}")
118
172
  end
119
173
  end
120
174
 
@@ -132,21 +186,18 @@ module EncryptedAttributes
132
186
  # don't want to encrypt when a new value has been set)
133
187
  unless value.blank? || value.encrypted? || attribute_changed?(to_attr_name)
134
188
  # Create the cipher configured for this attribute
135
- value.cipher = create_cipher(cipher_class, options, :read, value)
189
+ value.cipher = create_cipher(cipher_class, options, value)
136
190
  end
137
191
 
138
192
  value
139
193
  end
140
194
 
141
- # Creates a new cipher with the given configuration options. The
142
- # operator defines the context in which the cipher will be used.
143
- def create_cipher(klass, options, operator, value)
144
- if klass.parent == EncryptedAttributes
145
- # Only use the contextual information for ciphers defined in this plugin
146
- klass.new(self, value, operator, options.dup)
147
- else
148
- klass.new(options.dup)
149
- end
195
+ # Creates a new cipher with the given configuration options
196
+ def create_cipher(klass, options, value)
197
+ options = options.is_a?(Proc) ? options.call(self) : options.dup
198
+
199
+ # Only use the contextual information for this plugin's ciphers
200
+ klass.parent == EncryptedAttributes ? klass.new(value, options) : klass.new(options)
150
201
  end
151
202
  end
152
203
  end
@@ -5,4 +5,5 @@ Rails::Initializer.run do |config|
5
5
  config.plugins = %w(encrypted_strings encrypted_attributes)
6
6
  config.cache_classes = false
7
7
  config.whiny_nils = true
8
+ config.action_controller.session = {:key => 'rails_session', :secret => 'd229e4d22437432705ab3985d4d246'}
8
9
  end
@@ -1,9 +1,7 @@
1
1
  class CreateUsers < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :users do |t|
4
- t.string :login
5
- t.string :password
6
- t.string :crypted_password
4
+ t.string :login, :password, :crypted_password, :salt
7
5
  end
8
6
  end
9
7
 
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../test_helper'
2
2
 
3
- class EncryptedAttributesTest < Test::Unit::TestCase
3
+ class EncryptedAttributesTest < ActiveSupport::TestCase
4
4
  def setup
5
5
  User.encrypts :password
6
6
  end
@@ -61,7 +61,7 @@ class EncryptedAttributesTest < Test::Unit::TestCase
61
61
  end
62
62
  end
63
63
 
64
- class EncryptedAttributesWithDifferentTargetTest < Test::Unit::TestCase
64
+ class EncryptedAttributesWithDifferentTargetTest < ActiveSupport::TestCase
65
65
  def setup
66
66
  User.encrypts :password, :to => :crypted_password
67
67
  end
@@ -113,27 +113,115 @@ class EncryptedAttributesWithDifferentTargetTest < Test::Unit::TestCase
113
113
  end
114
114
  end
115
115
 
116
- class EncryptedAttributesWithConditionalsTest < Test::Unit::TestCase
116
+ class EncryptedAttributesWithVirtualAttributeSourceTest < ActiveSupport::TestCase
117
+ def setup
118
+ User.encrypts :raw_password, :to => :crypted_password
119
+ end
120
+
121
+ def test_should_define_source_reader
122
+ assert User.method_defined?(:raw_password)
123
+ end
124
+
125
+ def test_should_define_source_writer
126
+ assert User.method_defined?(:raw_password=)
127
+ end
128
+
129
+ def test_should_encrypt_from_virtual_attribute
130
+ user = create_user(:login => 'admin', :raw_password => 'secret')
131
+ assert user.crypted_password.encrypted?
132
+ assert_equal '8152bc582f58c854f580cb101d3182813dec4afe', "#{user.crypted_password}"
133
+ end
134
+
135
+ def teardown
136
+ User.class_eval do
137
+ @before_validation_callbacks = nil
138
+
139
+ remove_method(:raw_password)
140
+ remove_method(:raw_password=)
141
+ end
142
+ end
143
+ end
144
+
145
+ class EncryptedAttributesWithConflictingVirtualAttributeSourceTest < ActiveSupport::TestCase
146
+ def setup
147
+ User.class_eval do
148
+ def raw_password
149
+ 'raw_password'
150
+ end
151
+
152
+ def raw_password=(value)
153
+ self.password = value
154
+ end
155
+ end
156
+
157
+ User.encrypts :raw_password, :to => :crypted_password
158
+ @user = User.new
159
+ end
160
+
161
+ def test_should_not_define_source_reader
162
+ assert_equal 'raw_password', @user.raw_password
163
+ end
164
+
165
+ def test_should_not_define_source_writer
166
+ @user.raw_password = 'raw_password'
167
+ assert_equal 'raw_password', @user.password
168
+ end
169
+
170
+ def teardown
171
+ User.class_eval do
172
+ @before_validation_callbacks = nil
173
+
174
+ remove_method(:raw_password)
175
+ remove_method(:raw_password=)
176
+ end
177
+ end
178
+ end
179
+
180
+ class EncryptedAttributesWithCustomCallbackTest < ActiveSupport::TestCase
181
+ def setup
182
+ User.encrypts :password, :on => :before_save
183
+ end
184
+
185
+ def test_should_not_encrypt_on_validation
186
+ user = new_user(:login => 'admin', :password => 'secret')
187
+ user.valid?
188
+ assert_equal 'secret', user.password
189
+ end
190
+
191
+ def test_should_encrypt_on_create
192
+ user = new_user(:login => 'admin', :password => 'secret')
193
+ user.save
194
+ assert_equal '8152bc582f58c854f580cb101d3182813dec4afe', "#{user.password}"
195
+ end
196
+
197
+ def teardown
198
+ User.class_eval do
199
+ @before_save_callbacks = nil
200
+ end
201
+ end
202
+ end
203
+
204
+ class EncryptedAttributesWithConditionalsTest < ActiveSupport::TestCase
117
205
  def test_should_not_encrypt_if_if_conditional_is_false
118
- User.encrypts :password, :if => lambda {false}
206
+ User.encrypts :password, :if => lambda {|user| false}
119
207
  user = create_user(:login => 'admin', :password => 'secret')
120
208
  assert_equal 'secret', user.password
121
209
  end
122
210
 
123
211
  def test_should_encrypt_if_if_conditional_is_true
124
- User.encrypts :password, :if => lambda {true}
212
+ User.encrypts :password, :if => lambda {|user| true}
125
213
  user = create_user(:login => 'admin', :password => 'secret')
126
214
  assert_equal '8152bc582f58c854f580cb101d3182813dec4afe', "#{user.password}"
127
215
  end
128
216
 
129
217
  def test_should_not_encrypt_if_unless_conditional_is_true
130
- User.encrypts :password, :unless => lambda {true}
218
+ User.encrypts :password, :unless => lambda {|user| true}
131
219
  user = create_user(:login => 'admin', :password => 'secret')
132
220
  assert_equal 'secret', user.password
133
221
  end
134
222
 
135
223
  def test_should_encrypt_if_unless_conditional_is_false
136
- User.encrypts :password, :unless => lambda {false}
224
+ User.encrypts :password, :unless => lambda {|user| false}
137
225
  user = create_user(:login => 'admin', :password => 'secret')
138
226
  assert_equal '8152bc582f58c854f580cb101d3182813dec4afe', "#{user.password}"
139
227
  end
@@ -145,7 +233,88 @@ class EncryptedAttributesWithConditionalsTest < Test::Unit::TestCase
145
233
  end
146
234
  end
147
235
 
148
- class ShaEncryptionTest < Test::Unit::TestCase
236
+ class EncryptedAttributesWithBeforeCallbacksTest < ActiveSupport::TestCase
237
+ def setup
238
+ @password = nil
239
+ @ran_callback = false
240
+ User.encrypts :password, :before => lambda {|user| @ran_callback = true; @password = user.password.to_s}
241
+
242
+ create_user(:login => 'admin', :password => 'secret')
243
+ end
244
+
245
+ def test_should_run_callback
246
+ assert @ran_callback
247
+ end
248
+
249
+ def test_should_not_have_encrypted_yet
250
+ assert_equal 'secret', @password
251
+ end
252
+
253
+ def teardown
254
+ User.class_eval do
255
+ @before_validation_callbacks = nil
256
+ @before_encrypt_password_callbacks = nil
257
+ end
258
+ end
259
+ end
260
+
261
+ class EncryptedAttributesWithAfterCallbacksTest < ActiveSupport::TestCase
262
+ def setup
263
+ @password = nil
264
+ @ran_callback = false
265
+ User.encrypts :password, :after => lambda {|user| @ran_callback = true; @password = user.password.to_s}
266
+
267
+ create_user(:login => 'admin', :password => 'secret')
268
+ end
269
+
270
+ def test_should_run_callback
271
+ assert @ran_callback
272
+ end
273
+
274
+ def test_should_have_encrypted_already
275
+ assert_equal '8152bc582f58c854f580cb101d3182813dec4afe', @password
276
+ end
277
+
278
+ def teardown
279
+ User.class_eval do
280
+ @before_validation_callbacks = nil
281
+ @after_encrypt_password_callbacks = nil
282
+ end
283
+ end
284
+ end
285
+
286
+ class EncryptedAttributesWithDynamicConfigurationTest < ActiveSupport::TestCase
287
+ def setup
288
+ @salt = nil
289
+ User.encrypts :password, :before => lambda {|user| user.salt = user.login} do |user|
290
+ {:salt => @salt = user.salt}
291
+ end
292
+
293
+ @user = create_user(:login => 'admin', :password => 'secret')
294
+ end
295
+
296
+ def test_should_use_dynamic_configuration_during_write
297
+ assert_equal 'a55d037f385cad22efe7862e07b805938d150154', "#{@user[:password]}"
298
+ end
299
+
300
+ def test_should_use_dynamic_configuration_during_read
301
+ user = User.find(@user.id)
302
+ assert_equal 'a55d037f385cad22efe7862e07b805938d150154', "#{user.password}"
303
+ end
304
+
305
+ def test_should_build_configuration_after_before_callbacks_invoked
306
+ assert_equal 'admin', @salt
307
+ end
308
+
309
+ def teardown
310
+ User.class_eval do
311
+ @before_validation_callbacks = nil
312
+ @before_encrypt_password_callbacks = nil
313
+ end
314
+ end
315
+ end
316
+
317
+ class ShaEncryptionTest < ActiveSupport::TestCase
149
318
  def setup
150
319
  User.encrypts :password, :mode => :sha
151
320
  @user = create_user(:login => 'admin', :password => 'secret')
@@ -168,7 +337,7 @@ class ShaEncryptionTest < Test::Unit::TestCase
168
337
  end
169
338
 
170
339
  def test_should_be_able_to_check_password
171
- assert_equal 'secret', @user.password
340
+ assert_equal 'secret', @user. password
172
341
  end
173
342
 
174
343
  def teardown
@@ -178,9 +347,9 @@ class ShaEncryptionTest < Test::Unit::TestCase
178
347
  end
179
348
  end
180
349
 
181
- class ShaWithCustomSaltEncryptionTest < Test::Unit::TestCase
350
+ class ShaWithEmbeddedSaltEncryptionTest < ActiveSupport::TestCase
182
351
  def setup
183
- User.encrypts :password, :mode => :sha, :salt => :login
352
+ User.encrypts :password, :mode => :sha, :salt => 'admin', :embed_salt => true
184
353
  @user = create_user(:login => 'admin', :password => 'secret')
185
354
  end
186
355
 
@@ -211,7 +380,7 @@ class ShaWithCustomSaltEncryptionTest < Test::Unit::TestCase
211
380
  end
212
381
  end
213
382
 
214
- class SymmetricEncryptionTest < Test::Unit::TestCase
383
+ class SymmetricEncryptionTest < ActiveSupport::TestCase
215
384
  def setup
216
385
  User.encrypts :password, :mode => :symmetric, :password => 'key'
217
386
  @user = create_user(:login => 'admin', :password => 'secret')
@@ -244,7 +413,7 @@ class SymmetricEncryptionTest < Test::Unit::TestCase
244
413
  end
245
414
  end
246
415
 
247
- class AsymmetricEncryptionTest < Test::Unit::TestCase
416
+ class AsymmetricEncryptionTest < ActiveSupport::TestCase
248
417
  def setup
249
418
  User.encrypts :password, :mode => :asymmetric,
250
419
  :private_key_file => File.dirname(__FILE__) + '/../keys/private',
@@ -1,56 +1,43 @@
1
1
  require File.dirname(__FILE__) + '/../test_helper'
2
2
 
3
- class ShaCipherOnWriteTest < Test::Unit::TestCase
3
+ class ShaCipherWithoutEmbeddingTest < Test::Unit::TestCase
4
4
  def setup
5
- @user = create_user(:login => 'admin')
5
+ @cipher = EncryptedAttributes::ShaCipher.new('dc0fc7c07bba982a8d8f18fe138dbea912df5e0e', :salt => 'custom_salt')
6
6
  end
7
7
 
8
- def test_should_allow_symbolic_salt
9
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => :login)
10
- assert_equal 'admin', cipher.salt
11
- end
12
-
13
- def test_should_allow_stringified_salt
14
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => 'custom_salt')
15
- assert_equal 'custom_salt', cipher.salt
8
+ def test_should_use_configured_salt
9
+ assert_equal 'custom_salt', @cipher.salt
16
10
  end
17
11
 
18
- def test_should_allow_block_salt
19
- dynamic_salt = lambda {|user| user.login}
20
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => dynamic_salt)
21
- assert_equal 'admin', cipher.salt
12
+ def test_should_not_embed_salt_in_encrypted_string
13
+ assert_equal 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0e', @cipher.encrypt('secret')
22
14
  end
23
-
24
- def test_should_allow_dynamic_nil_salt
25
- dynamic_salt = lambda {|user| nil}
26
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => dynamic_salt)
27
- assert_equal '', cipher.salt
15
+ end
16
+
17
+ class ShaCipherWithNoSaltEmbeddedTest < Test::Unit::TestCase
18
+ def setup
19
+ @cipher = EncryptedAttributes::ShaCipher.new('dc0fc7c07bba982a8d8f18fe138dbea912df5e0e', :embed_salt => true, :salt => 'custom_salt')
28
20
  end
29
21
 
30
- def test_should_append_salt_to_encrypted_value_if_dynamic
31
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => :login)
32
- assert_equal 'a55d037f385cad22efe7862e07b805938d150154admin', cipher.encrypt('secret')
22
+ def test_should_use_configured_salt
23
+ assert_equal 'custom_salt', @cipher.salt
33
24
  end
34
25
 
35
- def test_should_not_append_salt_to_encrypted_value_if_static
36
- cipher = EncryptedAttributes::ShaCipher.new(@user, 'password', :write, :salt => 'custom_salt')
37
- assert_equal 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0e', cipher.encrypt('secret')
26
+ def test_should_embed_salt_in_encrypted_string
27
+ assert_equal 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0ecustom_salt', @cipher.encrypt('secret')
38
28
  end
39
29
  end
40
30
 
41
- class ShaCipherOnReadTest < Test::Unit::TestCase
31
+ class ShaCipherWithSaltEmbeddedTest < Test::Unit::TestCase
42
32
  def setup
43
- @user = create_user(:login => 'admin')
44
- @cipher = EncryptedAttributes::ShaCipher.new(@user, 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0ecustom_salt', :read)
33
+ @cipher = EncryptedAttributes::ShaCipher.new('dc0fc7c07bba982a8d8f18fe138dbea912df5e0ecustom_salt', :embed_salt => true, :salt => 'ignored_salt')
45
34
  end
46
35
 
47
- def test_should_should_use_remaining_characters_after_password_for_salt
36
+ def test_should_use_remaining_characters_after_password_for_salt
48
37
  assert_equal 'custom_salt', @cipher.salt
49
38
  end
50
39
 
51
- def test_should_be_able_to_perform_equality_on_encrypted_strings
52
- password = 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0ecustom_salt'
53
- password.cipher = @cipher
54
- assert_equal 'secret', password
40
+ def test_should_embed_salt_in_encrypted_string
41
+ assert_equal 'dc0fc7c07bba982a8d8f18fe138dbea912df5e0ecustom_salt', @cipher.encrypt('secret')
55
42
  end
56
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: encrypted_attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Pfeifer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-14 00:00:00 -05:00
12
+ date: 2009-05-02 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,7 +22,7 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.3.0
24
24
  version:
25
- description:
25
+ description: Adds support for automatically encrypting ActiveRecord attributes
26
26
  email: aaron@pluginaweek.org
27
27
  executables: []
28
28
 
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
78
  requirements: []
79
79
 
80
80
  rubyforge_project: pluginaweek
81
- rubygems_version: 1.2.0
81
+ rubygems_version: 1.3.1
82
82
  signing_key:
83
83
  specification_version: 2
84
84
  summary: Adds support for automatically encrypting ActiveRecord attributes