authlogic 2.0.1 → 2.0.2
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.
- data/CHANGELOG.rdoc +7 -1
- data/lib/authlogic/session/brute_force_protection.rb +20 -8
- data/lib/authlogic/session/magic_columns.rb +7 -4
- data/lib/authlogic/testing/test_unit_helpers.rb +5 -5
- data/lib/authlogic/version.rb +1 -1
- data/test/session_test/brute_force_protection_test.rb +27 -4
- data/test/session_test/magic_columns_test.rb +7 -4
- data/test/session_test/timeout_test.rb +9 -0
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
== 2.0.
|
1
|
+
== 2.0.2
|
2
|
+
|
3
|
+
* Reset failed_login_count if consecutive_failed_logins_limit has been exceed and the failed_login_ban_for has passed.
|
4
|
+
* Update test helpers to use the new configuration scheme.
|
5
|
+
* Fixed issue when logging doesn't update last_request_at, so the next persistence try would fail.
|
6
|
+
|
7
|
+
== 2.0.1 released 2009-3-23
|
2
8
|
|
3
9
|
* Validate length of password.
|
4
10
|
* Dont save sessions with a ! during session maintenance.
|
@@ -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
|
|
@@ -53,24 +54,35 @@ module Authlogic
|
|
53
54
|
# The methods available for an Authlogic::Session::Base object that make up the brute force protection feature.
|
54
55
|
module InstanceMethods
|
55
56
|
private
|
56
|
-
def
|
57
|
+
def exceeded_failed_logins_limit?
|
57
58
|
!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))
|
59
|
+
attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
63
|
-
|
62
|
+
def being_brute_force_protected?
|
63
|
+
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))
|
64
64
|
end
|
65
65
|
|
66
|
-
def
|
67
|
-
|
66
|
+
def reset_failed_login_count?
|
67
|
+
exceeded_failed_logins_limit? && !being_brute_force_protected?
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset_failed_login_count
|
71
|
+
attempted_record.failed_login_count = 0
|
68
72
|
end
|
69
73
|
|
70
74
|
def validate_failed_logins
|
71
75
|
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
76
|
errors.add_to_base(I18n.t('error_messages.consecutive_failed_logins_limit_exceeded', :default => "Consecutive failed logins limit exceeded, account is disabled."))
|
73
77
|
end
|
78
|
+
|
79
|
+
def consecutive_failed_logins_limit
|
80
|
+
self.class.consecutive_failed_logins_limit
|
81
|
+
end
|
82
|
+
|
83
|
+
def failed_login_ban_for
|
84
|
+
self.class.failed_login_ban_for
|
85
|
+
end
|
74
86
|
end
|
75
87
|
end
|
76
88
|
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
|
|
@@ -59,11 +60,13 @@ module Authlogic
|
|
59
60
|
record.current_login_ip = controller.request.remote_ip
|
60
61
|
end
|
61
62
|
end
|
63
|
+
|
64
|
+
def set_last_request_at?
|
65
|
+
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)
|
66
|
+
end
|
62
67
|
|
63
68
|
def set_last_request_at
|
64
|
-
|
65
|
-
record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
|
66
|
-
end
|
69
|
+
record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
|
67
70
|
end
|
68
71
|
|
69
72
|
def last_request_at_threshold
|
@@ -9,21 +9,21 @@ module Authlogic
|
|
9
9
|
# Then you will have the methods below to use in your tests.
|
10
10
|
module TestUnitHelpers
|
11
11
|
private
|
12
|
-
def session_class(record)
|
13
|
-
record.class.
|
12
|
+
def session_class(record)
|
13
|
+
record.class.session_class
|
14
14
|
end
|
15
15
|
|
16
16
|
# Sets the session for a record. This way when you execute a request in your test, session values will be present.
|
17
17
|
def set_session_for(record)
|
18
18
|
session_class = session_class(record)
|
19
|
-
@request.session[session_class.session_key] = record.
|
20
|
-
@request.session["#{session_class.session_key}
|
19
|
+
@request.session[session_class.session_key] = record.persistence_token
|
20
|
+
@request.session["#{session_class.session_key}_#{record.class.primary_key}"] = record.id
|
21
21
|
end
|
22
22
|
|
23
23
|
# Sets the cookie for a record. This way when you execute a request in your test, cookie values will be present.
|
24
24
|
def set_cookie_for(record)
|
25
25
|
session_class = session_class(record)
|
26
|
-
@request.cookies[session_class.cookie_key] = record.
|
26
|
+
@request.cookies[session_class.cookie_key] = record.persistence_token
|
27
27
|
end
|
28
28
|
|
29
29
|
# Sets the HTTP_AUTHORIZATION header for basic HTTP auth. This way when you execute a request in your test that is trying to authenticate
|
data/lib/authlogic/version.rb
CHANGED
@@ -56,20 +56,43 @@ module SessionTest
|
|
56
56
|
|
57
57
|
UserSession.consecutive_failed_logins_limit = 50
|
58
58
|
end
|
59
|
-
|
60
|
-
def
|
59
|
+
|
60
|
+
def test_exceeded_ban_for
|
61
|
+
UserSession.consecutive_failed_logins_limit = 2
|
61
62
|
ben = users(:ben)
|
62
63
|
|
63
64
|
2.times do |i|
|
64
|
-
session = UserSession.new(:login => ben.login, :password => "
|
65
|
+
session = UserSession.new(:login => ben.login, :password => "badpassword1")
|
65
66
|
assert !session.save
|
66
67
|
assert session.errors.on(:password)
|
67
68
|
assert_equal i + 1, ben.reload.failed_login_count
|
68
69
|
end
|
69
|
-
|
70
|
+
|
71
|
+
ActiveRecord::Base.connection.execute("update users set updated_at = '#{1.day.ago.to_s(:db)}' where login = '#{ben.login}'")
|
70
72
|
session = UserSession.new(:login => ben.login, :password => "benrocks")
|
71
73
|
assert session.save
|
72
74
|
assert_equal 0, ben.reload.failed_login_count
|
75
|
+
|
76
|
+
UserSession.consecutive_failed_logins_limit = 50
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_exceeded_ban_and_failed_doesnt_ban_again
|
80
|
+
UserSession.consecutive_failed_logins_limit = 2
|
81
|
+
ben = users(:ben)
|
82
|
+
|
83
|
+
2.times do |i|
|
84
|
+
session = UserSession.new(:login => ben.login, :password => "badpassword1")
|
85
|
+
assert !session.save
|
86
|
+
assert session.errors.on(:password)
|
87
|
+
assert_equal i + 1, ben.reload.failed_login_count
|
88
|
+
end
|
89
|
+
|
90
|
+
ActiveRecord::Base.connection.execute("update users set updated_at = '#{1.day.ago.to_s(:db)}' where login = '#{ben.login}'")
|
91
|
+
session = UserSession.new(:login => ben.login, :password => "badpassword1")
|
92
|
+
assert !session.save
|
93
|
+
assert_equal 1, ben.reload.failed_login_count
|
94
|
+
|
95
|
+
UserSession.consecutive_failed_logins_limit = 50
|
73
96
|
end
|
74
97
|
end
|
75
98
|
end
|
@@ -15,6 +15,8 @@ module SessionTest
|
|
15
15
|
class InstanceMethodsTest < ActiveSupport::TestCase
|
16
16
|
def test_after_persisting_set_last_request_at
|
17
17
|
ben = users(:ben)
|
18
|
+
assert UserSession.create(ben)
|
19
|
+
|
18
20
|
set_cookie_for(ben)
|
19
21
|
old_last_request_at = ben.last_request_at
|
20
22
|
assert UserSession.find
|
@@ -22,7 +24,7 @@ module SessionTest
|
|
22
24
|
assert ben.last_request_at != old_last_request_at
|
23
25
|
end
|
24
26
|
|
25
|
-
def
|
27
|
+
def test_valid_increase_failed_login_count
|
26
28
|
ben = users(:ben)
|
27
29
|
old_failed_login_count = ben.failed_login_count
|
28
30
|
assert !UserSession.create(:login => ben.login, :password => "wrong")
|
@@ -45,10 +47,11 @@ module SessionTest
|
|
45
47
|
old_last_login_ip = ben.last_login_ip
|
46
48
|
old_current_login_ip = ben.current_login_ip
|
47
49
|
|
48
|
-
assert UserSession.create(ben)
|
49
|
-
|
50
|
+
assert UserSession.create(:login => ben.login, :password => "benrocks")
|
51
|
+
|
52
|
+
ben.reload
|
50
53
|
assert_equal old_login_count + 1, ben.login_count
|
51
|
-
assert_equal
|
54
|
+
assert_equal 0, ben.failed_login_count
|
52
55
|
assert_equal old_current_login_at, ben.last_login_at
|
53
56
|
assert ben.current_login_at != old_current_login_at
|
54
57
|
assert_equal old_current_login_ip, ben.last_login_ip
|
@@ -38,6 +38,15 @@ module SessionTest
|
|
38
38
|
|
39
39
|
UserSession.logout_on_timeout = false
|
40
40
|
end
|
41
|
+
|
42
|
+
def test_successful_login
|
43
|
+
UserSession.logout_on_timeout = true
|
44
|
+
ben = users(:ben)
|
45
|
+
assert UserSession.create(:login => ben.login, :password => "benrocks")
|
46
|
+
assert session = UserSession.find
|
47
|
+
assert_equal ben, session.record
|
48
|
+
UserSession.logout_on_timeout = false
|
49
|
+
end
|
41
50
|
end
|
42
51
|
end
|
43
52
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authlogic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-24 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|