authlogic 2.0.9 → 2.0.11

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.

@@ -31,6 +31,16 @@ module Authlogic
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
+ config(:require_password_confirmation, value, true)
41
+ end
42
+ alias_method :require_password_confirmation=, :require_password_confirmation
43
+
34
44
  # By default passwords are required when a record is new or the crypted_password is blank, but if both of these things
35
45
  # are met a password is not required. In this case, blank passwords are ignored.
36
46
  #
@@ -47,6 +57,23 @@ module Authlogic
47
57
  end
48
58
  alias_method :ignore_blank_passwords=, :ignore_blank_passwords
49
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
+ config(:check_passwords_against_database, value, true)
74
+ end
75
+ alias_method :check_passwords_against_database=, :check_passwords_against_database
76
+
50
77
  # Whether or not to validate the password field.
51
78
  #
52
79
  # * <tt>Default:</tt> true
@@ -58,6 +85,10 @@ module Authlogic
58
85
 
59
86
  # A hash of options for the validates_length_of call for the password field. Allows you to change this however you want.
60
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
+ #
61
92
  # * <tt>Default:</tt> {:minimum => 4, :if => :require_password?}
62
93
  # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
63
94
  def validates_length_of_password_field_options(value = nil)
@@ -65,8 +96,23 @@ module Authlogic
65
96
  end
66
97
  alias_method :validates_length_of_password_field_options=, :validates_length_of_password_field_options
67
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
+
68
110
  # A hash of options for the validates_confirmation_of call for the password field. Allows you to change this however you want.
69
111
  #
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_login_field_options to merge
114
+ # options.</b>
115
+ #
70
116
  # * <tt>Default:</tt> {:minimum => 4, :if => "#{password_salt_field}_changed?".to_sym}
71
117
  # * <tt>Accepts:</tt> Hash of options accepted by validates_confirmation_of
72
118
  def validates_confirmation_of_password_field_options(value = nil)
@@ -74,8 +120,17 @@ module Authlogic
74
120
  end
75
121
  alias_method :validates_confirmation_of_password_field_options=, :validates_confirmation_of_password_field_options
76
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
+
77
128
  # A hash of options for the validates_length_of call for the password_confirmation field. Allows you to change this however you want.
78
129
  #
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_login_field_options to merge
132
+ # options.</b>
133
+ #
79
134
  # * <tt>Default:</tt> validates_length_of_password_field_options
80
135
  # * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
81
136
  def validates_length_of_password_confirmation_field_options(value = nil)
@@ -83,6 +138,11 @@ module Authlogic
83
138
  end
84
139
  alias_method :validates_length_of_password_confirmation_field_options=, :validates_length_of_password_confirmation_field_options
85
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
+
86
146
  # The class you want to use to encrypt and verify your encrypted passwords. See the Authlogic::CryptoProviders module for more info
87
147
  # on the available methods and how to create your own.
88
148
  #
@@ -142,8 +202,11 @@ module Authlogic
142
202
 
143
203
  if validate_password_field
144
204
  validates_length_of :password, validates_length_of_password_field_options
145
- validates_confirmation_of :password, validates_confirmation_of_password_field_options
146
- 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
147
210
  end
148
211
 
149
212
  after_save :reset_password_changed
@@ -168,30 +231,23 @@ module Authlogic
168
231
  after_password_set
169
232
  end
170
233
 
171
- # Accepts a raw password to determine if it is the correct password or not.
172
- def valid_password?(attempted_password)
173
- return false if attempted_password.blank? || send(crypted_password_field).blank?
174
-
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?
175
240
  before_password_verification
176
241
 
177
- crypto_providers = [crypto_provider] + transition_from_crypto_providers
178
242
  crypto_providers.each_with_index do |encryptor, index|
179
243
  # The arguments_type of for the transitioning from restful_authentication
180
244
  arguments_type = (act_like_restful_authentication? && index == 0) ||
181
245
  (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
182
246
  :restful_authentication : nil
183
247
 
184
- if encryptor.matches?(send(crypted_password_field), *encrypt_arguments(attempted_password, arguments_type))
185
- # If we are transitioning from an older encryption algorithm and the password is still using the old algorithm
186
- # 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
187
- # the new cost.
188
- if index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))
189
- self.password = attempted_password
190
- save(false)
191
- end
192
-
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)
193
250
  after_password_verification
194
-
195
251
  return true
196
252
  end
197
253
  end
@@ -215,8 +271,18 @@ module Authlogic
215
271
  alias_method :randomize_password!, :reset_password!
216
272
 
217
273
  private
218
- def encrypt_arguments(raw_password, arguments_type = nil)
219
- salt = password_salt_field ? send(password_salt_field) : nil
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
+
220
286
  case arguments_type
221
287
  when :restful_authentication
222
288
  [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
@@ -224,6 +290,21 @@ module Authlogic
224
290
  [raw_password, salt].compact
225
291
  end
226
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)
307
+ end
227
308
 
228
309
  def require_password?
229
310
  new_record? || password_changed? || send(crypted_password_field).blank?
@@ -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
+ 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.
@@ -74,7 +85,7 @@ module Authlogic
74
85
  end
75
86
 
76
87
  def update_sessions?
77
- !skip_session_maintenance && session_class && 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?
78
89
  end
79
90
 
80
91
  def get_session_information
@@ -34,7 +34,7 @@ module Authlogic
34
34
  include InstanceMethods
35
35
  validates_uniqueness_of :single_access_token, :if => :single_access_token_changed?
36
36
  before_validation :reset_single_access_token, :if => :reset_single_access_token?
37
- 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)
38
38
  end
39
39
  end
40
40
 
@@ -42,8 +42,12 @@ 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?)
47
51
  end
48
52
 
49
53
  private
@@ -45,7 +45,7 @@ module Authlogic
45
45
  # user_session: (or whatever name you are using)
46
46
  # login: login
47
47
  # email: email
48
- # passwword: password
48
+ # password: password
49
49
  # remember_me: remember me
50
50
  class I18n
51
51
  class << self
@@ -1,23 +1,58 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Handles all authentication that deals with basic HTTP auth.
3
+ # Handles all authentication that deals with basic HTTP auth. Which is authentication built into the HTTP protocol:
4
+ #
5
+ # http://username:password@whatever.com
6
+ #
7
+ # Also, if you are not comfortable letting users pass their raw username and password you can always use the single
8
+ # access token. See Authlogic::Session::Params for more info.
4
9
  module HttpAuth
5
10
  def self.included(klass)
6
- klass.persist :persist_by_http_auth
11
+ klass.class_eval do
12
+ extend Config
13
+ include InstanceMethods
14
+ persist :persist_by_http_auth, :if => :persist_by_http_auth?
15
+ end
7
16
  end
8
17
 
9
- private
10
- def persist_by_http_auth
11
- controller.authenticate_with_http_basic do |login, password|
12
- if !login.blank? && !password.blank?
13
- send("#{login_field}=", login)
14
- send("#{password_field}=", password)
15
- return valid?
18
+ # Configuration for the HTTP basic auth feature of Authlogic.
19
+ module Config
20
+ # Do you want to allow your users to log in via HTTP basic auth?
21
+ #
22
+ # I recommend keeping this enabled. The only time I feel this should be disabled is if you are not comfortable
23
+ # having your users provide their raw username and password. Whatever the reason, you can disable it here.
24
+ #
25
+ # * <tt>Default:</tt> true
26
+ # * <tt>Accepts:</tt> Boolean
27
+ def allow_http_basic_auth(value = nil)
28
+ config(:allow_http_basic_auth, value, true)
29
+ end
30
+ alias_method :allow_http_basic_auth=, :allow_http_basic_auth
31
+ end
32
+
33
+ # Instance methods for the HTTP basic auth feature of authlogic.
34
+ module InstanceMethods
35
+ private
36
+ def persist_by_http_auth?
37
+ allow_http_basic_auth? && login_field && password_field
38
+ end
39
+
40
+ def persist_by_http_auth
41
+ controller.authenticate_with_http_basic do |login, password|
42
+ if !login.blank? && !password.blank?
43
+ send("#{login_field}=", login)
44
+ send("#{password_field}=", password)
45
+ return valid?
46
+ end
16
47
  end
48
+
49
+ false
17
50
  end
18
51
 
19
- false
20
- end
52
+ def allow_http_basic_auth?
53
+ self.class.allow_http_basic_auth == true
54
+ end
55
+ end
21
56
  end
22
57
  end
23
58
  end
@@ -48,10 +48,10 @@ module Authlogic
48
48
  alias_method :params_key=, :params_key
49
49
 
50
50
  # Authentication is allowed via a single access token, but maybe this is something you don't want for your application as a whole. Maybe this is something you only want for specific request types.
51
- # Specify a list of allowed request types and single access authentication will only be allowed for the ones you specify. Checkout the "Single Access / Private Feeds Access" section in the README.
51
+ # Specify a list of allowed request types and single access authentication will only be allowed for the ones you specify.
52
52
  #
53
- # * <tt>Default:</tt> "application/rss+xml", "application/atom+xml"
54
- # * <tt>Accepts:</tt> String of request type, or :all to allow single access authentication for any and all request types
53
+ # * <tt>Default:</tt> ["application/rss+xml", "application/atom+xml"]
54
+ # * <tt>Accepts:</tt> String of a request type, or :all or :any to allow single access authentication for any and all request types
55
55
  def single_access_allowed_request_types(value = nil)
56
56
  config(:single_access_allowed_request_types, value, ["application/rss+xml", "application/atom+xml"])
57
57
  end
@@ -68,10 +68,15 @@ module Authlogic
68
68
  end
69
69
 
70
70
  def params_enabled?
71
- params_credentials && klass.column_names.include?("single_access_token") &&
72
- (single_access_allowed_request_types.include?(controller.request_content_type) ||
73
- single_access_allowed_request_types.include?(:all) ||
74
- controller.single_access_allowed?)
71
+ return false if !params_credentials || !klass.column_names.include?("single_access_token")
72
+ return controller.single_access_allowed? if controller.responds_to_single_access_allowed?
73
+
74
+ case single_access_allowed_request_types
75
+ when Array
76
+ single_access_allowed_request_types.include?(controller.request_content_type) || single_access_allowed_request_types.include?(:all)
77
+ else
78
+ [:all, :any].include?(single_access_allowed_request_types)
79
+ end
75
80
  end
76
81
 
77
82
  def params_key
@@ -5,10 +5,30 @@ require File.dirname(__FILE__) + "/test_case/mock_logger"
5
5
  require File.dirname(__FILE__) + "/test_case/mock_request"
6
6
 
7
7
  module Authlogic
8
- # This is a collection of methods and classes that help you easily test Authlogic. In fact, I use these same tools
9
- # to test the internals of Authlogic.
8
+ # This module is a collection of methods and classes that help you easily test Authlogic. In fact,
9
+ # I use these same tools to test the internals of Authlogic.
10
10
  #
11
- # Some important things to keep in mind when testing:
11
+ # === The quick and dirty
12
+ #
13
+ # require "authlogic/test_case" # include at the top of test_helper.rb
14
+ # setup :activate_authlogic # run before tests are executed
15
+ # UserSession.create(users(:whomever)) # logs a user in
16
+ #
17
+ # For a more detailed explanation, see below.
18
+ #
19
+ # === Setting up
20
+ #
21
+ # Authlogic comes with some simple testing tools. To get these, you need to first require Authlogic's TestCase. If
22
+ # you are doing this in a rails app, you would require this file at the top of your test_helper.rb file:
23
+ #
24
+ # require "authlogic/test_case"
25
+ #
26
+ # If you are using Test::Unit::TestCase, the standard testing library that comes with ruby, then you can skip this next part.
27
+ # If you are not, you need to include the Authlogic::TestCase into your testing suite as follows:
28
+ #
29
+ # include Authlogic::TestCase
30
+ #
31
+ # Now that everything is ready to go, let's move onto actually testing. Here is the basic idea behind testing:
12
32
  #
13
33
  # Authlogic requires a "connection" to your controller to activate it. In the same manner that ActiveRecord requires a connection to
14
34
  # your database. It can't do anything until it gets connnected. That being said, Authlogic will raise an
@@ -18,30 +38,38 @@ module Authlogic
18
38
  # === Functional tests
19
39
  #
20
40
  # Activating Authlogic isn't a problem here, because making a request will activate Authlogic for you. The problem is
21
- # logging users in so they can access restricted areas. Solvin this is simple, just do this:
41
+ # logging users in so they can access restricted areas. Solving this is simple, just do this:
22
42
  #
23
43
  # setup :activate_authlogic
24
44
  #
25
- # Now log users in using Authlogic:
45
+ # For those of you unfamiliar with TestUnit, the setup method bascially just executes a method before any test is ran.
46
+ # It is essentially "setting up" your tests.
47
+ #
48
+ # Once you have done this, just log users in like usual:
26
49
  #
27
50
  # UserSession.create(users(:whomever))
51
+ # # access my restricted area here
28
52
  #
29
53
  # Do this before you make your request and it will act as if that user is logged in.
30
54
  #
31
55
  # === Integration tests
32
56
  #
33
57
  # Again, just like functional tests, you don't have to do anything. As soon as you make a request, Authlogic will be
34
- # conntected.
58
+ # conntected. If you want to activate Authlogic before making a request follow the same steps described in the
59
+ # "functional tests" section above. It works in the same manner.
35
60
  #
36
61
  # === Unit tests
37
62
  #
38
- # The only time you need to do any trickiness here is if you want to test Authlogic yourself. Maybe you added some custom
39
- # code or methods in your Session class. Maybe you are writing a plugin or a library that extends Authlogic. Whatever it is
40
- # you need to make sure your code is tested and working properly.
63
+ # The only time you need to do any trickiness here is if you want to test Authlogic models. Maybe you added some custom
64
+ # code or methods in your Authlogic models. Maybe you are writing a plugin or a library that extends Authlogic.
41
65
  #
42
- # That being said, in this environment there is no controller. So you need to "fake" Authlogic into
43
- # thinking there is. Don't worry, because the this module takes care of this for you. Just do the following
44
- # in your test's setup and you are good to go:
66
+ # That being said, in this environment there is no controller. So you need to use a "mock" controller. Something
67
+ # that looks like a controller, acts like a controller, but isn't a "real" controller. You are essentially connecting
68
+ # Authlogic to your "mock" controller, then you can test off of the mock controller to make sure everything is functioning
69
+ # properly.
70
+ #
71
+ # I use a mock controller to test Authlogic myself. It's part of the Authlogic library that you can easily use. It's as simple
72
+ # as functional and integration tests. Just do the following:
45
73
  #
46
74
  # setup :activate_authlogic
47
75
  #
@@ -52,13 +80,7 @@ module Authlogic
52
80
  # assert UserSession.create(ben)
53
81
  # assert_equal controller.session["user_credentials"], ben.persistence_token
54
82
  #
55
- # That's it.
56
- #
57
- # === How to use
58
- #
59
- # Just require the file in your test_helper.rb file.
60
- #
61
- # require "authlogic/test_case"
83
+ # See how I am checking that Authlogic is interacting with the controller properly? That's the idea here.
62
84
  module TestCase
63
85
  # Activates authlogic so that you can use it in your tests. You should call this method in your test's setup. Ex:
64
86
  #
@@ -74,5 +96,5 @@ module Authlogic
74
96
  end
75
97
  end
76
98
 
77
- ::Test::Unit::TestCase.send(:include, TestCase)
99
+ ::Test::Unit::TestCase.send(:include, TestCase) if defined?(::Test::Unit::TestCase)
78
100
  end