authlogic 2.0.1 → 2.1.1

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 +105 -1
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.rdoc +137 -160
  5. data/Rakefile +47 -19
  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 +31 -17
  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 +207 -88
  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 +16 -4
  18. data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -3
  19. data/lib/authlogic/acts_as_authentic/validations_scope.rb +1 -1
  20. data/lib/authlogic/controller_adapters/abstract_adapter.rb +13 -1
  21. data/lib/authlogic/controller_adapters/rails_adapter.rb +14 -4
  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 +13 -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 +16 -12
  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 +46 -44
  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 +26 -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 +43 -21
  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,53 +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
 
43
86
  # A hash of options for the validates_length_of call for the password field. Allows you to change this however you want.
44
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
+ #
45
92
  # * <tt>Default:</tt> {:minimum => 4, :if => :require_password?}
46
93
  # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
47
94
  def validates_length_of_password_field_options(value = nil)
48
- config(:validates_length_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
95
+ rw_config(:validates_length_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
49
96
  end
50
97
  alias_method :validates_length_of_password_field_options=, :validates_length_of_password_field_options
51
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
+
52
110
  # A hash of options for the validates_confirmation_of call for the password field. Allows you to change this however you want.
53
111
  #
54
- # * <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?}
55
117
  # * <tt>Accepts:</tt> Hash of options accepted by validates_confirmation_of
56
118
  def validates_confirmation_of_password_field_options(value = nil)
57
- 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?})
58
120
  end
59
121
  alias_method :validates_confirmation_of_password_field_options=, :validates_confirmation_of_password_field_options
60
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
+
61
128
  # A hash of options for the validates_length_of call for the password_confirmation field. Allows you to change this however you want.
62
129
  #
63
- # * <tt>Default:</tt> {:minimum => 4, :if => :require_password_?}
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
64
135
  # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
65
136
  def validates_length_of_password_confirmation_field_options(value = nil)
66
- config(:validates_length_of_password_confirmation_field_options, value, {:minimum => 4, :if => :require_password?})
137
+ rw_config(:validates_length_of_password_confirmation_field_options, value, validates_length_of_password_field_options)
67
138
  end
68
139
  alias_method :validates_length_of_password_confirmation_field_options=, :validates_length_of_password_confirmation_field_options
69
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
+
70
146
  # The class you want to use to encrypt and verify your encrypted passwords. See the Authlogic::CryptoProviders module for more info
71
147
  # on the available methods and how to create your own.
72
148
  #
73
149
  # * <tt>Default:</tt> CryptoProviders::Sha512
74
150
  # * <tt>Accepts:</tt> Class
75
151
  def crypto_provider(value = nil)
76
- config(:crypto_provider, value, CryptoProviders::Sha512)
152
+ rw_config(:crypto_provider, value, CryptoProviders::Sha512)
77
153
  end
78
154
  alias_method :crypto_provider=, :crypto_provider
79
155
 
@@ -89,7 +165,7 @@ module Authlogic
89
165
  # * <tt>Default:</tt> nil
90
166
  # * <tt>Accepts:</tt> Class or Array
91
167
  def transition_from_crypto_providers(value = nil)
92
- config(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
168
+ rw_config(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
93
169
  end
94
170
  alias_method :transition_from_crypto_providers=, :transition_from_crypto_providers
95
171
  end
@@ -102,6 +178,7 @@ module Authlogic
102
178
  ]
103
179
 
104
180
  def self.included(klass)
181
+ return if klass.crypted_password_field.nil?
105
182
  klass.define_callbacks *METHODS
106
183
  end
107
184
 
@@ -118,107 +195,149 @@ module Authlogic
118
195
  # The methods related to the password field.
119
196
  module Methods
120
197
  def self.included(klass)
198
+ return if klass.crypted_password_field.nil?
199
+
121
200
  klass.class_eval do
201
+ include InstanceMethods
202
+
122
203
  if validate_password_field
123
204
  validates_length_of :password, validates_length_of_password_field_options
124
- validates_confirmation_of :password, validates_confirmation_of_password_field_options
125
- validates_length_of :password_confirmation, validates_length_of_password_confirmation_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
126
210
  end
211
+
212
+ after_save :reset_password_changed
127
213
  end
128
214
  end
129
215
 
130
- # The password
131
- def password
132
- @password
133
- end
216
+ module InstanceMethods
217
+ # The password
218
+ def password
219
+ @password
220
+ end
134
221
 
135
- # This is a virtual method. Once a password is passed to it, it will create new password salt as well as encrypt
136
- # the password.
137
- def password=(pass)
138
- return if pass.blank?
139
- before_password_set
140
- @password = pass
141
- send("#{password_salt_field}=", Authlogic::Random.friendly_token) if password_salt_field
142
- send("#{crypted_password_field}=", crypto_provider.encrypt(*encrypt_arguments(@password, act_like_restful_authentication? ? :restful_authentication : nil)))
143
- after_password_set
144
- 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
145
233
 
146
- # Accepts a raw password to determine if it is the correct password or not.
147
- def valid_password?(attempted_password)
148
- return false if attempted_password.blank? || send(crypted_password_field).blank?
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
149
241
 
150
- before_password_verification
151
-
152
- crypto_providers = [crypto_provider] + transition_from_crypto_providers
153
- crypto_providers.each_with_index do |encryptor, index|
154
- # The arguments_type of for the transitioning from restful_authentication
155
- arguments_type = (act_like_restful_authentication? && index == 0) ||
156
- (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
157
- :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
158
247
 
159
- if encryptor.matches?(send(crypted_password_field), *encrypt_arguments(attempted_password, arguments_type))
160
- # If we are transitioning from an older encryption algorithm and the password is still using the old algorithm
161
- # 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
162
- # the new cost.
163
- if index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))
164
- self.password = attempted_password
165
- 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
166
252
  end
167
-
168
- after_password_verification
169
-
170
- return true
171
253
  end
172
- end
173
254
 
174
- false
175
- end
255
+ false
256
+ end
176
257
 
177
- # Resets the password to a random friendly token.
178
- def reset_password
179
- friendly_token = Authlogic::Random.friendly_token
180
- self.password = friendly_token
181
- self.password_confirmation = friendly_token
182
- end
183
- 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
184
265
 
185
- # Resets the password to a random friendly token and then saves the record.
186
- def reset_password!
187
- reset_password
188
- save_without_session_maintenance(false)
189
- end
190
- 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!
191
272
 
192
- private
193
- def encrypt_arguments(raw_password, arguments_type = nil)
194
- salt = password_salt_field ? send(password_salt_field) : nil
195
- case arguments_type
196
- when :restful_authentication
197
- [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
198
- else
199
- [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)
200
307
  end
201
- end
202
308
 
203
- def require_password?
204
- new_record? || (password_salt_field && send("#{password_salt_field}_changed?")) || send(crypted_password_field).blank?
205
- end
309
+ def require_password?
310
+ new_record? || password_changed? || send(crypted_password_field).blank?
311
+ end
206
312
 
207
- def crypted_password_field
208
- self.class.crypted_password_field
209
- end
313
+ def ignore_blank_passwords?
314
+ self.class.ignore_blank_passwords == true
315
+ end
210
316
 
211
- def password_salt_field
212
- self.class.password_salt_field
213
- end
317
+ def password_changed?
318
+ @password_changed == true
319
+ end
320
+
321
+ def reset_password_changed
322
+ @password_changed = nil
323
+ end
214
324
 
215
- def crypto_provider
216
- self.class.crypto_provider
217
- end
325
+ def crypted_password_field
326
+ self.class.crypted_password_field
327
+ end
218
328
 
219
- def transition_from_crypto_providers
220
- self.class.transition_from_crypto_providers
221
- 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
222
341
  end
223
342
  end
224
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
@@ -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
 
@@ -23,7 +23,7 @@ module Authlogic
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
@@ -3,6 +3,8 @@ module Authlogic
3
3
  # Adapts authlogic to work with rails. The point is to close the gap between what authlogic expects and what the rails controller object
4
4
  # provides. Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite, etc.
5
5
  class RailsAdapter < AbstractAdapter
6
+ class AuthlogicLoadedTooLateError < StandardError; end
7
+
6
8
  def authenticate_with_http_basic(&block)
7
9
  controller.authenticate_with_http_basic(&block)
8
10
  end
@@ -12,8 +14,8 @@ module Authlogic
12
14
  end
13
15
 
14
16
  def cookie_domain
15
- @cookie_domain_key ||= (Rails::VERSION::MAJOR >= 2 && Rails::VERSION::MINOR >= 3) ? :domain : :session_domain
16
- controller.class.session_options[@cookie_domain_key]
17
+ @cookie_domain_key ||= Rails::VERSION::STRING >= '2.3' ? :domain : :session_domain
18
+ ActionController::Base.session_options[@cookie_domain_key]
17
19
  end
18
20
 
19
21
  def request_content_type
@@ -23,9 +25,17 @@ module Authlogic
23
25
  # Lets Authlogic know about the controller object via a before filter, AKA "activates" authlogic.
24
26
  module RailsImplementation
25
27
  def self.included(klass) # :nodoc:
28
+ if defined?(::ApplicationController)
29
+ raise AuthlogicLoadedTooLateError.new("Authlogic is trying to prepend a before_filter in ActionController::Base to active itself" +
30
+ ", the problem is that ApplicationController has already been loaded meaning the before_filter won't get copied into your" +
31
+ " application. Generally this is due to another gem or plugin requiring your ApplicationController prematurely, such as" +
32
+ " the resource_controller plugin. The solution is to require Authlogic before these other gems / plugins. Please require" +
33
+ " authlogic first to get rid of this error.")
34
+ end
35
+
26
36
  klass.prepend_before_filter :activate_authlogic
27
37
  end
28
-
38
+
29
39
  private
30
40
  def activate_authlogic
31
41
  Authlogic::Session::Base.controller = RailsAdapter.new(self)
@@ -35,4 +45,4 @@ module Authlogic
35
45
  end
36
46
  end
37
47
 
38
- ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation)
48
+ ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation)