authlogic 2.0.1 → 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 +99 -1
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.rdoc +136 -159
  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 +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 +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 +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 +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
@@ -2,8 +2,8 @@ require "digest/sha1"
2
2
 
3
3
  module Authlogic
4
4
  module CryptoProviders
5
- # This class was made for the users transitioning from restful_authentication. I highly discourage using this crypto provider as it inferior to your other options.
6
- # Please use any other provider offered by Authlogic.
5
+ # This class was made for the users transitioning from restful_authentication. I highly discourage using this
6
+ # crypto provider as it inferior to your other options. Please use any other provider offered by Authlogic.
7
7
  class Sha1
8
8
  class << self
9
9
  def join_token
@@ -29,7 +29,7 @@ module Authlogic
29
29
  # authlogic:
30
30
  # error_messages:
31
31
  # login_blank: can not be blank
32
- # login_not_found: does not exist
32
+ # login_not_found: is not valid
33
33
  # login_invalid: should use only letters, numbers, spaces, and .-_@ please.
34
34
  # consecutive_failed_logins_limit_exceeded: Consecutive failed logins limit exceeded, account is disabled.
35
35
  # email_invalid: should look like an email address.
@@ -39,6 +39,14 @@ module Authlogic
39
39
  # not_confirmed: Your account is not confirmed
40
40
  # not_approved: Your account is not approved
41
41
  # no_authentication_details: You did not provide any details for authentication.
42
+ # models:
43
+ # user_session: UserSession (or whatever name you are using)
44
+ # attributes:
45
+ # user_session: (or whatever name you are using)
46
+ # login: login
47
+ # email: email
48
+ # password: password
49
+ # remember_me: remember me
42
50
  class I18n
43
51
  class << self
44
52
  # All message translation is passed to this method. The first argument is the key for the message. The second is options, see the rails I18n library for a list of options used.
@@ -49,6 +57,7 @@ module Authlogic
49
57
  options[:default]
50
58
  end
51
59
  end
60
+ alias_method :translate, :t
52
61
  end
53
62
  end
54
63
  end
@@ -0,0 +1,25 @@
1
+ module Authlogic
2
+ # This is a module the contains regular expressions used throughout Authlogic. The point of extracting
3
+ # them out into their own module is to make them easily available to you for other uses. Ex:
4
+ #
5
+ # validates_format_of :my_email_field, :with => Authlogic::Regex.email
6
+ module Regex
7
+ # A general email regular expression. It allows top level domains (TLD) to be from 2 - 4 in length, any
8
+ # TLD longer than that must be manually specified. The decisions behind this regular expression were made
9
+ # by reading this website: http://www.regular-expressions.info/email.html, which is an excellent resource
10
+ # for regular expressions.
11
+ def self.email
12
+ return @email_regex if @email_regex
13
+ email_name_regex = '[A-Z0-9_\.%\+\-]+'
14
+ domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
15
+ domain_tld_regex = '(?:[A-Z]{2,4}|museum|travel)'
16
+ @email_regex = /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
17
+ end
18
+
19
+ # A simple regular expression that only allows for letters, numbers, spaces, and .-_@. Just a standard login / username
20
+ # regular expression.
21
+ def self.login
22
+ /\A\w[\w\.+\-_@ ]+\z/
23
+ end
24
+ end
25
+ end
@@ -29,6 +29,8 @@ module Authlogic
29
29
  # This accepts a controller object wrapped with the Authlogic controller adapter. The controller adapters close the gap
30
30
  # between the different controllers in each framework. That being said, Authlogic is expecting your object's class to
31
31
  # extend Authlogic::ControllerAdapters::AbstractAdapter. See Authlogic::ControllerAdapters for more info.
32
+ #
33
+ # Lastly, this is thread safe.
32
34
  def controller=(value)
33
35
  Thread.current[:authlogic_controller] = value
34
36
  end
@@ -11,12 +11,21 @@ module Authlogic
11
11
  end
12
12
 
13
13
  module ClassMethods
14
- def human_attribute_name(*args)
15
- klass.human_attribute_name(*args)
14
+ # How to name the attributes of Authlogic, works JUST LIKE ActiveRecord, but instead it uses the following
15
+ # namespace:
16
+ #
17
+ # authlogic.attributes.user_session.login
18
+ def human_attribute_name(attribute_key_name, options = {})
19
+ options[:count] ||= 1
20
+ options[:default] ||= attribute_key_name.to_s.humanize
21
+ I18n.t("attributes.#{name.underscore}.#{attribute_key_name}", options)
16
22
  end
17
23
 
24
+ # How to name the class, works JUST LIKE ActiveRecord, except it uses the following namespace:
25
+ #
26
+ # authlogic.models.user_session
18
27
  def human_name(*args)
19
- klass.human_name(*args)
28
+ I18n.t("models.#{name.underscore}", {:count => 1, :default => name.humanize})
20
29
  end
21
30
 
22
31
  # For rails < 2.3, mispelled
@@ -31,6 +40,7 @@ module Authlogic
31
40
  end
32
41
 
33
42
  module InstanceMethods
43
+ # Don't use this yourself, this is to just trick some of the helpers since this is the method it calls.
34
44
  def new_record?
35
45
  new_session?
36
46
  end
@@ -19,7 +19,8 @@ module Authlogic
19
19
  klass.class_eval do
20
20
  extend Config
21
21
  include InstanceMethods
22
- validate :validate_failed_logins, :if => :protect_from_brute_force_attacks?
22
+ validate :reset_failed_login_count, :if => :reset_failed_login_count?
23
+ validate :validate_failed_logins, :if => :being_brute_force_protected?
23
24
  end
24
25
  end
25
26
 
@@ -36,7 +37,7 @@ module Authlogic
36
37
  # * <tt>Default:</tt> 50
37
38
  # * <tt>Accepts:</tt> Integer, set to 0 to disable
38
39
  def consecutive_failed_logins_limit(value = nil)
39
- config(:consecutive_failed_logins_limit, value, 50)
40
+ rw_config(:consecutive_failed_logins_limit, value, 50)
40
41
  end
41
42
  alias_method :consecutive_failed_logins_limit=, :consecutive_failed_logins_limit
42
43
 
@@ -45,31 +46,45 @@ module Authlogic
45
46
  # * <tt>Default:</tt> 2.hours
46
47
  # * <tt>Accepts:</tt> Fixnum, set to 0 for permanent ban
47
48
  def failed_login_ban_for(value = nil)
48
- config(:failed_login_ban_for, (!value.nil? && value) || value, 2.hours.to_i)
49
+ rw_config(:failed_login_ban_for, (!value.nil? && value) || value, 2.hours.to_i)
49
50
  end
50
51
  alias_method :failed_login_ban_for=, :failed_login_ban_for
51
52
  end
52
53
 
53
54
  # The methods available for an Authlogic::Session::Base object that make up the brute force protection feature.
54
55
  module InstanceMethods
56
+ # Returns true when the consecutive_failed_logins_limit has been exceeded and is being temporarily banned.
57
+ # Notice the word temporary, the user will not be permanently banned unless you choose to do so with configuration.
58
+ # By default they will be banned for 2 hours. During that 2 hour period this method will return true.
59
+ def being_brute_force_protected?
60
+ exceeded_failed_logins_limit? && (failed_login_ban_for <= 0 || (attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
61
+ end
62
+
55
63
  private
56
- def protect_from_brute_force_attacks?
64
+ def exceeded_failed_logins_limit?
57
65
  !attempted_record.nil? && attempted_record.respond_to?(:failed_login_count) && consecutive_failed_logins_limit > 0 &&
58
- attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit &&
59
- (failed_login_ban_for <= 0 || (attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
66
+ attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
60
67
  end
61
68
 
62
- def consecutive_failed_logins_limit
63
- self.class.consecutive_failed_logins_limit
69
+ def reset_failed_login_count?
70
+ exceeded_failed_logins_limit? && !being_brute_force_protected?
64
71
  end
65
72
 
66
- def failed_login_ban_for
67
- self.class.failed_login_ban_for
73
+ def reset_failed_login_count
74
+ attempted_record.failed_login_count = 0
68
75
  end
69
76
 
70
77
  def validate_failed_logins
71
78
  errors.clear # Clear all other error messages, as they are irrelevant at this point and can only provide additional information that is not needed
72
- errors.add_to_base(I18n.t('error_messages.consecutive_failed_logins_limit_exceeded', :default => "Consecutive failed logins limit exceeded, account is disabled."))
79
+ errors.add(:base, I18n.t('error_messages.consecutive_failed_logins_limit_exceeded', :default => "Consecutive failed logins limit exceeded, account is disabled."))
80
+ end
81
+
82
+ def consecutive_failed_logins_limit
83
+ self.class.consecutive_failed_logins_limit
84
+ end
85
+
86
+ def failed_login_ban_for
87
+ self.class.failed_login_ban_for
73
88
  end
74
89
  end
75
90
  end
@@ -1,8 +1,15 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Just like in ActiveRecord you have before_save, before_validation, etc. You have similar callbacks with Authlogic, see the METHODS constant below. The order of execution is as follows:
3
+ # Between these callsbacks and the configuration, this is the contract between me and you to safely
4
+ # modify Authlogic's behavior. I will do everything I can to make sure these do not change.
4
5
  #
5
- # Here is the order they execute
6
+ # Check out the sub modules of Authlogic::Session. They are very concise, clear, and to the point. More
7
+ # importantly they use the same API that you would use to extend Authlogic. That being said, they are great
8
+ # examples of how to extend Authlogic and add / modify behavior to Authlogic. These modules could easily be pulled out
9
+ # into their own plugin and become an "add on" without any change.
10
+ #
11
+ # Now to the point of this module. Just like in ActiveRecord you have before_save, before_validation, etc.
12
+ # You have similar callbacks with Authlogic, see the METHODS constant below. The order of execution is as follows:
6
13
  #
7
14
  # before_persisting
8
15
  # persist
@@ -27,11 +34,13 @@ module Authlogic
27
34
  # [save record if record.changed?]
28
35
  #
29
36
  # before_destroy
37
+ # [save record if record.changed?]
30
38
  # destroy
31
39
  # after_destroy
32
40
  #
33
- # Notice the "save record if changed?" lines above. This helps with performance. If you need to make changes to the associated record, there is no need to save the record, Authlogic will do it for you.
34
- # This allow multiple modules to modify the record and execute as few queries as possible.
41
+ # Notice the "save record if changed?" lines above. This helps with performance. If you need to make
42
+ # changes to the associated record, there is no need to save the record, Authlogic will do it for you.
43
+ # This allows multiple modules to modify the record and execute as few queries as possible.
35
44
  #
36
45
  # **WARNING**: unlike ActiveRecord, these callbacks must be set up on the class level:
37
46
  #
@@ -41,7 +50,8 @@ module Authlogic
41
50
  # # ..etc
42
51
  # end
43
52
  #
44
- # You can NOT define a "before_validation" method, this is bad practice and does not allow Authlogic to extend properly with multiple extensions. Please ONLY use the method above.
53
+ # You can NOT define a "before_validation" method, this is bad practice and does not allow Authlogic
54
+ # to extend properly with multiple extensions. Please ONLY use the method above.
45
55
  module Callbacks
46
56
  METHODS = [
47
57
  "before_persisting", "persist", "after_persisting",
@@ -70,7 +80,7 @@ module Authlogic
70
80
 
71
81
  def save_record(alternate_record = nil)
72
82
  r = alternate_record || record
73
- r.save_without_session_maintenance(false) if r && r.changed?
83
+ r.save_without_session_maintenance(false) if r && r.changed? && !r.readonly?
74
84
  end
75
85
  end
76
86
  end
@@ -1,6 +1,6 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Handles all authentication that deals with cookies, such as persisting a session and saving / destroying a session.
3
+ # Handles all authentication that deals with cookies, such as persisting, saving, and destroying.
4
4
  module Cookies
5
5
  def self.included(klass)
6
6
  klass.class_eval do
@@ -26,7 +26,7 @@ module Authlogic
26
26
  # * <tt>Default:</tt> "#{klass_name.underscore}_credentials"
27
27
  # * <tt>Accepts:</tt> String
28
28
  def cookie_key(value = nil)
29
- config(:cookie_key, value, "#{klass_name.underscore}_credentials")
29
+ rw_config(:cookie_key, value, "#{klass_name.underscore}_credentials")
30
30
  end
31
31
  alias_method :cookie_key=, :cookie_key
32
32
 
@@ -35,7 +35,7 @@ module Authlogic
35
35
  # * <tt>Default:</tt> false
36
36
  # * <tt>Accepts:</tt> Boolean
37
37
  def remember_me(value = nil)
38
- config(:remember_me, value, false)
38
+ rw_config(:remember_me, value, false)
39
39
  end
40
40
  alias_method :remember_me=, :remember_me
41
41
 
@@ -44,13 +44,14 @@ module Authlogic
44
44
  # * <tt>Default:</tt> 3.months
45
45
  # * <tt>Accepts:</tt> Integer, length of time in seconds, such as 60 or 3.months
46
46
  def remember_me_for(value = :_read)
47
- config(:remember_me_for, value, 3.months, :_read)
47
+ rw_config(:remember_me_for, value, 3.months, :_read)
48
48
  end
49
49
  alias_method :remember_me_for=, :remember_me_for
50
50
  end
51
51
 
52
52
  # The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
53
- module InstanceMethods
53
+ module InstanceMethods
54
+ # Allows you to set the remember_me option when passing credentials.
54
55
  def credentials=(value)
55
56
  super
56
57
  values = value.is_a?(Array) ? value : [value]
@@ -63,7 +64,8 @@ module Authlogic
63
64
  end
64
65
  end
65
66
 
66
- def remember_me # :nodoc:
67
+ # Is the cookie going to expire after the session is over, or will it stick around?
68
+ def remember_me
67
69
  return @remember_me if defined?(@remember_me)
68
70
  @remember_me = self.class.remember_me
69
71
  end
@@ -73,7 +75,7 @@ module Authlogic
73
75
  @remember_me = value
74
76
  end
75
77
 
76
- # Allows users to be remembered via a cookie.
78
+ # See remember_me
77
79
  def remember_me?
78
80
  remember_me == true || remember_me == "true" || remember_me == "1"
79
81
  end
@@ -96,13 +98,15 @@ module Authlogic
96
98
  end
97
99
 
98
100
  def cookie_credentials
99
- controller.cookies[cookie_key]
101
+ controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::")
100
102
  end
101
103
 
102
104
  # Tries to validate the session from information in the cookie
103
105
  def persist_by_cookie
104
- if cookie_credentials
105
- self.unauthorized_record = search_for_record("find_by_persistence_token", cookie_credentials)
106
+ persistence_token, record_id = cookie_credentials
107
+ if !persistence_token.nil?
108
+ record = record_id.nil? ? search_for_record("find_by_persistence_token", persistence_token) : search_for_record("find_by_#{klass.primary_key}", record_id)
109
+ self.unauthorized_record = record if record && record.persistence_token == persistence_token
106
110
  valid?
107
111
  else
108
112
  false
@@ -111,7 +115,7 @@ module Authlogic
111
115
 
112
116
  def save_cookie
113
117
  controller.cookies[cookie_key] = {
114
- :value => record.persistence_token,
118
+ :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}",
115
119
  :expires => remember_me_until,
116
120
  :domain => controller.cookie_domain
117
121
  }
@@ -20,7 +20,7 @@ module Authlogic
20
20
  # A convenince method. The same as:
21
21
  #
22
22
  # session = UserSession.new(*args)
23
- # session.create
23
+ # session.save
24
24
  #
25
25
  # Instead you can do:
26
26
  #
@@ -42,13 +42,15 @@ module Authlogic
42
42
  # the user to authenticate again if it is needed.
43
43
  def destroy
44
44
  before_destroy
45
+ save_record
45
46
  errors.clear
46
47
  @record = nil
47
48
  after_destroy
48
49
  true
49
50
  end
50
51
 
51
- # Returns true if the session has not been saved yet.
52
+ # Returns true if the session is new, meaning no action has been taken on it and a successful save
53
+ # has not taken place.
52
54
  def new_session?
53
55
  new_session != false
54
56
  end
@@ -13,7 +13,7 @@ module Authlogic
13
13
 
14
14
  module ClassMethods
15
15
  private
16
- def config(key, value, default_value = nil, read_value = nil)
16
+ def rw_config(key, value, default_value = nil, read_value = nil)
17
17
  if value == read_value
18
18
  return read_inheritable_attribute(key) if inheritable_attributes.include?(key)
19
19
  write_inheritable_attribute(key, default_value)
@@ -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
+ rw_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
@@ -15,9 +15,10 @@ module Authlogic
15
15
  klass.class_eval do
16
16
  extend Config
17
17
  include InstanceMethods
18
- after_persisting :set_last_request_at
18
+ after_persisting :set_last_request_at, :if => :set_last_request_at?
19
19
  validate :increase_failed_login_count
20
20
  before_save :update_info
21
+ before_save :set_last_request_at, :if => :set_last_request_at?
21
22
  end
22
23
  end
23
24
 
@@ -30,7 +31,7 @@ module Authlogic
30
31
  # * <tt>Default:</tt> 0
31
32
  # * <tt>Accepts:</tt> integer representing time in seconds
32
33
  def last_request_at_threshold(value = nil)
33
- config(:last_request_at_threshold, value, 0)
34
+ rw_config(:last_request_at_threshold, value, 0)
34
35
  end
35
36
  alias_method :last_request_at_threshold=, :last_request_at_threshold
36
37
  end
@@ -39,7 +40,7 @@ module Authlogic
39
40
  module InstanceMethods
40
41
  private
41
42
  def increase_failed_login_count
42
- if errors.on(password_field) && attempted_record.respond_to?(:failed_login_count)
43
+ if invalid_password? && attempted_record.respond_to?(:failed_login_count)
43
44
  attempted_record.failed_login_count ||= 0
44
45
  attempted_record.failed_login_count += 1
45
46
  end
@@ -59,11 +60,29 @@ module Authlogic
59
60
  record.current_login_ip = controller.request.remote_ip
60
61
  end
61
62
  end
63
+
64
+ # This method lets authlogic know whether it should allow the last_request_at field to be updated
65
+ # with the current time (Time.now). One thing to note here is that it also checks for the existence of a
66
+ # last_request_update_allowed? method in your controller. This allows you to control this method pragmatically
67
+ # in your controller.
68
+ #
69
+ # For example, what if you had a javascript function that polled the server updating how much time is left in their
70
+ # session before it times out. Obviously you would want to ignore this request, because then the user would never time out.
71
+ # So you can do something like this in your controller:
72
+ #
73
+ # def last_request_update_allowed?
74
+ # action_name =! "update_session_time_left"
75
+ # end
76
+ #
77
+ # You can do whatever you want with that method.
78
+ def set_last_request_at? # :doc:
79
+ return false if !record || !klass.column_names.include?("last_request_at")
80
+ return controller.last_request_update_allowed? if controller.responds_to_last_request_update_allowed?
81
+ record.last_request_at.blank? || last_request_at_threshold.to_i.seconds.ago >= record.last_request_at
82
+ end
62
83
 
63
84
  def set_last_request_at
64
- if record && record.class.column_names.include?("last_request_at") && (record.last_request_at.blank? || last_request_at_threshold.to_i.seconds.ago >= record.last_request_at)
65
- record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
66
- end
85
+ record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
67
86
  end
68
87
 
69
88
  def last_request_at_threshold
@@ -17,7 +17,7 @@ module Authlogic
17
17
  klass.class_eval do
18
18
  extend Config
19
19
  include InstanceMethods
20
- validate :validate_magic_states
20
+ validate :validate_magic_states, :unless => :disable_magic_states?
21
21
  end
22
22
  end
23
23
 
@@ -30,7 +30,7 @@ module Authlogic
30
30
  # * <tt>Default:</tt> false
31
31
  # * <tt>Accepts:</tt> Boolean
32
32
  def disable_magic_states(value = nil)
33
- config(:disable_magic_states, value, false)
33
+ rw_config(:disable_magic_states, value, false)
34
34
  end
35
35
  alias_method :disable_magic_states=, :disable_magic_states
36
36
  end
@@ -43,10 +43,10 @@ module Authlogic
43
43
  end
44
44
 
45
45
  def validate_magic_states
46
- return true if disable_magic_states? || attempted_record.nil?
46
+ return true if attempted_record.nil?
47
47
  [:active, :approved, :confirmed].each do |required_status|
48
48
  if attempted_record.respond_to?("#{required_status}?") && !attempted_record.send("#{required_status}?")
49
- errors.add_to_base(I18n.t("error_messages.not_#{required_status}", :default => "Your account is not #{required_status}"))
49
+ errors.add(:base, I18n.t("error_messages.not_#{required_status}", :default => "Your account is not #{required_status}"))
50
50
  return false
51
51
  end
52
52
  end
@@ -43,17 +43,17 @@ module Authlogic
43
43
  # * <tt>Default:</tt> cookie_key
44
44
  # * <tt>Accepts:</tt> String
45
45
  def params_key(value = nil)
46
- config(:params_key, value, cookie_key)
46
+ rw_config(:params_key, value, cookie_key)
47
47
  end
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
- config(:single_access_allowed_request_types, value, ["application/rss+xml", "application/atom+xml"])
56
+ rw_config(:single_access_allowed_request_types, value, ["application/rss+xml", "application/atom+xml"])
57
57
  end
58
58
  alias_method :single_access_allowed_request_types=, :single_access_allowed_request_types
59
59
  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