authlogic 2.0.0 → 2.1.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.
Files changed (82) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.rdoc +107 -2
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.rdoc +136 -159
  5. data/Rakefile +47 -18
  6. data/VERSION.yml +4 -0
  7. data/authlogic.gemspec +205 -0
  8. data/init.rb +1 -1
  9. data/lib/authlogic/acts_as_authentic/base.rb +33 -12
  10. data/lib/authlogic/acts_as_authentic/email.rb +47 -14
  11. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +20 -14
  12. data/lib/authlogic/acts_as_authentic/login.rb +84 -8
  13. data/lib/authlogic/acts_as_authentic/password.rb +216 -87
  14. data/lib/authlogic/acts_as_authentic/perishable_token.rb +9 -4
  15. data/lib/authlogic/acts_as_authentic/persistence_token.rb +4 -2
  16. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +5 -4
  17. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +17 -5
  18. data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -3
  19. data/lib/authlogic/acts_as_authentic/validations_scope.rb +2 -2
  20. data/lib/authlogic/controller_adapters/abstract_adapter.rb +13 -1
  21. data/lib/authlogic/controller_adapters/rails_adapter.rb +1 -1
  22. data/lib/authlogic/crypto_providers/aes256.rb +2 -2
  23. data/lib/authlogic/crypto_providers/bcrypt.rb +3 -1
  24. data/lib/authlogic/crypto_providers/sha1.rb +2 -2
  25. data/lib/authlogic/i18n.rb +10 -1
  26. data/lib/authlogic/regex.rb +25 -0
  27. data/lib/authlogic/session/activation.rb +2 -0
  28. data/lib/authlogic/session/active_record_trickery.rb +19 -3
  29. data/lib/authlogic/session/brute_force_protection.rb +26 -11
  30. data/lib/authlogic/session/callbacks.rb +16 -6
  31. data/lib/authlogic/session/cookies.rb +15 -11
  32. data/lib/authlogic/session/existence.rb +4 -2
  33. data/lib/authlogic/session/foundation.rb +1 -1
  34. data/lib/authlogic/session/http_auth.rb +46 -11
  35. data/lib/authlogic/session/magic_columns.rb +25 -6
  36. data/lib/authlogic/session/magic_states.rb +4 -4
  37. data/lib/authlogic/session/params.rb +14 -9
  38. data/lib/authlogic/session/password.rb +86 -24
  39. data/lib/authlogic/session/priority_record.rb +1 -1
  40. data/lib/authlogic/session/session.rb +2 -2
  41. data/lib/authlogic/session/timeout.rb +1 -1
  42. data/lib/authlogic/session/validation.rb +9 -5
  43. data/lib/authlogic/test_case/mock_controller.rb +45 -0
  44. data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -0
  45. data/lib/authlogic/test_case/mock_logger.rb +10 -0
  46. data/lib/authlogic/test_case/mock_request.rb +19 -0
  47. data/lib/authlogic/test_case/rails_request_adapter.rb +30 -0
  48. data/lib/authlogic/test_case.rb +114 -0
  49. data/lib/authlogic.rb +1 -1
  50. data/rails/init.rb +1 -0
  51. data/shoulda_macros/authlogic.rb +2 -1
  52. data/test/acts_as_authentic_test/base_test.rb +6 -0
  53. data/test/acts_as_authentic_test/email_test.rb +25 -7
  54. data/test/acts_as_authentic_test/login_test.rb +37 -7
  55. data/test/acts_as_authentic_test/magic_columns_test.rb +4 -4
  56. data/test/acts_as_authentic_test/password_test.rb +64 -40
  57. data/test/acts_as_authentic_test/perishable_token_test.rb +36 -2
  58. data/test/acts_as_authentic_test/restful_authentication_test.rb +40 -0
  59. data/test/acts_as_authentic_test/session_maintenance_test.rb +34 -18
  60. data/test/acts_as_authentic_test/single_access_test.rb +6 -1
  61. data/test/libs/affiliate.rb +7 -0
  62. data/test/libs/ldaper.rb +3 -0
  63. data/test/random_test.rb +2 -2
  64. data/test/session_test/activation_test.rb +1 -1
  65. data/test/session_test/active_record_trickery_test.rb +2 -1
  66. data/test/session_test/brute_force_protection_test.rb +34 -9
  67. data/test/session_test/cookies_test.rb +3 -3
  68. data/test/session_test/http_auth_test.rb +20 -8
  69. data/test/session_test/magic_columns_test.rb +7 -4
  70. data/test/session_test/magic_states_test.rb +3 -3
  71. data/test/session_test/params_test.rb +4 -4
  72. data/test/session_test/password_test.rb +8 -0
  73. data/test/session_test/session_test.rb +9 -9
  74. data/test/session_test/timeout_test.rb +10 -1
  75. data/test/test_helper.rb +46 -24
  76. metadata +38 -27
  77. data/Manifest.txt +0 -111
  78. data/lib/authlogic/testing/test_unit_helpers.rb +0 -39
  79. data/lib/authlogic/version.rb +0 -56
  80. data/test/libs/mock_controller.rb +0 -35
  81. data/test/libs/mock_cookie_jar.rb +0 -10
  82. data/test/libs/mock_request.rb +0 -5
@@ -18,7 +18,7 @@ module Authlogic
18
18
  # * <tt>Default:</tt> :crypted_password, :encrypted_password, :password_hash, or :pw_hash
19
19
  # * <tt>Accepts:</tt> Symbol
20
20
  def crypted_password_field(value = nil)
21
- config(:crypted_password_field, value, first_column_to_exist(:crypted_password, :encrypted_password, :password_hash, :pw_hash))
21
+ rw_config(:crypted_password_field, value, first_column_to_exist(nil, :crypted_password, :encrypted_password, :password_hash, :pw_hash))
22
22
  end
23
23
  alias_method :crypted_password_field=, :crypted_password_field
24
24
 
@@ -27,44 +27,129 @@ module Authlogic
27
27
  # * <tt>Default:</tt> :password_salt, :pw_salt, :salt, nil if none exist
28
28
  # * <tt>Accepts:</tt> Symbol
29
29
  def password_salt_field(value = nil)
30
- config(:password_salt_field, value, first_column_to_exist(nil, :password_salt, :pw_salt, :salt))
30
+ rw_config(:password_salt_field, value, first_column_to_exist(nil, :password_salt, :pw_salt, :salt))
31
31
  end
32
32
  alias_method :password_salt_field=, :password_salt_field
33
33
 
34
+ # Whether or not to require a password confirmation. If you don't want your users to confirm their password
35
+ # just set this to false.
36
+ #
37
+ # * <tt>Default:</tt> true
38
+ # * <tt>Accepts:</tt> Boolean
39
+ def require_password_confirmation(value = nil)
40
+ rw_config(:require_password_confirmation, value, true)
41
+ end
42
+ alias_method :require_password_confirmation=, :require_password_confirmation
43
+
44
+ # By default passwords are required when a record is new or the crypted_password is blank, but if both of these things
45
+ # are met a password is not required. In this case, blank passwords are ignored.
46
+ #
47
+ # Think about a profile page, where the user can edit all of their information, including changing their password.
48
+ # If they do not want to change their password they just leave the fields blank. This will try to set the password to
49
+ # a blank value, in which case is incorrect behavior. As such, Authlogic ignores this. But let's say you have a completely
50
+ # separate page for resetting passwords, you might not want to ignore blank passwords. If this is the case for you, then
51
+ # just set this value to false.
52
+ #
53
+ # * <tt>Default:</tt> true
54
+ # * <tt>Accepts:</tt> Boolean
55
+ def ignore_blank_passwords(value = nil)
56
+ rw_config(:ignore_blank_passwords, value, true)
57
+ end
58
+ alias_method :ignore_blank_passwords=, :ignore_blank_passwords
59
+
60
+ # When calling valid_password?("some pass") do you want to check that password against what's in that object or whats in
61
+ # the datbase. Take this example:
62
+ #
63
+ # u = User.first
64
+ # u.password = "new pass"
65
+ # u.valid_password?("old pass")
66
+ #
67
+ # Should the last line above return true or false? The record hasn't been saved yet, so most would assume yes.
68
+ # Other would assume no. So I let you decide by giving you this option.
69
+ #
70
+ # * <tt>Default:</tt> true
71
+ # * <tt>Accepts:</tt> Boolean
72
+ def check_passwords_against_database(value = nil)
73
+ rw_config(:check_passwords_against_database, value, true)
74
+ end
75
+ alias_method :check_passwords_against_database=, :check_passwords_against_database
76
+
34
77
  # Whether or not to validate the password field.
35
78
  #
36
79
  # * <tt>Default:</tt> true
37
80
  # * <tt>Accepts:</tt> Boolean
38
81
  def validate_password_field(value = nil)
39
- config(:validate_password_field, value, true)
82
+ rw_config(:validate_password_field, value, true)
40
83
  end
41
84
  alias_method :validate_password_field=, :validate_password_field
42
85
 
86
+ # A hash of options for the validates_length_of call for the password field. Allows you to change this however you want.
87
+ #
88
+ # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
89
+ # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
90
+ # options.</b>
91
+ #
92
+ # * <tt>Default:</tt> {:minimum => 4, :if => :require_password?}
93
+ # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
94
+ def validates_length_of_password_field_options(value = nil)
95
+ rw_config(:validates_length_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
96
+ end
97
+ alias_method :validates_length_of_password_field_options=, :validates_length_of_password_field_options
98
+
99
+ # A convenience function to merge options into the validates_length_of_login_field_options. So intead of:
100
+ #
101
+ # self.validates_length_of_password_field_options = validates_length_of_password_field_options.merge(:my_option => my_value)
102
+ #
103
+ # You can do this:
104
+ #
105
+ # merge_validates_length_of_password_field_options :my_option => my_value
106
+ def merge_validates_length_of_password_field_options(options = {})
107
+ self.validates_length_of_password_field_options = validates_length_of_password_field_options.merge(options)
108
+ end
109
+
43
110
  # A hash of options for the validates_confirmation_of call for the password field. Allows you to change this however you want.
44
111
  #
45
- # * <tt>Default:</tt> {:minimum => 4, :if => "#{password_salt_field}_changed?".to_sym}
112
+ # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
113
+ # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
114
+ # options.</b>
115
+ #
116
+ # * <tt>Default:</tt> {:if => :require_password?}
46
117
  # * <tt>Accepts:</tt> Hash of options accepted by validates_confirmation_of
47
118
  def validates_confirmation_of_password_field_options(value = nil)
48
- config(:validates_confirmation_of_password_field_options, value, {:minimum => 4, :if => (password_salt_field ? "#{password_salt_field}_changed?".to_sym : nil)})
119
+ rw_config(:validates_confirmation_of_password_field_options, value, {:if => :require_password?})
49
120
  end
50
121
  alias_method :validates_confirmation_of_password_field_options=, :validates_confirmation_of_password_field_options
51
122
 
123
+ # See merge_validates_length_of_password_field_options. The same thing, except for validates_confirmation_of_password_field_options
124
+ def merge_validates_confirmation_of_password_field_options(options = {})
125
+ self.validates_confirmation_of_password_field_options = validates_confirmation_of_password_field_options.merge(options)
126
+ end
127
+
52
128
  # A hash of options for the validates_length_of call for the password_confirmation field. Allows you to change this however you want.
53
129
  #
54
- # * <tt>Default:</tt> {:minimum => 4, :if => :require_password_confirmation?}
130
+ # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
131
+ # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
132
+ # options.</b>
133
+ #
134
+ # * <tt>Default:</tt> validates_length_of_password_field_options
55
135
  # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
56
136
  def validates_length_of_password_confirmation_field_options(value = nil)
57
- config(:validates_length_of_password_confirmation_field_options, value, {:minimum => 4, :if => :require_password_confirmation?})
137
+ rw_config(:validates_length_of_password_confirmation_field_options, value, validates_length_of_password_field_options)
58
138
  end
59
139
  alias_method :validates_length_of_password_confirmation_field_options=, :validates_length_of_password_confirmation_field_options
60
140
 
141
+ # See merge_validates_length_of_password_field_options. The same thing, except for validates_length_of_password_confirmation_field_options
142
+ def merge_validates_length_of_password_confirmation_field_options(options = {})
143
+ self.validates_length_of_password_confirmation_field_options = validates_length_of_password_confirmation_field_options.merge(options)
144
+ end
145
+
61
146
  # The class you want to use to encrypt and verify your encrypted passwords. See the Authlogic::CryptoProviders module for more info
62
147
  # on the available methods and how to create your own.
63
148
  #
64
149
  # * <tt>Default:</tt> CryptoProviders::Sha512
65
150
  # * <tt>Accepts:</tt> Class
66
151
  def crypto_provider(value = nil)
67
- config(:crypto_provider, value, CryptoProviders::Sha512)
152
+ rw_config(:crypto_provider, value, CryptoProviders::Sha512)
68
153
  end
69
154
  alias_method :crypto_provider=, :crypto_provider
70
155
 
@@ -80,7 +165,7 @@ module Authlogic
80
165
  # * <tt>Default:</tt> nil
81
166
  # * <tt>Accepts:</tt> Class or Array
82
167
  def transition_from_crypto_providers(value = nil)
83
- config(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
168
+ rw_config(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
84
169
  end
85
170
  alias_method :transition_from_crypto_providers=, :transition_from_crypto_providers
86
171
  end
@@ -93,6 +178,7 @@ module Authlogic
93
178
  ]
94
179
 
95
180
  def self.included(klass)
181
+ return if klass.crypted_password_field.nil?
96
182
  klass.define_callbacks *METHODS
97
183
  end
98
184
 
@@ -109,106 +195,149 @@ module Authlogic
109
195
  # The methods related to the password field.
110
196
  module Methods
111
197
  def self.included(klass)
198
+ return if klass.crypted_password_field.nil?
199
+
112
200
  klass.class_eval do
201
+ include InstanceMethods
202
+
113
203
  if validate_password_field
114
- validates_confirmation_of :password, validates_confirmation_of_password_field_options
115
- validates_length_of :password_confirmation, validates_length_of_password_confirmation_field_options
204
+ validates_length_of :password, validates_length_of_password_field_options
205
+
206
+ if require_password_confirmation
207
+ validates_confirmation_of :password, validates_confirmation_of_password_field_options
208
+ validates_length_of :password_confirmation, validates_length_of_password_confirmation_field_options
209
+ end
116
210
  end
211
+
212
+ after_save :reset_password_changed
117
213
  end
118
214
  end
119
215
 
120
- # The password
121
- def password
122
- @password
123
- end
216
+ module InstanceMethods
217
+ # The password
218
+ def password
219
+ @password
220
+ end
124
221
 
125
- # This is a virtual method. Once a password is passed to it, it will create new password salt as well as encrypt
126
- # the password.
127
- def password=(pass)
128
- return if pass.blank?
129
- before_password_set
130
- @password = pass
131
- send("#{password_salt_field}=", Authlogic::Random.friendly_token) if password_salt_field
132
- send("#{crypted_password_field}=", crypto_provider.encrypt(*encrypt_arguments(@password, act_like_restful_authentication? ? :restful_authentication : nil)))
133
- after_password_set
134
- end
222
+ # This is a virtual method. Once a password is passed to it, it will create new password salt as well as encrypt
223
+ # the password.
224
+ def password=(pass)
225
+ return if ignore_blank_passwords? && pass.blank?
226
+ before_password_set
227
+ @password = pass
228
+ send("#{password_salt_field}=", Authlogic::Random.friendly_token) if password_salt_field
229
+ send("#{crypted_password_field}=", crypto_provider.encrypt(*encrypt_arguments(@password, false, act_like_restful_authentication? ? :restful_authentication : nil)))
230
+ @password_changed = true
231
+ after_password_set
232
+ end
135
233
 
136
- # Accepts a raw password to determine if it is the correct password or not.
137
- def valid_password?(attempted_password)
138
- return false if attempted_password.blank? || send(crypted_password_field).blank?
139
-
140
- before_password_verification
234
+ # Accepts a raw password to determine if it is the correct password or not. Notice the second argument. That defaults to the value of
235
+ # check_passwords_against_database. See that method for mor information, but basically it just tells Authlogic to check the password
236
+ # against the value in the database or the value in the object.
237
+ def valid_password?(attempted_password, check_against_database = check_passwords_against_database?)
238
+ crypted = check_against_database && send("#{crypted_password_field}_changed?") ? send("#{crypted_password_field}_was") : send(crypted_password_field)
239
+ return false if attempted_password.blank? || crypted.blank?
240
+ before_password_verification
141
241
 
142
- crypto_providers = [crypto_provider] + transition_from_crypto_providers
143
- crypto_providers.each_with_index do |encryptor, index|
144
- # The arguments_type of for the transitioning from restful_authentication
145
- arguments_type = (act_like_restful_authentication? && index == 0) ||
146
- (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
147
- :restful_authentication : nil
242
+ crypto_providers.each_with_index do |encryptor, index|
243
+ # The arguments_type of for the transitioning from restful_authentication
244
+ arguments_type = (act_like_restful_authentication? && index == 0) ||
245
+ (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
246
+ :restful_authentication : nil
148
247
 
149
- if encryptor.matches?(send(crypted_password_field), *encrypt_arguments(attempted_password, arguments_type))
150
- # If we are transitioning from an older encryption algorithm and the password is still using the old algorithm
151
- # then let's reset the password using the new algorithm. If the algorithm has a cost (BCrypt) and the cost has changed, update the password with
152
- # the new cost.
153
- if index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))
154
- self.password = attempted_password
155
- save(false)
248
+ if encryptor.matches?(crypted, *encrypt_arguments(attempted_password, check_against_database, arguments_type))
249
+ transition_password(attempted_password) if transition_password?(index, encryptor, crypted, check_against_database)
250
+ after_password_verification
251
+ return true
156
252
  end
157
-
158
- after_password_verification
159
-
160
- return true
161
253
  end
162
- end
163
254
 
164
- false
165
- end
255
+ false
256
+ end
166
257
 
167
- # Resets the password to a random friendly token.
168
- def reset_password
169
- friendly_token = Authlogic::Random.friendly_token
170
- self.password = friendly_token
171
- self.password_confirmation = friendly_token
172
- end
173
- alias_method :randomize_password, :reset_password
258
+ # Resets the password to a random friendly token.
259
+ def reset_password
260
+ friendly_token = Authlogic::Random.friendly_token
261
+ self.password = friendly_token
262
+ self.password_confirmation = friendly_token
263
+ end
264
+ alias_method :randomize_password, :reset_password
174
265
 
175
- # Resets the password to a random friendly token and then saves the record.
176
- def reset_password!
177
- reset_password
178
- save_without_session_maintenance(false)
179
- end
180
- alias_method :randomize_password!, :reset_password!
266
+ # Resets the password to a random friendly token and then saves the record.
267
+ def reset_password!
268
+ reset_password
269
+ save_without_session_maintenance(false)
270
+ end
271
+ alias_method :randomize_password!, :reset_password!
181
272
 
182
- private
183
- def encrypt_arguments(raw_password, arguments_type = nil)
184
- salt = password_salt_field ? send(password_salt_field) : nil
185
- case arguments_type
186
- when :restful_authentication
187
- [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
188
- else
189
- [raw_password, salt].compact
273
+ private
274
+ def check_passwords_against_database?
275
+ self.class.check_passwords_against_database == true
276
+ end
277
+
278
+ def crypto_providers
279
+ [crypto_provider] + transition_from_crypto_providers
280
+ end
281
+
282
+ def encrypt_arguments(raw_password, check_against_database, arguments_type = nil)
283
+ salt = nil
284
+ salt = (check_against_database && send("#{password_salt_field}_changed?") ? send("#{password_salt_field}_was") : send(password_salt_field)) if password_salt_field
285
+
286
+ case arguments_type
287
+ when :restful_authentication
288
+ [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
289
+ else
290
+ [raw_password, salt].compact
291
+ end
292
+ end
293
+
294
+ # Determines if we need to tranisiton the password.
295
+ # If the index > 0 then we are using an "transition from" crypto provider.
296
+ # If the encryptor has a cost and the cost it outdated.
297
+ # If we aren't using database values
298
+ # If we are using database values, only if the password hasnt change so we don't overwrite any changes
299
+ def transition_password?(index, encryptor, crypted, check_against_database)
300
+ (index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))) &&
301
+ (!check_against_database || !send("#{crypted_password_field}_changed?"))
302
+ end
303
+
304
+ def transition_password(attempted_password)
305
+ self.password = attempted_password
306
+ save(false)
190
307
  end
191
- end
192
308
 
193
- def require_password_confirmation?
194
- new_record? || (password_salt_field && send("#{password_salt_field}_changed?")) || send(crypted_password_field).blank?
195
- end
309
+ def require_password?
310
+ new_record? || password_changed? || send(crypted_password_field).blank?
311
+ end
196
312
 
197
- def crypted_password_field
198
- self.class.crypted_password_field
199
- end
313
+ def ignore_blank_passwords?
314
+ self.class.ignore_blank_passwords == true
315
+ end
200
316
 
201
- def password_salt_field
202
- self.class.password_salt_field
203
- end
317
+ def password_changed?
318
+ @password_changed == true
319
+ end
320
+
321
+ def reset_password_changed
322
+ @password_changed = nil
323
+ end
204
324
 
205
- def crypto_provider
206
- self.class.crypto_provider
207
- end
325
+ def crypted_password_field
326
+ self.class.crypted_password_field
327
+ end
208
328
 
209
- def transition_from_crypto_providers
210
- self.class.transition_from_crypto_providers
211
- end
329
+ def password_salt_field
330
+ self.class.password_salt_field
331
+ end
332
+
333
+ def crypto_provider
334
+ self.class.crypto_provider
335
+ end
336
+
337
+ def transition_from_crypto_providers
338
+ self.class.transition_from_crypto_providers
339
+ end
340
+ end
212
341
  end
213
342
  end
214
343
  end
@@ -20,7 +20,7 @@ module Authlogic
20
20
  # * <tt>Default:</tt> 10.minutes
21
21
  # * <tt>Accepts:</tt> Fixnum
22
22
  def perishable_token_valid_for(value = nil)
23
- config(:perishable_token_valid_for, (!value.nil? && value.to_i) || value, 10.minutes.to_i)
23
+ rw_config(:perishable_token_valid_for, (!value.nil? && value.to_i) || value, 10.minutes.to_i)
24
24
  end
25
25
  alias_method :perishable_token_valid_for=, :perishable_token_valid_for
26
26
 
@@ -31,7 +31,7 @@ module Authlogic
31
31
  # * <tt>Default:</tt> false
32
32
  # * <tt>Accepts:</tt> Boolean
33
33
  def disable_perishable_token_maintenance(value = nil)
34
- config(:disable_perishable_token_maintenance, value, false)
34
+ rw_config(:disable_perishable_token_maintenance, value, false)
35
35
  end
36
36
  alias_method :disable_perishable_token_maintenance=, :disable_perishable_token_maintenance
37
37
  end
@@ -60,7 +60,7 @@ module Authlogic
60
60
  # If you want to use a different timeout value, just pass it as the second parameter:
61
61
  #
62
62
  # User.find_using_perishable_token(token, 1.hour)
63
- def find_using_perishable_token(token, age = perishable_token_valid_for)
63
+ def find_using_perishable_token(token, age = self.perishable_token_valid_for)
64
64
  return if token.blank?
65
65
  age = age.to_i
66
66
 
@@ -74,6 +74,11 @@ module Authlogic
74
74
 
75
75
  find(:first, :conditions => [conditions_sql, *conditions_subs])
76
76
  end
77
+
78
+ # This method will raise ActiveRecord::NotFound if no record is found.
79
+ def find_using_perishable_token!(token, age = perishable_token_valid_for)
80
+ find_using_perishable_token(token, age) || raise(ActiveRecord::RecordNotFound)
81
+ end
77
82
  end
78
83
 
79
84
  # Instance level methods for the perishable token.
@@ -97,4 +102,4 @@ module Authlogic
97
102
  end
98
103
  end
99
104
  end
100
- end
105
+ end
@@ -16,8 +16,10 @@ module Authlogic
16
16
  extend ClassMethods
17
17
  include InstanceMethods
18
18
 
19
- after_password_set :reset_persistence_token
20
- after_password_verification :reset_persistence_token!, :if => :reset_persistence_token?
19
+ if respond_to?(:after_password_set) && respond_to?(:after_password_verification)
20
+ after_password_set :reset_persistence_token
21
+ after_password_verification :reset_persistence_token!, :if => :reset_persistence_token?
22
+ end
21
23
 
22
24
  validates_presence_of :persistence_token
23
25
  validates_uniqueness_of :persistence_token, :if => :persistence_token_changed?
@@ -18,7 +18,7 @@ module Authlogic
18
18
  # * <tt>Default:</tt> false
19
19
  # * <tt>Accepts:</tt> Boolean
20
20
  def act_like_restful_authentication(value = nil)
21
- r = config(:act_like_restful_authentication, value, false)
21
+ r = rw_config(:act_like_restful_authentication, value, false)
22
22
  set_restful_authentication_config if value
23
23
  r
24
24
  end
@@ -26,9 +26,10 @@ module Authlogic
26
26
 
27
27
  # This works just like act_like_restful_authentication except that it will start transitioning your users to the algorithm you
28
28
  # specify with the crypto provider option. The next time they log in it will resave their password with the new algorithm
29
- # and any new record will use the new algorithm as well.
29
+ # and any new record will use the new algorithm as well. Make sure to update your users table if you are using the default
30
+ # migration since it will set crypted_password and salt columns to a maximum width of 40 characters which is not enough.
30
31
  def transition_from_restful_authentication(value = nil)
31
- r = config(:transition_from_restful_authentication, value, false)
32
+ r = rw_config(:transition_from_restful_authentication, value, false)
32
33
  set_restful_authentication_config if value
33
34
  r
34
35
  end
@@ -39,7 +40,7 @@ module Authlogic
39
40
  crypto_provider_key = act_like_restful_authentication ? :crypto_provider : :transition_from_crypto_providers
40
41
  self.send("#{crypto_provider_key}=", CryptoProviders::Sha1)
41
42
  if !defined?(::REST_AUTH_SITE_KEY) || ::REST_AUTH_SITE_KEY.nil?
42
- class_eval("::REST_AUTH_SITE_KEY = nil") if !defined?(::REST_AUTH_SITE_KEY)
43
+ class_eval("::REST_AUTH_SITE_KEY = ''") if !defined?(::REST_AUTH_SITE_KEY)
43
44
  CryptoProviders::Sha1.stretches = 1
44
45
  end
45
46
  end
@@ -5,7 +5,7 @@ module Authlogic
5
5
  #
6
6
  # Just to clear up any confusion, Authlogic stores both the record id and the persistence token in the session.
7
7
  # Why? So stale sessions can not be persisted. It stores the id so it can quickly find the record, and the
8
- # persistence token to ensure no sessions are stale. So if the persistence token changes, the user muct log
8
+ # persistence token to ensure no sessions are stale. So if the persistence token changes, the user must log
9
9
  # back in.
10
10
  #
11
11
  # Well, the persistence token changes with the password. What happens if the user changes his own password?
@@ -26,6 +26,17 @@ module Authlogic
26
26
  end
27
27
 
28
28
  module Config
29
+ # This is more of a convenience method. In order to turn off automatic maintenance of sessions just
30
+ # set this to false, or you can also set the session_ids method to a blank array. Both accomplish
31
+ # the same thing. This method is a little clearer in it's intentions though.
32
+ #
33
+ # * <tt>Default:</tt> true
34
+ # * <tt>Accepts:</tt> Boolean
35
+ def maintain_sessions(value = nil)
36
+ rw_config(:maintain_sessions, value, true)
37
+ end
38
+ alias_method :maintain_sessions=, :maintain_sessions
39
+
29
40
  # As you may know, authlogic sessions can be separate by id (See Authlogic::Session::Base#id). You can
30
41
  # specify here what session ids you want auto maintained. By default it is the main session, which has
31
42
  # an id of nil.
@@ -33,7 +44,7 @@ module Authlogic
33
44
  # * <tt>Default:</tt> [nil]
34
45
  # * <tt>Accepts:</tt> Array
35
46
  def session_ids(value = nil)
36
- config(:session_ids, value, [nil])
47
+ rw_config(:session_ids, value, [nil])
37
48
  end
38
49
  alias_method :session_ids=, :session_ids
39
50
 
@@ -42,7 +53,8 @@ module Authlogic
42
53
  # * <tt>Default:</tt> "#{klass.name}Session".constantize
43
54
  # * <tt>Accepts:</tt> Class
44
55
  def session_class(value = nil)
45
- config(:session_class, value, "#{name}Session".constantize)
56
+ const = "#{base_class.name}Session".constantize rescue nil
57
+ rw_config(:session_class, value, const)
46
58
  end
47
59
  alias_method :session_class=, :session_class
48
60
  end
@@ -73,7 +85,7 @@ module Authlogic
73
85
  end
74
86
 
75
87
  def update_sessions?
76
- !skip_session_maintenance && session_class.activated? && !session_ids.blank? && persistence_token_changed?
88
+ !skip_session_maintenance && session_class && session_class.activated? && self.class.maintain_sessions == true && !session_ids.blank? && persistence_token_changed?
77
89
  end
78
90
 
79
91
  def get_session_information
@@ -98,7 +110,7 @@ module Authlogic
98
110
  # We only want to automatically login into the first session, since this is the main session. The other sessions are sessions
99
111
  # that need to be created after logging into the main session.
100
112
  session_id = session_ids.first
101
- session_class.create!(*[self, self, session_id].compact)
113
+ session_class.create(*[self, self, session_id].compact)
102
114
 
103
115
  return true
104
116
  end
@@ -12,8 +12,15 @@ module Authlogic
12
12
 
13
13
  # All configuration for the single_access token aspect of acts_as_authentic.
14
14
  module Config
15
+ # The single access token is used for authentication via URLs, such as a private feed. That being said,
16
+ # if the user changes their password, that token probably shouldn't change. If it did, the user would have
17
+ # to update all of their URLs. So be default this is option is disabled, if you need it, feel free to turn
18
+ # it on.
19
+ #
20
+ # * <tt>Default:</tt> false
21
+ # * <tt>Accepts:</tt> Boolean
15
22
  def change_single_access_token_with_password(value = nil)
16
- config(:change_single_access_token_with_password, value, false)
23
+ rw_config(:change_single_access_token_with_password, value, false)
17
24
  end
18
25
  alias_method :change_single_access_token_with_password=, :change_single_access_token_with_password
19
26
  end
@@ -22,12 +29,12 @@ module Authlogic
22
29
  module Methods
23
30
  def self.included(klass)
24
31
  return if !klass.column_names.include?("single_access_token")
25
-
32
+
26
33
  klass.class_eval do
27
34
  include InstanceMethods
28
35
  validates_uniqueness_of :single_access_token, :if => :single_access_token_changed?
29
36
  before_validation :reset_single_access_token, :if => :reset_single_access_token?
30
- after_password_set :reset_single_access_token, :if => :change_single_access_token_with_password?
37
+ after_password_set(:reset_single_access_token, :if => :change_single_access_token_with_password?) if respond_to?(:after_password_set)
31
38
  end
32
39
  end
33
40
 
@@ -17,13 +17,13 @@ module Authlogic
17
17
  # company:
18
18
  #
19
19
  # acts_as_authentic do |c|
20
- # c.validation_scope = :company_id
20
+ # c.validations_scope = :company_id
21
21
  # end
22
22
  #
23
23
  # * <tt>Default:</tt> nil
24
24
  # * <tt>Accepts:</tt> Symbol or Array of symbols
25
25
  def validations_scope(value = nil)
26
- config(:validations_scope, value)
26
+ rw_config(:validations_scope, value)
27
27
  end
28
28
  alias_method :validations_scope=, :validations_scope
29
29
  end
@@ -42,8 +42,20 @@ module Authlogic
42
42
  controller.session
43
43
  end
44
44
 
45
+ def responds_to_single_access_allowed?
46
+ controller.respond_to?(:single_access_allowed?, true)
47
+ end
48
+
45
49
  def single_access_allowed?
46
- controller.respond_to?(:single_access_allowed?, true) && controller.send(:single_access_allowed?)
50
+ controller.send(:single_access_allowed?)
51
+ end
52
+
53
+ def responds_to_last_request_update_allowed?
54
+ controller.respond_to?(:last_request_update_allowed?, true)
55
+ end
56
+
57
+ def last_request_update_allowed?
58
+ controller.send(:last_request_update_allowed?)
47
59
  end
48
60
 
49
61
  private
@@ -13,7 +13,7 @@ module Authlogic
13
13
 
14
14
  def cookie_domain
15
15
  @cookie_domain_key ||= (Rails::VERSION::MAJOR >= 2 && Rails::VERSION::MINOR >= 3) ? :domain : :session_domain
16
- controller.class.session_options[@cookie_domain_key]
16
+ ActionController::Base.session_options[@cookie_domain_key]
17
17
  end
18
18
 
19
19
  def request_content_type
@@ -5,7 +5,7 @@ module Authlogic
5
5
  # This encryption method is reversible if you have the supplied key. So in order to use this encryption method you must supply it with a key first.
6
6
  # In an initializer, or before your application initializes, you should do the following:
7
7
  #
8
- # Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrable a bunch of random characters"
8
+ # Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrably a bunch of random characters"
9
9
  #
10
10
  # My final comment is that this is a strong encryption method, but its main weakness is that its reversible. If you do not need to reverse the hash
11
11
  # then you should consider Sha512 or BCrypt instead.
@@ -34,7 +34,7 @@ module Authlogic
34
34
 
35
35
  private
36
36
  def aes
37
- raise ArgumentError.new("You provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
37
+ raise ArgumentError.new("You must provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
38
38
  @aes ||= OpenSSL::Cipher::Cipher.new("AES-256-ECB")
39
39
  end
40
40
  end