authlogic 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of authlogic might be problematic. Click here for more details.

Files changed (26) hide show
  1. data/CHANGELOG.rdoc +8 -1
  2. data/Manifest +4 -0
  3. data/README.rdoc +50 -24
  4. data/authlogic.gemspec +5 -5
  5. data/lib/authlogic.rb +3 -0
  6. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb +34 -10
  7. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb +8 -3
  8. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb +2 -2
  9. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb +73 -0
  10. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb +27 -27
  11. data/lib/authlogic/session/base.rb +41 -38
  12. data/lib/authlogic/session/config.rb +125 -32
  13. data/lib/authlogic/session/password_reset.rb +17 -0
  14. data/lib/authlogic/session/scopes.rb +2 -6
  15. data/lib/authlogic/version.rb +2 -2
  16. data/test/fixtures/users.yml +3 -0
  17. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb +6 -1
  18. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb +16 -2
  19. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb +40 -0
  20. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb +1 -1
  21. data/test/session_tests/base_test.rb +9 -8
  22. data/test/session_tests/config_test.rb +84 -12
  23. data/test/session_tests/password_reset_test.rb +15 -0
  24. data/test/session_tests/scopes_test.rb +5 -4
  25. data/test/test_helper.rb +12 -6
  26. metadata +10 -2
@@ -18,34 +18,34 @@ module Authlogic
18
18
  module SingleAccess
19
19
  def acts_as_authentic_with_single_access(options = {})
20
20
  acts_as_authentic_without_single_access(options)
21
-
22
- if options[:single_access_token_field]
23
- class_eval <<-"end_eval", __FILE__, __LINE__
24
- validates_uniqueness_of :#{options[:single_access_token_field]}
25
-
26
- before_validation :set_#{options[:single_access_token_field]}_field
27
-
28
- def password_with_single_access=(value)
29
- reset_#{options[:single_access_token_field]} if #{options[:change_single_access_token_with_password].inspect}
30
- self.password_without_single_access = value
21
+
22
+ return if options[:single_access_token_field].blank?
23
+
24
+ class_eval <<-"end_eval", __FILE__, __LINE__
25
+ validates_uniqueness_of :#{options[:single_access_token_field]}
26
+
27
+ before_validation :set_#{options[:single_access_token_field]}_field
28
+
29
+ def password_with_single_access=(value)
30
+ reset_#{options[:single_access_token_field]} if #{options[:change_single_access_token_with_password].inspect}
31
+ self.password_without_single_access = value
32
+ end
33
+ alias_method_chain :password=, :single_access
34
+
35
+ def reset_#{options[:single_access_token_field]}
36
+ self.#{options[:single_access_token_field]} = self.class.friendly_unique_token
37
+ end
38
+
39
+ def reset_#{options[:single_access_token_field]}!
40
+ reset_#{options[:single_access_token_field]}
41
+ save_without_session_maintenance
42
+ end
43
+
44
+ protected
45
+ def set_#{options[:single_access_token_field]}_field
46
+ reset_#{options[:single_access_token_field]} if #{options[:single_access_token_field]}.blank?
31
47
  end
32
- alias_method_chain :password=, :single_access
33
-
34
- def reset_#{options[:single_access_token_field]}
35
- self.#{options[:single_access_token_field]} = self.class.friendly_unique_token
36
- end
37
-
38
- def reset_#{options[:single_access_token_field]}!
39
- reset_#{options[:single_access_token_field]}
40
- save_without_session_maintenance
41
- end
42
-
43
- protected
44
- def set_#{options[:single_access_token_field]}_field
45
- reset_#{options[:single_access_token_field]} if #{options[:single_access_token_field]}.blank?
46
- end
47
- end_eval
48
- end
48
+ end_eval
49
49
  end
50
50
  end
51
51
  end
@@ -8,22 +8,18 @@ module Authlogic
8
8
 
9
9
  class << self
10
10
  # Returns true if a controller have been set and can be used properly. This MUST be set before anything can be done. Similar to how ActiveRecord won't allow you to do anything
11
- # without establishing a DB connection. By default this is done for you automatically, but if you are using Authlogic in a unique way outside of rails, you need to assign a controller
11
+ # without establishing a DB connection. In your framework environment this is done for you, but if you are using Authlogic outside of your frameword, you need to assign a controller
12
12
  # object to Authlogic via Authlogic::Session::Base.controller = obj.
13
13
  def activated?
14
14
  !controller.blank?
15
15
  end
16
16
 
17
17
  def controller=(value) # :nodoc:
18
- controllers[Thread.current] = value
18
+ Thread.current[:authlogic_controller] = value
19
19
  end
20
20
 
21
21
  def controller # :nodoc:
22
- controllers[Thread.current]
23
- end
24
-
25
- def reset_controllers!
26
- @@controllers = {}
22
+ Thread.current[:authlogic_controller]
27
23
  end
28
24
 
29
25
  # A convenince method. The same as:
@@ -41,16 +37,26 @@ module Authlogic
41
37
  session.save!
42
38
  end
43
39
 
44
- # A convenience method for session.find_record. Finds your session by session, then cookie, and finally basic http auth. Perfect for that global before_filter to find your logged in user:
40
+ # A convenience method for session.find_record. Finds your session by parameters, then session, then cookie, and finally by basic http auth.
41
+ # This is perfect for persisting your session:
42
+ #
43
+ # helper_method :current_user_session, :current_user
45
44
  #
46
- # before_filter :load_user
45
+ # def current_user_session
46
+ # return @current_user_session if defined?(@current_user_session)
47
+ # @current_user_session = UserSession.find
48
+ # end
47
49
  #
48
- # def load_user
49
- # @user_session = UserSession.find
50
- # @current_user = @user_session && @user_session.user
50
+ # def current_user
51
+ # return @current_user if defined?(@current_user)
52
+ # @current_user = current_user_session && current_user_session.user
51
53
  # end
52
54
  #
53
- # Accepts a single parameter as the id. See initialize for more information on ids. Lastly, how it finds the session can be modified via configuration.
55
+ # Accepts a single parameter as the id, to find session that you marked with an id:
56
+ #
57
+ # UserSession.find(:secure)
58
+ #
59
+ # See the id method for more information on ids.
54
60
  def find(id = nil)
55
61
  args = [id].compact
56
62
  session = new(*args)
@@ -58,7 +64,9 @@ module Authlogic
58
64
  nil
59
65
  end
60
66
 
61
- def klass # :nodoc:
67
+ # The name of the class that this session is authenticating with. For example, the UserSession class will authenticate with the User class
68
+ # unless you specify otherwise in your configuration.
69
+ def klass
62
70
  @klass ||=
63
71
  if klass_name
64
72
  klass_name.constantize
@@ -67,17 +75,13 @@ module Authlogic
67
75
  end
68
76
  end
69
77
 
70
- def klass_name # :nodoc:
78
+ # Same as klass, just returns a string instead of the actual constant.
79
+ def klass_name
71
80
  @klass_name ||=
72
81
  if guessed_name = name.scan(/(.*)Session/)[0]
73
82
  @klass_name = guessed_name[0]
74
83
  end
75
84
  end
76
-
77
- private
78
- def controllers
79
- @@controllers ||= {}
80
- end
81
85
  end
82
86
 
83
87
  attr_accessor :new_session
@@ -96,10 +100,10 @@ module Authlogic
96
100
  # UserSession.new({:login => "login", :password => "password", :remember_me => true}, :my_id)
97
101
  # UserSession.new(User.first, true, :my_id)
98
102
  #
99
- # Ids are rarely used, but they can be useful. For example, what if users allow other users to login into their account via proxy? Now that user can "technically" be logged into 2 accounts at once.
100
- # To solve this just pass a id called :proxy, or whatever you want. Authlogic will separate everything out.
103
+ # For more information on ids see the id method.
101
104
  #
102
- # The reason the id is separate from the first parameter hash is becuase this should be controlled by you, not by what the user passes. A usr could inject their own id and things would not work as expected.
105
+ # Lastly, the reason the id is separate from the first parameter hash is becuase this should be controlled by you, not by what the user passes.
106
+ # A user could inject their own id and things would not work as expected.
103
107
  def initialize(*args)
104
108
  raise NotActivated.new(self) unless self.class.activated?
105
109
 
@@ -117,8 +121,8 @@ module Authlogic
117
121
 
118
122
  # A flag for how the user is logging in. Possible values:
119
123
  #
120
- # * :password - username and password
121
- # * :unauthorized_record - an actual ActiveRecord object
124
+ # * <tt>:password</tt> - username and password
125
+ # * <tt>:unauthorized_record</tt> - an actual ActiveRecord object
122
126
  #
123
127
  # By default this is :password
124
128
  def authenticating_with
@@ -176,7 +180,7 @@ module Authlogic
176
180
  @errors ||= Errors.new(self)
177
181
  end
178
182
 
179
- # Attempts to find the record by session, then cookie, and finally basic http auth. See the class level find method if you are wanting to use this in a before_filter to persist your session.
183
+ # Attempts to find the record by params, then session, then cookie, and finally basic http auth. See the class level find method if you are wanting to use this to persist your session.
180
184
  def find_record
181
185
  if record
182
186
  self.new_session = false
@@ -202,14 +206,13 @@ module Authlogic
202
206
  # and a "secure" user session. The secure user session would be created only when they want to modify their billing information, or other sensative information. Similar to me.com. This requires 2
203
207
  # user sessions. Just use an id for the "secure" session and you should be good.
204
208
  #
205
- # You can set the id a number of ways:
209
+ # You can set the id during initialization (see initialize for more information), or as an attribute:
210
+ #
211
+ # session.id = :my_id
206
212
  #
207
- # session = Session.new(:secure)
208
- # session = Session.new("username", "password", :secure)
209
- # session = Session.new({:username => "username", :password => "password"}, :secure)
210
- # session.id = :secure
213
+ # Just be sure and set your id before you save your session.
211
214
  #
212
- # Just be sure and set your id before you validate / create / update your session.
215
+ # Lastly, to retrieve your session with the id check out the find class method.
213
216
  def id
214
217
  @id
215
218
  end
@@ -393,26 +396,26 @@ module Authlogic
393
396
 
394
397
  case authenticating_with
395
398
  when :password
396
- errors.add(login_field, "can not be blank") if send(login_field).blank?
397
- errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
399
+ errors.add(login_field, login_blank_message) if send(login_field).blank?
400
+ errors.add(password_field, password_blank_message) if send("protected_#{password_field}").blank?
398
401
  return false if errors.count > 0
399
402
 
400
403
  unchecked_record = search_for_record(find_by_login_method, send(login_field))
401
404
 
402
405
  if unchecked_record.blank?
403
- errors.add(login_field, "was not found")
406
+ errors.add(login_field, login_not_found_message)
404
407
  return false
405
408
  end
406
409
 
407
410
  unless unchecked_record.send(verify_password_method, send("protected_#{password_field}"))
408
- errors.add(password_field, "is invalid")
411
+ errors.add(password_field, password_invalid_message)
409
412
  return false
410
413
  end
411
414
  when :unauthorized_record
412
415
  unchecked_record = unauthorized_record
413
416
 
414
417
  if unchecked_record.blank?
415
- errors.add_to_base("The record could not be found and did not match the requirements.")
418
+ errors.add_to_base("You can not login with a blank record.")
416
419
  return false
417
420
  end
418
421
 
@@ -429,7 +432,7 @@ module Authlogic
429
432
  def valid_record?
430
433
  [:active, :approved, :confirmed].each do |required_status|
431
434
  if record.respond_to?("#{required_status}?") && !record.send("#{required_status}?")
432
- errors.add_to_base("Your account has not been marked as #{required_status}")
435
+ errors.add_to_base(send("not_#{required_status}_message"))
433
436
  return false
434
437
  end
435
438
  end
@@ -92,7 +92,7 @@ module Authlogic
92
92
  # Calling UserSession.find tries to find the user session by session, then cookie, then params, and finally by basic http auth.
93
93
  # This option allows you to change the order or remove any of these.
94
94
  #
95
- # * <tt>Default:</tt> [:session, :cookie, :params, :http_auth]
95
+ # * <tt>Default:</tt> [:params, :session, :cookie, :http_auth]
96
96
  # * <tt>Accepts:</tt> Array, and can only use any of the 3 options above
97
97
  def find_with(*values)
98
98
  if values.blank?
@@ -119,6 +119,32 @@ module Authlogic
119
119
  end
120
120
  alias_method :last_request_at_threshold=, :last_request_at_threshold
121
121
 
122
+ # The error message used when the login is left blank.
123
+ #
124
+ # * <tt>Default:</tt> "can not be blank"
125
+ # * <tt>Accepts:</tt> String
126
+ def login_blank_message(value = nil)
127
+ if value.nil?
128
+ read_inheritable_attribute(:login_blank_message) || login_blank_message("can not be blank")
129
+ else
130
+ write_inheritable_attribute(:login_blank_message, value)
131
+ end
132
+ end
133
+ alias_method :login_blank_message=, :login_blank_message
134
+
135
+ # The error message used when the login could not be found in the database.
136
+ #
137
+ # * <tt>Default:</tt> "does not exist"
138
+ # * <tt>Accepts:</tt> String
139
+ def login_not_found_message(value = nil)
140
+ if value.nil?
141
+ read_inheritable_attribute(:login_not_found_message) || login_not_found_message("does not exist")
142
+ else
143
+ write_inheritable_attribute(:login_not_found_message, value)
144
+ end
145
+ end
146
+ alias_method :login_not_found_message=, :login_not_found_message
147
+
122
148
  # The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your
123
149
  # Authlogic::Session, if you want it can be something completely different than the field in your model. So if you wanted people to
124
150
  # login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
@@ -135,6 +161,45 @@ module Authlogic
135
161
  end
136
162
  alias_method :login_field=, :login_field
137
163
 
164
+ # The error message used when the record returns false to active?
165
+ #
166
+ # * <tt>Default:</tt> "Your account is not active"
167
+ # * <tt>Accepts:</tt> String
168
+ def not_active_message(value = nil)
169
+ if value.nil?
170
+ read_inheritable_attribute(:not_active_message) || not_active_message("Your account is not active")
171
+ else
172
+ write_inheritable_attribute(:not_active_message, value)
173
+ end
174
+ end
175
+ alias_method :not_active_message=, :not_active_message
176
+
177
+ # The error message used when the record returns false to approved?
178
+ #
179
+ # * <tt>Default:</tt> "Your account is not approved"
180
+ # * <tt>Accepts:</tt> String
181
+ def not_approved_message(value = nil)
182
+ if value.nil?
183
+ read_inheritable_attribute(:not_approved_message) || not_active_message("Your account is not approved")
184
+ else
185
+ write_inheritable_attribute(:not_approved_message, value)
186
+ end
187
+ end
188
+ alias_method :not_approved_message=, :not_approved_message
189
+
190
+ # The error message used when the record returns false to confirmed?
191
+ #
192
+ # * <tt>Default:</tt> "Your account is not confirmed"
193
+ # * <tt>Accepts:</tt> String
194
+ def not_confirmed_message(value = nil)
195
+ if value.nil?
196
+ read_inheritable_attribute(:not_confirmed_message) || not_active_message("Your account is not confirmed")
197
+ else
198
+ write_inheritable_attribute(:not_confirmed_message, value)
199
+ end
200
+ end
201
+ alias_method :not_confirmed_message=, :not_confirmed_message
202
+
138
203
  # Works exactly like cookie_key, but for params. So a user can login via params just like a cookie or a session. Your URL would look like:
139
204
  #
140
205
  # http://www.domain.com?user_credentials=my_single_access_key
@@ -153,6 +218,18 @@ module Authlogic
153
218
  end
154
219
  alias_method :params_key=, :params_key
155
220
 
221
+ # The error message used when the password is left blank.
222
+ #
223
+ # * <tt>Default:</tt> "can not be blank"
224
+ # * <tt>Accepts:</tt> String
225
+ def password_blank_message(value = nil)
226
+ if value.nil?
227
+ read_inheritable_attribute(:password_blank_message) || password_blank_message("can not be blank")
228
+ else
229
+ write_inheritable_attribute(:password_blank_message, value)
230
+ end
231
+ end
232
+ alias_method :password_blank_message=, :password_blank_message
156
233
 
157
234
  # Works exactly like login_field, but for the password instead.
158
235
  #
@@ -167,6 +244,19 @@ module Authlogic
167
244
  end
168
245
  alias_method :password_field=, :password_field
169
246
 
247
+ # The error message used when the password is invalid.
248
+ #
249
+ # * <tt>Default:</tt> "is invalid"
250
+ # * <tt>Accepts:</tt> String
251
+ def password_invalid_message(value = nil)
252
+ if value.nil?
253
+ read_inheritable_attribute(:password_invalid_message) || login_not_found_message("is invalid")
254
+ else
255
+ write_inheritable_attribute(:password_invalid_message, value)
256
+ end
257
+ end
258
+ alias_method :password_invalid_message=, :password_invalid_message
259
+
170
260
  # If sessions should be remembered by default or not.
171
261
  #
172
262
  # * <tt>Default:</tt> false
@@ -193,21 +283,6 @@ module Authlogic
193
283
  end
194
284
  alias_method :remember_me_for=, :remember_me_for
195
285
 
196
- # The name of the field that the remember token is stored. This is for cookies. Let's say you set up your app and want all users to be remembered for 6 months. Then you realize that might be a little too
197
- # long. Well they already have a cookie set to expire in 6 months. Without a token you would have to reset their password, which obviously isn't feasible. So instead of messing with their password
198
- # just reset their remember token. Next time they access the site and try to login via a cookie it will be rejected and they will have to relogin.
199
- #
200
- # * <tt>Default:</tt> Uses the configuration option in your model: User.acts_as_authentic_config[:remember_token_field]
201
- # * <tt>Accepts:</tt> Symbol or String
202
- def remember_token_field(value = nil)
203
- if value.nil?
204
- read_inheritable_attribute(:remember_token_field) || remember_token_field(klass.acts_as_authentic_config[:remember_token_field])
205
- else
206
- write_inheritable_attribute(:remember_token_field, value)
207
- end
208
- end
209
- alias_method :remember_token_field=, :remember_token_field
210
-
211
286
  # Works exactly like cookie_key, but for sessions. See cookie_key for more info.
212
287
  #
213
288
  # * <tt>Default:</tt> cookie_key
@@ -235,20 +310,6 @@ module Authlogic
235
310
  end
236
311
  alias_method :single_access_allowed_request_types=, :single_access_allowed_request_types
237
312
 
238
- # This is a separate token for logging with single access. It works just the the remember_token but it does NOT persist. Meaning if a record is found with the single_access_token it will not set
239
- # the session or the cookie and "remember" the user. Checkout the "Single Access / Private Feeds Access" section in the README.
240
- #
241
- # * <tt>Default:</tt> Uses the configuration option in your model: User.acts_as_authentic_config[:single_access_token]
242
- # * <tt>Accepts:</tt> Symbol or String
243
- def single_access_token_field(value = nil)
244
- if value.nil?
245
- read_inheritable_attribute(:single_access_token_field) || single_access_token_field(klass.acts_as_authentic_config[:single_access_token_field])
246
- else
247
- write_inheritable_attribute(:single_access_token_field, value)
248
- end
249
- end
250
- alias_method :single_access_token_field=, :single_access_token_field
251
-
252
313
  # The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
253
314
  #
254
315
  # * <tt>Default:</tt> "valid_#{password_field}?"
@@ -283,11 +344,31 @@ module Authlogic
283
344
  def last_request_at_threshold
284
345
  self.class.last_request_at_threshold
285
346
  end
347
+
348
+ def login_blank_message
349
+ self.class.login_blank_message
350
+ end
351
+
352
+ def login_not_found_message
353
+ self.class.login_not_found_message
354
+ end
286
355
 
287
356
  def login_field
288
357
  self.class.login_field
289
358
  end
290
359
 
360
+ def not_active_message
361
+ self.class.not_active_message
362
+ end
363
+
364
+ def not_approved_message
365
+ self.class.not_approved_message
366
+ end
367
+
368
+ def not_confirmed_message
369
+ self.class.not_confirmed_message
370
+ end
371
+
291
372
  def params_allowed_request_types
292
373
  build_key(self.class.params_allowed_request_types)
293
374
  end
@@ -295,18 +376,30 @@ module Authlogic
295
376
  def params_key
296
377
  build_key(self.class.params_key)
297
378
  end
379
+
380
+ def password_blank_message
381
+ self.class.password_blank_message
382
+ end
298
383
 
299
384
  def password_field
300
385
  self.class.password_field
301
386
  end
302
387
 
388
+ def password_invalid_message
389
+ self.class.password_invalid_message
390
+ end
391
+
392
+ def password_reset_token_field
393
+ klass.acts_as_authentic_config[:password_reset_token_field]
394
+ end
395
+
303
396
  def remember_me_for
304
397
  return unless remember_me?
305
398
  self.class.remember_me_for
306
399
  end
307
400
 
308
401
  def remember_token_field
309
- self.class.remember_token_field
402
+ klass.acts_as_authentic_config[:remember_token_field]
310
403
  end
311
404
 
312
405
  def session_key
@@ -314,7 +407,7 @@ module Authlogic
314
407
  end
315
408
 
316
409
  def single_access_token_field
317
- self.class.single_access_token_field
410
+ klass.acts_as_authentic_config[:single_access_token_field]
318
411
  end
319
412
 
320
413
  def single_access_allowed_request_types