authlogic 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +9 -0
- data/CHANGELOG.rdoc +107 -2
- data/{MIT-LICENSE → LICENSE} +1 -1
- data/README.rdoc +136 -159
- data/Rakefile +47 -18
- data/VERSION.yml +4 -0
- data/authlogic.gemspec +205 -0
- data/init.rb +1 -1
- data/lib/authlogic/acts_as_authentic/base.rb +33 -12
- data/lib/authlogic/acts_as_authentic/email.rb +47 -14
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +20 -14
- data/lib/authlogic/acts_as_authentic/login.rb +84 -8
- data/lib/authlogic/acts_as_authentic/password.rb +216 -87
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +9 -4
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +4 -2
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +5 -4
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +17 -5
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -3
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +2 -2
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +13 -1
- data/lib/authlogic/controller_adapters/rails_adapter.rb +1 -1
- data/lib/authlogic/crypto_providers/aes256.rb +2 -2
- data/lib/authlogic/crypto_providers/bcrypt.rb +3 -1
- data/lib/authlogic/crypto_providers/sha1.rb +2 -2
- data/lib/authlogic/i18n.rb +10 -1
- data/lib/authlogic/regex.rb +25 -0
- data/lib/authlogic/session/activation.rb +2 -0
- data/lib/authlogic/session/active_record_trickery.rb +19 -3
- data/lib/authlogic/session/brute_force_protection.rb +26 -11
- data/lib/authlogic/session/callbacks.rb +16 -6
- data/lib/authlogic/session/cookies.rb +15 -11
- data/lib/authlogic/session/existence.rb +4 -2
- data/lib/authlogic/session/foundation.rb +1 -1
- data/lib/authlogic/session/http_auth.rb +46 -11
- data/lib/authlogic/session/magic_columns.rb +25 -6
- data/lib/authlogic/session/magic_states.rb +4 -4
- data/lib/authlogic/session/params.rb +14 -9
- data/lib/authlogic/session/password.rb +86 -24
- data/lib/authlogic/session/priority_record.rb +1 -1
- data/lib/authlogic/session/session.rb +2 -2
- data/lib/authlogic/session/timeout.rb +1 -1
- data/lib/authlogic/session/validation.rb +9 -5
- data/lib/authlogic/test_case/mock_controller.rb +45 -0
- data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -0
- data/lib/authlogic/test_case/mock_logger.rb +10 -0
- data/lib/authlogic/test_case/mock_request.rb +19 -0
- data/lib/authlogic/test_case/rails_request_adapter.rb +30 -0
- data/lib/authlogic/test_case.rb +114 -0
- data/lib/authlogic.rb +1 -1
- data/rails/init.rb +1 -0
- data/shoulda_macros/authlogic.rb +2 -1
- data/test/acts_as_authentic_test/base_test.rb +6 -0
- data/test/acts_as_authentic_test/email_test.rb +25 -7
- data/test/acts_as_authentic_test/login_test.rb +37 -7
- data/test/acts_as_authentic_test/magic_columns_test.rb +4 -4
- data/test/acts_as_authentic_test/password_test.rb +64 -40
- data/test/acts_as_authentic_test/perishable_token_test.rb +36 -2
- data/test/acts_as_authentic_test/restful_authentication_test.rb +40 -0
- data/test/acts_as_authentic_test/session_maintenance_test.rb +34 -18
- data/test/acts_as_authentic_test/single_access_test.rb +6 -1
- data/test/libs/affiliate.rb +7 -0
- data/test/libs/ldaper.rb +3 -0
- data/test/random_test.rb +2 -2
- data/test/session_test/activation_test.rb +1 -1
- data/test/session_test/active_record_trickery_test.rb +2 -1
- data/test/session_test/brute_force_protection_test.rb +34 -9
- data/test/session_test/cookies_test.rb +3 -3
- data/test/session_test/http_auth_test.rb +20 -8
- data/test/session_test/magic_columns_test.rb +7 -4
- data/test/session_test/magic_states_test.rb +3 -3
- data/test/session_test/params_test.rb +4 -4
- data/test/session_test/password_test.rb +8 -0
- data/test/session_test/session_test.rb +9 -9
- data/test/session_test/timeout_test.rb +10 -1
- data/test/test_helper.rb +46 -24
- metadata +38 -27
- data/Manifest.txt +0 -111
- data/lib/authlogic/testing/test_unit_helpers.rb +0 -39
- data/lib/authlogic/version.rb +0 -56
- data/test/libs/mock_controller.rb +0 -35
- data/test/libs/mock_cookie_jar.rb +0 -10
- data/test/libs/mock_request.rb +0 -5
|
@@ -35,7 +35,9 @@ module Authlogic
|
|
|
35
35
|
#
|
|
36
36
|
# Tell acts_as_authentic to use it:
|
|
37
37
|
#
|
|
38
|
-
# acts_as_authentic
|
|
38
|
+
# acts_as_authentic do |c|
|
|
39
|
+
# c.crypto_provider = Authlogic::CryptoProviders::BCrypt
|
|
40
|
+
# end
|
|
39
41
|
#
|
|
40
42
|
# You are good to go!
|
|
41
43
|
class BCrypt
|
|
@@ -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
|
|
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
|
data/lib/authlogic/i18n.rb
CHANGED
|
@@ -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:
|
|
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,20 +11,36 @@ module Authlogic
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
module ClassMethods
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
28
|
+
I18n.t("models.#{name.underscore}", {:count => 1, :default => name.humanize})
|
|
20
29
|
end
|
|
21
30
|
|
|
31
|
+
# For rails < 2.3, mispelled
|
|
22
32
|
def self_and_descendents_from_active_record
|
|
23
33
|
[self]
|
|
24
34
|
end
|
|
35
|
+
|
|
36
|
+
# For Rails >2.3, fix mispelling
|
|
37
|
+
def self_and_descendants_from_active_record
|
|
38
|
+
[self]
|
|
39
|
+
end
|
|
25
40
|
end
|
|
26
41
|
|
|
27
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.
|
|
28
44
|
def new_record?
|
|
29
45
|
new_session?
|
|
30
46
|
end
|
|
@@ -19,7 +19,8 @@ module Authlogic
|
|
|
19
19
|
klass.class_eval do
|
|
20
20
|
extend Config
|
|
21
21
|
include InstanceMethods
|
|
22
|
-
validate :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
63
|
-
|
|
69
|
+
def reset_failed_login_count?
|
|
70
|
+
exceeded_failed_logins_limit? && !being_brute_force_protected?
|
|
64
71
|
end
|
|
65
72
|
|
|
66
|
-
def
|
|
67
|
-
|
|
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.
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
34
|
-
#
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
105
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
+
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|