authlogic 3.4.6 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.github/triage.md +87 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +127 -0
- data/.rubocop_todo.yml +65 -0
- data/.travis.yml +18 -10
- data/CHANGELOG.md +156 -6
- data/CONTRIBUTING.md +71 -3
- data/Gemfile +2 -2
- data/README.md +386 -0
- data/Rakefile +13 -7
- data/UPGRADING.md +22 -0
- data/authlogic.gemspec +33 -22
- data/lib/authlogic.rb +60 -52
- data/lib/authlogic/acts_as_authentic/base.rb +40 -26
- data/lib/authlogic/acts_as_authentic/email.rb +96 -32
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +36 -12
- data/lib/authlogic/acts_as_authentic/login.rb +114 -49
- data/lib/authlogic/acts_as_authentic/magic_columns.rb +17 -6
- data/lib/authlogic/acts_as_authentic/password.rb +296 -139
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +34 -20
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +20 -24
- data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +68 -23
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +128 -85
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +41 -25
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +8 -8
- data/lib/authlogic/authenticates_many/association.rb +22 -14
- data/lib/authlogic/authenticates_many/base.rb +35 -16
- data/lib/authlogic/config.rb +10 -10
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +40 -12
- data/lib/authlogic/controller_adapters/rack_adapter.rb +15 -8
- data/lib/authlogic/controller_adapters/rails_adapter.rb +42 -22
- data/lib/authlogic/controller_adapters/sinatra_adapter.rb +3 -3
- data/lib/authlogic/crypto_providers.rb +91 -0
- data/lib/authlogic/crypto_providers/aes256.rb +42 -14
- data/lib/authlogic/crypto_providers/bcrypt.rb +35 -20
- data/lib/authlogic/crypto_providers/md5.rb +11 -9
- data/lib/authlogic/crypto_providers/scrypt.rb +26 -13
- data/lib/authlogic/crypto_providers/sha1.rb +14 -8
- data/lib/authlogic/crypto_providers/sha256.rb +16 -12
- data/lib/authlogic/crypto_providers/sha512.rb +8 -24
- data/lib/authlogic/crypto_providers/wordpress.rb +44 -15
- data/lib/authlogic/i18n.rb +33 -20
- data/lib/authlogic/i18n/translator.rb +1 -1
- data/lib/authlogic/random.rb +12 -29
- data/lib/authlogic/regex.rb +59 -27
- data/lib/authlogic/session/activation.rb +36 -23
- data/lib/authlogic/session/active_record_trickery.rb +13 -10
- data/lib/authlogic/session/base.rb +20 -8
- data/lib/authlogic/session/brute_force_protection.rb +87 -56
- data/lib/authlogic/session/callbacks.rb +99 -49
- data/lib/authlogic/session/cookies.rb +128 -59
- data/lib/authlogic/session/existence.rb +29 -19
- data/lib/authlogic/session/foundation.rb +70 -16
- data/lib/authlogic/session/http_auth.rb +39 -31
- data/lib/authlogic/session/id.rb +27 -15
- data/lib/authlogic/session/klass.rb +17 -13
- data/lib/authlogic/session/magic_columns.rb +78 -59
- data/lib/authlogic/session/magic_states.rb +50 -27
- data/lib/authlogic/session/params.rb +79 -50
- data/lib/authlogic/session/password.rb +197 -118
- data/lib/authlogic/session/perishable_token.rb +12 -6
- data/lib/authlogic/session/persistence.rb +20 -14
- data/lib/authlogic/session/priority_record.rb +20 -16
- data/lib/authlogic/session/scopes.rb +63 -33
- data/lib/authlogic/session/session.rb +40 -25
- data/lib/authlogic/session/timeout.rb +51 -34
- data/lib/authlogic/session/unauthorized_record.rb +24 -18
- data/lib/authlogic/session/validation.rb +32 -21
- data/lib/authlogic/test_case.rb +123 -35
- data/lib/authlogic/test_case/mock_controller.rb +14 -13
- data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -5
- data/lib/authlogic/test_case/mock_logger.rb +1 -1
- data/lib/authlogic/test_case/mock_request.rb +9 -4
- data/lib/authlogic/test_case/rails_request_adapter.rb +8 -7
- data/lib/authlogic/version.rb +21 -0
- data/test/acts_as_authentic_test/base_test.rb +1 -1
- data/test/acts_as_authentic_test/email_test.rb +80 -63
- data/test/acts_as_authentic_test/logged_in_status_test.rb +14 -8
- data/test/acts_as_authentic_test/login_test.rb +91 -49
- data/test/acts_as_authentic_test/magic_columns_test.rb +13 -13
- data/test/acts_as_authentic_test/password_test.rb +82 -60
- data/test/acts_as_authentic_test/perishable_token_test.rb +31 -25
- data/test/acts_as_authentic_test/persistence_token_test.rb +9 -5
- data/test/acts_as_authentic_test/restful_authentication_test.rb +18 -9
- data/test/acts_as_authentic_test/session_maintenance_test.rb +86 -22
- data/test/acts_as_authentic_test/single_access_test.rb +15 -15
- data/test/adapter_test.rb +21 -0
- data/test/authenticates_many_test.rb +26 -11
- data/test/config_test.rb +9 -9
- data/test/crypto_provider_test/aes256_test.rb +3 -3
- data/test/crypto_provider_test/bcrypt_test.rb +1 -1
- data/test/crypto_provider_test/scrypt_test.rb +2 -2
- data/test/crypto_provider_test/sha1_test.rb +4 -4
- data/test/crypto_provider_test/sha256_test.rb +2 -2
- data/test/crypto_provider_test/sha512_test.rb +3 -3
- data/test/crypto_provider_test/wordpress_test.rb +24 -0
- data/test/gemfiles/Gemfile.rails-4.2.x +2 -2
- data/test/gemfiles/Gemfile.rails-5.0.x +6 -0
- data/test/gemfiles/Gemfile.rails-5.1.x +6 -0
- data/test/gemfiles/Gemfile.rails-5.2.x +6 -0
- data/test/gemfiles/Gemfile.rails-master +6 -0
- data/test/i18n_test.rb +9 -9
- data/test/libs/affiliate.rb +2 -2
- data/test/libs/company.rb +4 -4
- data/test/libs/employee.rb +2 -2
- data/test/libs/employee_session.rb +1 -1
- data/test/libs/ldaper.rb +1 -1
- data/test/libs/project.rb +1 -1
- data/test/libs/user_session.rb +2 -2
- data/test/random_test.rb +9 -38
- data/test/session_test/activation_test.rb +7 -7
- data/test/session_test/active_record_trickery_test.rb +9 -6
- data/test/session_test/brute_force_protection_test.rb +26 -21
- data/test/session_test/callbacks_test.rb +10 -4
- data/test/session_test/cookies_test.rb +54 -20
- data/test/session_test/existence_test.rb +45 -23
- data/test/session_test/foundation_test.rb +17 -1
- data/test/session_test/http_auth_test.rb +11 -12
- data/test/session_test/id_test.rb +3 -3
- data/test/session_test/klass_test.rb +2 -2
- data/test/session_test/magic_columns_test.rb +15 -17
- data/test/session_test/magic_states_test.rb +17 -19
- data/test/session_test/params_test.rb +26 -20
- data/test/session_test/password_test.rb +11 -12
- data/test/session_test/perishability_test.rb +5 -5
- data/test/session_test/persistence_test.rb +4 -3
- data/test/session_test/scopes_test.rb +15 -9
- data/test/session_test/session_test.rb +7 -6
- data/test/session_test/timeout_test.rb +16 -14
- data/test/session_test/unauthorized_record_test.rb +3 -3
- data/test/session_test/validation_test.rb +5 -5
- data/test/test_helper.rb +115 -49
- metadata +107 -36
- data/README.rdoc +0 -232
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
@@ -1,59 +1,72 @@
|
|
1
|
-
require
|
1
|
+
require "request_store"
|
2
2
|
|
3
3
|
module Authlogic
|
4
4
|
module Session
|
5
|
-
# Activating Authlogic requires that you pass it an
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# Activating Authlogic requires that you pass it an
|
6
|
+
# Authlogic::ControllerAdapters::AbstractAdapter object, or a class that
|
7
|
+
# extends it. This is sort of like a database connection for an ORM library,
|
8
|
+
# Authlogic can't do anything until it is "connected" to a controller. If
|
9
|
+
# you are using a supported framework, Authlogic takes care of this for you.
|
8
10
|
module Activation
|
9
11
|
class NotActivatedError < ::StandardError # :nodoc:
|
10
|
-
def initialize
|
11
|
-
super(
|
12
|
+
def initialize
|
13
|
+
super(
|
14
|
+
"You must activate the Authlogic::Session::Base.controller with " \
|
15
|
+
"a controller object before creating objects"
|
16
|
+
)
|
12
17
|
end
|
13
18
|
end
|
14
|
-
|
19
|
+
|
15
20
|
def self.included(klass)
|
16
21
|
klass.class_eval do
|
17
22
|
extend ClassMethods
|
18
23
|
include InstanceMethods
|
19
24
|
end
|
20
25
|
end
|
21
|
-
|
26
|
+
|
22
27
|
module ClassMethods
|
23
|
-
# Returns true if a controller has been set and can be used properly.
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
28
|
+
# Returns true if a controller has been set and can be used properly.
|
29
|
+
# This MUST be set before anything can be done. Similar to how
|
30
|
+
# ActiveRecord won't allow you to do anything without establishing a DB
|
31
|
+
# connection. In your framework environment this is done for you, but if
|
32
|
+
# you are using Authlogic outside of your framework, you need to assign
|
33
|
+
# a controller object to Authlogic via
|
34
|
+
# Authlogic::Session::Base.controller = obj. See the controller= method
|
35
|
+
# for more information.
|
27
36
|
def activated?
|
28
37
|
!controller.nil?
|
29
38
|
end
|
30
|
-
|
31
|
-
# This accepts a controller object wrapped with the Authlogic controller
|
32
|
-
#
|
33
|
-
#
|
39
|
+
|
40
|
+
# This accepts a controller object wrapped with the Authlogic controller
|
41
|
+
# adapter. The controller adapters close the gap between the different
|
42
|
+
# controllers in each framework. That being said, Authlogic is expecting
|
43
|
+
# your object's class to extend
|
44
|
+
# Authlogic::ControllerAdapters::AbstractAdapter. See
|
45
|
+
# Authlogic::ControllerAdapters for more info.
|
34
46
|
#
|
35
47
|
# Lastly, this is thread safe.
|
36
48
|
def controller=(value)
|
37
49
|
RequestStore.store[:authlogic_controller] = value
|
38
50
|
end
|
39
|
-
|
51
|
+
|
40
52
|
# The current controller object
|
41
53
|
def controller
|
42
54
|
RequestStore.store[:authlogic_controller]
|
43
55
|
end
|
44
56
|
end
|
45
|
-
|
57
|
+
|
46
58
|
module InstanceMethods
|
47
59
|
# Making sure we are activated before we start creating objects
|
48
60
|
def initialize(*args)
|
49
|
-
raise NotActivatedError
|
61
|
+
raise NotActivatedError unless self.class.activated?
|
50
62
|
super
|
51
63
|
end
|
52
|
-
|
64
|
+
|
53
65
|
private
|
54
|
-
|
55
|
-
|
56
|
-
|
66
|
+
|
67
|
+
def controller
|
68
|
+
self.class.controller
|
69
|
+
end
|
57
70
|
end
|
58
71
|
end
|
59
72
|
end
|
@@ -1,16 +1,18 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# Authlogic looks like ActiveRecord, sounds like ActiveRecord, but its not
|
4
|
-
# This is useful for the various rails
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# Authlogic looks like ActiveRecord, sounds like ActiveRecord, but its not
|
4
|
+
# ActiveRecord. That's the goal here. This is useful for the various rails
|
5
|
+
# helper methods such as form_for, error_messages_for, or any method that
|
6
|
+
# expects an ActiveRecord object. The point is to disguise the object as an
|
7
|
+
# ActiveRecord object so we can take advantage of the many ActiveRecord
|
8
|
+
# tools.
|
7
9
|
module ActiveRecordTrickery
|
8
10
|
def self.included(klass)
|
9
11
|
klass.extend ActiveModel::Naming
|
10
12
|
klass.extend ActiveModel::Translation
|
11
13
|
|
12
14
|
# Support ActiveModel::Name#name for Rails versions before 4.0.
|
13
|
-
|
15
|
+
unless klass.model_name.respond_to?(:name)
|
14
16
|
ActiveModel::Name.module_eval do
|
15
17
|
alias_method :name, :to_s
|
16
18
|
end
|
@@ -21,21 +23,22 @@ module Authlogic
|
|
21
23
|
end
|
22
24
|
|
23
25
|
module ClassMethods
|
24
|
-
# How to name the class, works JUST LIKE ActiveRecord, except it uses
|
26
|
+
# How to name the class, works JUST LIKE ActiveRecord, except it uses
|
27
|
+
# the following namespace:
|
25
28
|
#
|
26
29
|
# authlogic.models.user_session
|
27
|
-
def human_name(*
|
28
|
-
I18n.t("models.#{name.underscore}",
|
30
|
+
def human_name(*)
|
31
|
+
I18n.t("models.#{name.underscore}", count: 1, default: name.humanize)
|
29
32
|
end
|
30
33
|
|
31
34
|
def i18n_scope
|
32
35
|
I18n.scope
|
33
36
|
end
|
34
|
-
|
35
37
|
end
|
36
38
|
|
37
39
|
module InstanceMethods
|
38
|
-
# Don't use this yourself, this is to just trick some of the helpers
|
40
|
+
# Don't use this yourself, this is to just trick some of the helpers
|
41
|
+
# since this is the method it calls.
|
39
42
|
def new_record?
|
40
43
|
new_session?
|
41
44
|
end
|
@@ -1,25 +1,37 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session # :nodoc:
|
3
|
-
# This is the
|
4
|
-
#
|
3
|
+
# This is the most important class in Authlogic. You will inherit this class
|
4
|
+
# for your own eg. `UserSession`.
|
5
|
+
#
|
6
|
+
# Code is organized topically. Each topic is represented by a module. So, to
|
7
|
+
# learn about password-based authentication, read the `Password` module.
|
8
|
+
#
|
9
|
+
# It is common for methods (.initialize and #credentials=, for example) to
|
10
|
+
# be implemented in multiple mixins. Those methods will call `super`, so the
|
11
|
+
# order of `include`s here is important.
|
12
|
+
#
|
13
|
+
# Also, to fully understand such a method (like #credentials=) you will need
|
14
|
+
# to mentally combine all of its definitions. This is perhaps the primary
|
15
|
+
# disadvantage of topical organization using modules.
|
5
16
|
class Base
|
6
17
|
include Foundation
|
7
18
|
include Callbacks
|
8
|
-
|
19
|
+
|
9
20
|
# Included first so that the session resets itself to nil
|
10
21
|
include Timeout
|
11
|
-
|
22
|
+
|
12
23
|
# Included in a specific order so they are tried in this order when persisting
|
13
24
|
include Params
|
14
25
|
include Cookies
|
15
26
|
include Session
|
16
27
|
include HttpAuth
|
17
|
-
|
18
|
-
# Included in a specific order so magic states gets
|
28
|
+
|
29
|
+
# Included in a specific order so magic states gets run after a record is found
|
30
|
+
# TODO: What does "magic states gets run" mean? Be specific.
|
19
31
|
include Password
|
20
32
|
include UnauthorizedRecord
|
21
33
|
include MagicStates
|
22
|
-
|
34
|
+
|
23
35
|
include Activation
|
24
36
|
include ActiveRecordTrickery
|
25
37
|
include BruteForceProtection
|
@@ -34,4 +46,4 @@ module Authlogic
|
|
34
46
|
include PriorityRecord
|
35
47
|
end
|
36
48
|
end
|
37
|
-
end
|
49
|
+
end
|
@@ -1,15 +1,21 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# A brute force attacks is executed by hammering a login with as many password
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
3
|
+
# A brute force attacks is executed by hammering a login with as many password
|
4
|
+
# combinations as possible, until one works. A brute force attacked is generally
|
5
|
+
# combated with a slow hashing algorithm such as BCrypt. You can increase the cost,
|
6
|
+
# which makes the hash generation slower, and ultimately increases the time it takes
|
7
|
+
# to execute a brute force attack. Just to put this into perspective, if a hacker was
|
8
|
+
# to gain access to your server and execute a brute force attack locally, meaning
|
9
|
+
# there is no network lag, it would probably take decades to complete. Now throw in
|
10
|
+
# network lag and it would take MUCH longer.
|
8
11
|
#
|
9
|
-
# But for those that are extra paranoid and can't get enough protection, why not stop
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
12
|
+
# But for those that are extra paranoid and can't get enough protection, why not stop
|
13
|
+
# them as soon as you realize something isn't right? That's what this module is all
|
14
|
+
# about. By default the consecutive_failed_logins_limit configuration option is set to
|
15
|
+
# 50, if someone consecutively fails to login after 50 attempts their account will be
|
16
|
+
# suspended. This is a very liberal number and at this point it should be obvious that
|
17
|
+
# something is not right. If you wish to lower this number just set the configuration
|
18
|
+
# to a lower number:
|
13
19
|
#
|
14
20
|
# class UserSession < Authlogic::Session::Base
|
15
21
|
# consecutive_failed_logins_limit 10
|
@@ -19,20 +25,26 @@ module Authlogic
|
|
19
25
|
klass.class_eval do
|
20
26
|
extend Config
|
21
27
|
include InstanceMethods
|
22
|
-
validate :reset_failed_login_count, :
|
23
|
-
validate :validate_failed_logins, :
|
28
|
+
validate :reset_failed_login_count, if: :reset_failed_login_count?
|
29
|
+
validate :validate_failed_logins, if: :being_brute_force_protected?
|
24
30
|
end
|
25
31
|
end
|
26
|
-
|
32
|
+
|
27
33
|
# Configuration for the brute force protection feature.
|
28
34
|
module Config
|
29
|
-
# To help protect from brute force attacks you can set a limit on the
|
30
|
-
# number
|
35
|
+
# To help protect from brute force attacks you can set a limit on the
|
36
|
+
# allowed number of consecutive failed logins. By default this is 50,
|
37
|
+
# this is a very liberal number, and if someone fails to login after 50
|
38
|
+
# tries it should be pretty obvious that it's a machine trying to login
|
39
|
+
# in and very likely a brute force attack.
|
31
40
|
#
|
32
|
-
# In order to enable this field your model MUST have a
|
41
|
+
# In order to enable this field your model MUST have a
|
42
|
+
# failed_login_count (integer) field.
|
33
43
|
#
|
34
|
-
# If you don't know what a brute force attack is, it's when a machine
|
35
|
-
#
|
44
|
+
# If you don't know what a brute force attack is, it's when a machine
|
45
|
+
# tries to login into a system using every combination of character
|
46
|
+
# possible. Thus resulting in possibly millions of attempts to log into
|
47
|
+
# an account.
|
36
48
|
#
|
37
49
|
# * <tt>Default:</tt> 50
|
38
50
|
# * <tt>Accepts:</tt> Integer, set to 0 to disable
|
@@ -40,8 +52,9 @@ module Authlogic
|
|
40
52
|
rw_config(:consecutive_failed_logins_limit, value, 50)
|
41
53
|
end
|
42
54
|
alias_method :consecutive_failed_logins_limit=, :consecutive_failed_logins_limit
|
43
|
-
|
44
|
-
# Once the failed logins limit has been exceed, how long do you want to
|
55
|
+
|
56
|
+
# Once the failed logins limit has been exceed, how long do you want to
|
57
|
+
# ban the user? This can be a temporary or permanent ban.
|
45
58
|
#
|
46
59
|
# * <tt>Default:</tt> 2.hours
|
47
60
|
# * <tt>Accepts:</tt> Fixnum, set to 0 for permanent ban
|
@@ -50,47 +63,65 @@ module Authlogic
|
|
50
63
|
end
|
51
64
|
alias_method :failed_login_ban_for=, :failed_login_ban_for
|
52
65
|
end
|
53
|
-
|
54
|
-
# The methods available for an Authlogic::Session::Base object that make
|
66
|
+
|
67
|
+
# The methods available for an Authlogic::Session::Base object that make
|
68
|
+
# up the brute force protection feature.
|
55
69
|
module InstanceMethods
|
56
|
-
# Returns true when the consecutive_failed_logins_limit has been
|
57
|
-
#
|
58
|
-
#
|
70
|
+
# Returns true when the consecutive_failed_logins_limit has been
|
71
|
+
# exceeded and is being temporarily banned. Notice the word temporary,
|
72
|
+
# the user will not be permanently banned unless you choose to do so
|
73
|
+
# with configuration. By default they will be banned for 2 hours. During
|
74
|
+
# that 2 hour period this method will return true.
|
59
75
|
def being_brute_force_protected?
|
60
|
-
exceeded_failed_logins_limit? &&
|
61
|
-
(
|
76
|
+
exceeded_failed_logins_limit? &&
|
77
|
+
(
|
78
|
+
failed_login_ban_for <= 0 ||
|
79
|
+
attempted_record.respond_to?(:updated_at) &&
|
80
|
+
attempted_record.updated_at >= failed_login_ban_for.seconds.ago
|
81
|
+
)
|
62
82
|
end
|
63
|
-
|
83
|
+
|
64
84
|
private
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
85
|
+
|
86
|
+
def exceeded_failed_logins_limit?
|
87
|
+
!attempted_record.nil? &&
|
88
|
+
attempted_record.respond_to?(:failed_login_count) &&
|
89
|
+
consecutive_failed_logins_limit > 0 &&
|
90
|
+
attempted_record.failed_login_count &&
|
91
|
+
attempted_record.failed_login_count >= consecutive_failed_logins_limit
|
92
|
+
end
|
93
|
+
|
94
|
+
def reset_failed_login_count?
|
95
|
+
exceeded_failed_logins_limit? && !being_brute_force_protected?
|
96
|
+
end
|
97
|
+
|
98
|
+
def reset_failed_login_count
|
99
|
+
attempted_record.failed_login_count = 0
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_failed_logins
|
103
|
+
# Clear all other error messages, as they are irrelevant at this point and can
|
104
|
+
# only provide additional information that is not needed
|
105
|
+
errors.clear
|
106
|
+
errors.add(
|
107
|
+
:base,
|
108
|
+
I18n.t(
|
109
|
+
"error_messages.consecutive_failed_logins_limit_exceeded",
|
110
|
+
default: "Consecutive failed logins limit exceeded, account has been" +
|
111
|
+
(failed_login_ban_for.zero? ? "" : " temporarily") +
|
112
|
+
" disabled."
|
113
|
+
)
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def consecutive_failed_logins_limit
|
118
|
+
self.class.consecutive_failed_logins_limit
|
119
|
+
end
|
120
|
+
|
121
|
+
def failed_login_ban_for
|
122
|
+
self.class.failed_login_ban_for
|
123
|
+
end
|
93
124
|
end
|
94
125
|
end
|
95
126
|
end
|
96
|
-
end
|
127
|
+
end
|
@@ -1,21 +1,24 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# Between these
|
4
|
-
# modify Authlogic's behavior. I will do everything I can to make sure
|
3
|
+
# Between these callbacks and the configuration, this is the contract between me and
|
4
|
+
# you to safely modify Authlogic's behavior. I will do everything I can to make sure
|
5
|
+
# these do not change.
|
5
6
|
#
|
6
|
-
# Check out the sub modules of Authlogic::Session. They are very concise, clear, and
|
7
|
-
# importantly they use the same API that you would use to extend
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# Check out the sub modules of Authlogic::Session. They are very concise, clear, and
|
8
|
+
# to the point. More importantly they use the same API that you would use to extend
|
9
|
+
# Authlogic. That being said, they are great examples of how to extend Authlogic and
|
10
|
+
# add / modify behavior to Authlogic. These modules could easily be pulled out into
|
11
|
+
# their own plugin and become an "add on" without any change.
|
10
12
|
#
|
11
|
-
# Now to the point of this module. Just like in ActiveRecord you have before_save,
|
12
|
-
# You have similar callbacks with Authlogic, see the METHODS
|
13
|
+
# Now to the point of this module. Just like in ActiveRecord you have before_save,
|
14
|
+
# before_validation, etc. You have similar callbacks with Authlogic, see the METHODS
|
15
|
+
# constant below. The order of execution is as follows:
|
13
16
|
#
|
14
17
|
# before_persisting
|
15
18
|
# persist
|
16
19
|
# after_persisting
|
17
20
|
# [save record if record.changed?]
|
18
|
-
#
|
21
|
+
#
|
19
22
|
# before_validation
|
20
23
|
# before_validation_on_create
|
21
24
|
# before_validation_on_update
|
@@ -24,7 +27,7 @@ module Authlogic
|
|
24
27
|
# after_validation_on_create
|
25
28
|
# after_validation
|
26
29
|
# [save record if record.changed?]
|
27
|
-
#
|
30
|
+
#
|
28
31
|
# before_save
|
29
32
|
# before_create
|
30
33
|
# before_update
|
@@ -32,15 +35,16 @@ module Authlogic
|
|
32
35
|
# after_create
|
33
36
|
# after_save
|
34
37
|
# [save record if record.changed?]
|
35
|
-
#
|
38
|
+
#
|
36
39
|
# before_destroy
|
37
40
|
# [save record if record.changed?]
|
38
41
|
# destroy
|
39
42
|
# after_destroy
|
40
43
|
#
|
41
|
-
# Notice the "save record if changed?" lines above. This helps with performance. If
|
42
|
-
# changes to the associated record, there is no need to save the
|
43
|
-
# This allows multiple modules to modify the
|
44
|
+
# Notice the "save record if changed?" lines above. This helps with performance. If
|
45
|
+
# you need to make changes to the associated record, there is no need to save the
|
46
|
+
# record, Authlogic will do it for you. This allows multiple modules to modify the
|
47
|
+
# record and execute as few queries as possible.
|
44
48
|
#
|
45
49
|
# **WARNING**: unlike ActiveRecord, these callbacks must be set up on the class level:
|
46
50
|
#
|
@@ -50,52 +54,98 @@ module Authlogic
|
|
50
54
|
# # ..etc
|
51
55
|
# end
|
52
56
|
#
|
53
|
-
# You can NOT define a "before_validation" method, this is bad practice and does not
|
54
|
-
# to extend properly with multiple extensions. Please ONLY use the
|
57
|
+
# You can NOT define a "before_validation" method, this is bad practice and does not
|
58
|
+
# allow Authlogic to extend properly with multiple extensions. Please ONLY use the
|
59
|
+
# method above.
|
55
60
|
module Callbacks
|
56
|
-
METHODS = [
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
METHODS = %w[
|
62
|
+
before_persisting
|
63
|
+
persist
|
64
|
+
after_persisting
|
65
|
+
before_validation
|
66
|
+
before_validation_on_create
|
67
|
+
before_validation_on_update
|
68
|
+
validate
|
69
|
+
after_validation_on_update
|
70
|
+
after_validation_on_create
|
71
|
+
after_validation
|
72
|
+
before_save
|
73
|
+
before_create
|
74
|
+
before_update
|
75
|
+
after_update
|
76
|
+
after_create
|
77
|
+
after_save
|
78
|
+
before_destroy
|
79
|
+
after_destroy
|
80
|
+
].freeze
|
81
|
+
|
82
|
+
class << self
|
83
|
+
def included(base) #:nodoc:
|
84
|
+
base.send :include, ActiveSupport::Callbacks
|
85
|
+
define_session_callbacks(base)
|
86
|
+
define_session_callback_installation_methods(base)
|
72
87
|
end
|
73
88
|
|
74
|
-
|
75
|
-
|
89
|
+
private
|
90
|
+
|
91
|
+
# Defines the "callback installation methods". Other modules will use
|
92
|
+
# these class methods to install their callbacks. Examples:
|
93
|
+
#
|
94
|
+
# ```
|
95
|
+
# # session/timeout.rb, in `included`
|
96
|
+
# before_persisting :reset_stale_state
|
97
|
+
#
|
98
|
+
# # session/password.rb, in `included`
|
99
|
+
# validate :validate_by_password, if: :authenticating_with_password?
|
100
|
+
# ```
|
101
|
+
def define_session_callback_installation_methods(base)
|
76
102
|
METHODS.each do |method|
|
77
|
-
base.class_eval <<-
|
78
|
-
def self.#{method}(*
|
79
|
-
set_callback
|
103
|
+
base.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
104
|
+
def self.#{method}(*filter_list, &block)
|
105
|
+
set_callback(:#{method}, *filter_list, &block)
|
80
106
|
end
|
81
|
-
|
107
|
+
EOS
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Defines session life cycle events that support callbacks.
|
112
|
+
def define_session_callbacks(base)
|
113
|
+
if Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new("5")
|
114
|
+
base.define_callbacks(
|
115
|
+
*METHODS,
|
116
|
+
terminator: ->(_target, result_lambda) { result_lambda.call == false }
|
117
|
+
)
|
118
|
+
base.define_callbacks(
|
119
|
+
"persist",
|
120
|
+
terminator: ->(_target, result_lambda) { result_lambda.call == true }
|
121
|
+
)
|
122
|
+
else
|
123
|
+
base.define_callbacks(
|
124
|
+
*METHODS,
|
125
|
+
terminator: ->(_target, result) { result == false }
|
126
|
+
)
|
127
|
+
base.define_callbacks(
|
128
|
+
"persist",
|
129
|
+
terminator: ->(_target, result) { result == true }
|
130
|
+
)
|
82
131
|
end
|
83
132
|
end
|
84
133
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
134
|
+
|
135
|
+
METHODS.each do |method|
|
136
|
+
class_eval(
|
137
|
+
<<-EOS, __FILE__, __LINE__ + 1
|
89
138
|
def #{method}
|
90
139
|
run_callbacks(:#{method})
|
91
140
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
141
|
+
EOS
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
def save_record(alternate_record = nil)
|
146
|
+
r = alternate_record || record
|
147
|
+
r.save_without_session_maintenance(validate: false) if r && r.changed? && !r.readonly?
|
148
|
+
end
|
99
149
|
end
|
100
150
|
end
|
101
151
|
end
|