encrypted_attributes 0.3.0 → 0.4.0

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