authlogic 3.4.6 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +13 -0
  3. data/.github/triage.md +87 -0
  4. data/.gitignore +4 -0
  5. data/.rubocop.yml +127 -0
  6. data/.rubocop_todo.yml +65 -0
  7. data/.travis.yml +18 -10
  8. data/CHANGELOG.md +156 -6
  9. data/CONTRIBUTING.md +71 -3
  10. data/Gemfile +2 -2
  11. data/README.md +386 -0
  12. data/Rakefile +13 -7
  13. data/UPGRADING.md +22 -0
  14. data/authlogic.gemspec +33 -22
  15. data/lib/authlogic.rb +60 -52
  16. data/lib/authlogic/acts_as_authentic/base.rb +40 -26
  17. data/lib/authlogic/acts_as_authentic/email.rb +96 -32
  18. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +36 -12
  19. data/lib/authlogic/acts_as_authentic/login.rb +114 -49
  20. data/lib/authlogic/acts_as_authentic/magic_columns.rb +17 -6
  21. data/lib/authlogic/acts_as_authentic/password.rb +296 -139
  22. data/lib/authlogic/acts_as_authentic/perishable_token.rb +34 -20
  23. data/lib/authlogic/acts_as_authentic/persistence_token.rb +20 -24
  24. data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
  25. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +68 -23
  26. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +128 -85
  27. data/lib/authlogic/acts_as_authentic/single_access_token.rb +41 -25
  28. data/lib/authlogic/acts_as_authentic/validations_scope.rb +8 -8
  29. data/lib/authlogic/authenticates_many/association.rb +22 -14
  30. data/lib/authlogic/authenticates_many/base.rb +35 -16
  31. data/lib/authlogic/config.rb +10 -10
  32. data/lib/authlogic/controller_adapters/abstract_adapter.rb +40 -12
  33. data/lib/authlogic/controller_adapters/rack_adapter.rb +15 -8
  34. data/lib/authlogic/controller_adapters/rails_adapter.rb +42 -22
  35. data/lib/authlogic/controller_adapters/sinatra_adapter.rb +3 -3
  36. data/lib/authlogic/crypto_providers.rb +91 -0
  37. data/lib/authlogic/crypto_providers/aes256.rb +42 -14
  38. data/lib/authlogic/crypto_providers/bcrypt.rb +35 -20
  39. data/lib/authlogic/crypto_providers/md5.rb +11 -9
  40. data/lib/authlogic/crypto_providers/scrypt.rb +26 -13
  41. data/lib/authlogic/crypto_providers/sha1.rb +14 -8
  42. data/lib/authlogic/crypto_providers/sha256.rb +16 -12
  43. data/lib/authlogic/crypto_providers/sha512.rb +8 -24
  44. data/lib/authlogic/crypto_providers/wordpress.rb +44 -15
  45. data/lib/authlogic/i18n.rb +33 -20
  46. data/lib/authlogic/i18n/translator.rb +1 -1
  47. data/lib/authlogic/random.rb +12 -29
  48. data/lib/authlogic/regex.rb +59 -27
  49. data/lib/authlogic/session/activation.rb +36 -23
  50. data/lib/authlogic/session/active_record_trickery.rb +13 -10
  51. data/lib/authlogic/session/base.rb +20 -8
  52. data/lib/authlogic/session/brute_force_protection.rb +87 -56
  53. data/lib/authlogic/session/callbacks.rb +99 -49
  54. data/lib/authlogic/session/cookies.rb +128 -59
  55. data/lib/authlogic/session/existence.rb +29 -19
  56. data/lib/authlogic/session/foundation.rb +70 -16
  57. data/lib/authlogic/session/http_auth.rb +39 -31
  58. data/lib/authlogic/session/id.rb +27 -15
  59. data/lib/authlogic/session/klass.rb +17 -13
  60. data/lib/authlogic/session/magic_columns.rb +78 -59
  61. data/lib/authlogic/session/magic_states.rb +50 -27
  62. data/lib/authlogic/session/params.rb +79 -50
  63. data/lib/authlogic/session/password.rb +197 -118
  64. data/lib/authlogic/session/perishable_token.rb +12 -6
  65. data/lib/authlogic/session/persistence.rb +20 -14
  66. data/lib/authlogic/session/priority_record.rb +20 -16
  67. data/lib/authlogic/session/scopes.rb +63 -33
  68. data/lib/authlogic/session/session.rb +40 -25
  69. data/lib/authlogic/session/timeout.rb +51 -34
  70. data/lib/authlogic/session/unauthorized_record.rb +24 -18
  71. data/lib/authlogic/session/validation.rb +32 -21
  72. data/lib/authlogic/test_case.rb +123 -35
  73. data/lib/authlogic/test_case/mock_controller.rb +14 -13
  74. data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -5
  75. data/lib/authlogic/test_case/mock_logger.rb +1 -1
  76. data/lib/authlogic/test_case/mock_request.rb +9 -4
  77. data/lib/authlogic/test_case/rails_request_adapter.rb +8 -7
  78. data/lib/authlogic/version.rb +21 -0
  79. data/test/acts_as_authentic_test/base_test.rb +1 -1
  80. data/test/acts_as_authentic_test/email_test.rb +80 -63
  81. data/test/acts_as_authentic_test/logged_in_status_test.rb +14 -8
  82. data/test/acts_as_authentic_test/login_test.rb +91 -49
  83. data/test/acts_as_authentic_test/magic_columns_test.rb +13 -13
  84. data/test/acts_as_authentic_test/password_test.rb +82 -60
  85. data/test/acts_as_authentic_test/perishable_token_test.rb +31 -25
  86. data/test/acts_as_authentic_test/persistence_token_test.rb +9 -5
  87. data/test/acts_as_authentic_test/restful_authentication_test.rb +18 -9
  88. data/test/acts_as_authentic_test/session_maintenance_test.rb +86 -22
  89. data/test/acts_as_authentic_test/single_access_test.rb +15 -15
  90. data/test/adapter_test.rb +21 -0
  91. data/test/authenticates_many_test.rb +26 -11
  92. data/test/config_test.rb +9 -9
  93. data/test/crypto_provider_test/aes256_test.rb +3 -3
  94. data/test/crypto_provider_test/bcrypt_test.rb +1 -1
  95. data/test/crypto_provider_test/scrypt_test.rb +2 -2
  96. data/test/crypto_provider_test/sha1_test.rb +4 -4
  97. data/test/crypto_provider_test/sha256_test.rb +2 -2
  98. data/test/crypto_provider_test/sha512_test.rb +3 -3
  99. data/test/crypto_provider_test/wordpress_test.rb +24 -0
  100. data/test/gemfiles/Gemfile.rails-4.2.x +2 -2
  101. data/test/gemfiles/Gemfile.rails-5.0.x +6 -0
  102. data/test/gemfiles/Gemfile.rails-5.1.x +6 -0
  103. data/test/gemfiles/Gemfile.rails-5.2.x +6 -0
  104. data/test/gemfiles/Gemfile.rails-master +6 -0
  105. data/test/i18n_test.rb +9 -9
  106. data/test/libs/affiliate.rb +2 -2
  107. data/test/libs/company.rb +4 -4
  108. data/test/libs/employee.rb +2 -2
  109. data/test/libs/employee_session.rb +1 -1
  110. data/test/libs/ldaper.rb +1 -1
  111. data/test/libs/project.rb +1 -1
  112. data/test/libs/user_session.rb +2 -2
  113. data/test/random_test.rb +9 -38
  114. data/test/session_test/activation_test.rb +7 -7
  115. data/test/session_test/active_record_trickery_test.rb +9 -6
  116. data/test/session_test/brute_force_protection_test.rb +26 -21
  117. data/test/session_test/callbacks_test.rb +10 -4
  118. data/test/session_test/cookies_test.rb +54 -20
  119. data/test/session_test/existence_test.rb +45 -23
  120. data/test/session_test/foundation_test.rb +17 -1
  121. data/test/session_test/http_auth_test.rb +11 -12
  122. data/test/session_test/id_test.rb +3 -3
  123. data/test/session_test/klass_test.rb +2 -2
  124. data/test/session_test/magic_columns_test.rb +15 -17
  125. data/test/session_test/magic_states_test.rb +17 -19
  126. data/test/session_test/params_test.rb +26 -20
  127. data/test/session_test/password_test.rb +11 -12
  128. data/test/session_test/perishability_test.rb +5 -5
  129. data/test/session_test/persistence_test.rb +4 -3
  130. data/test/session_test/scopes_test.rb +15 -9
  131. data/test/session_test/session_test.rb +7 -6
  132. data/test/session_test/timeout_test.rb +16 -14
  133. data/test/session_test/unauthorized_record_test.rb +3 -3
  134. data/test/session_test/validation_test.rb +5 -5
  135. data/test/test_helper.rb +115 -49
  136. metadata +107 -36
  137. data/README.rdoc +0 -232
  138. data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
  139. data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
  140. data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
@@ -1,59 +1,72 @@
1
- require 'request_store'
1
+ require "request_store"
2
2
 
3
3
  module Authlogic
4
4
  module Session
5
- # Activating Authlogic requires that you pass it an Authlogic::ControllerAdapters::AbstractAdapter object, or a class that extends it.
6
- # This is sort of like a database connection for an ORM library, Authlogic can't do anything until it is "connected" to a controller.
7
- # If you are using a supported framework, Authlogic takes care of this for you.
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(session)
11
- super("You must activate the Authlogic::Session::Base.controller with a controller object before creating objects")
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. This MUST be set before anything can be done.
24
- # Similar to how ActiveRecord won't allow you to do anything without establishing a DB connection. In your framework
25
- # environment this is done for you, but if you are using Authlogic outside of your framework, you need to assign a controller
26
- # object to Authlogic via Authlogic::Session::Base.controller = obj. See the controller= method for more information.
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 adapter. The controller adapters close the gap
32
- # between the different controllers in each framework. That being said, Authlogic is expecting your object's class to
33
- # extend Authlogic::ControllerAdapters::AbstractAdapter. See Authlogic::ControllerAdapters for more info.
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.new(self) unless self.class.activated?
61
+ raise NotActivatedError unless self.class.activated?
50
62
  super
51
63
  end
52
-
64
+
53
65
  private
54
- def controller
55
- self.class.controller
56
- end
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 ActiveRecord. That's the goal here.
4
- # This is useful for the various rails helper methods such as form_for, error_messages_for, or any method that
5
- # expects an ActiveRecord object. The point is to disguise the object as an ActiveRecord object so we can take
6
- # advantage of the many ActiveRecord tools.
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
- if !klass.model_name.respond_to?(:name)
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 the following namespace:
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(*args)
28
- I18n.t("models.#{name.underscore}", {:count => 1, :default => name.humanize})
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 since this is the method it calls.
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 base class Authlogic, where all modules are included. For information on functiionality see the various
4
- # sub modules.
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 ran after a record is found
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 combinations as possible, until one works. A brute force attacked is
4
- # generally combated with a slow hasing algorithm such as BCrypt. You can increase the cost, which makes the hash generation slower, and ultimately
5
- # increases the time it takes to execute a brute force attack. Just to put this into perspective, if a hacker was to gain access to your server
6
- # and execute a brute force attack locally, meaning there is no network lag, it would probably take decades to complete. Now throw in network lag
7
- # and it would take MUCH longer.
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 them as soon as you realize something isn't right? That's
10
- # what this module is all about. By default the consecutive_failed_logins_limit configuration option is set to 50, if someone consecutively fails to login
11
- # after 50 attempts their account will be suspended. This is a very liberal number and at this point it should be obvious that something is not right.
12
- # If you wish to lower this number just set the configuration to a lower number:
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, :if => :reset_failed_login_count?
23
- validate :validate_failed_logins, :if => :being_brute_force_protected?
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 allowed number of consecutive failed logins. By default this is 50, this is a very liberal
30
- # number, and if someone fails to login after 50 tries it should be pretty obvious that it's a machine trying to login in and very likely a brute force attack.
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 failed_login_count (integer) field.
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 tries to login into a system using every combination of character possible. Thus resulting
35
- # in possibly millions of attempts to log into an account.
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 ban the user? This can be a temporary or permanent ban.
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 up the brute force protection feature.
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 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.
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? && (failed_login_ban_for <= 0 ||
61
- (attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
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
- def exceeded_failed_logins_limit?
66
- !attempted_record.nil? && attempted_record.respond_to?(:failed_login_count) && consecutive_failed_logins_limit > 0 &&
67
- attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
68
- end
69
-
70
- def reset_failed_login_count?
71
- exceeded_failed_logins_limit? && !being_brute_force_protected?
72
- end
73
-
74
- def reset_failed_login_count
75
- attempted_record.failed_login_count = 0
76
- end
77
-
78
- def validate_failed_logins
79
- errors.clear # Clear all other error messages, as they are irrelevant at this point and can only provide additional information that is not needed
80
- errors.add(:base, I18n.t(
81
- 'error_messages.consecutive_failed_logins_limit_exceeded',
82
- :default => "Consecutive failed logins limit exceeded, account has been" + (failed_login_ban_for == 0 ? "" : " temporarily") + " disabled."
83
- ))
84
- end
85
-
86
- def consecutive_failed_logins_limit
87
- self.class.consecutive_failed_logins_limit
88
- end
89
-
90
- def failed_login_ban_for
91
- self.class.failed_login_ban_for
92
- end
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 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.
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 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.
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, before_validation, etc.
12
- # You have similar callbacks with Authlogic, see the METHODS constant below. The order of execution is as follows:
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 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.
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 allow Authlogic
54
- # to extend properly with multiple extensions. Please ONLY use the method above.
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
- "before_persisting", "persist", "after_persisting",
58
- "before_validation", "before_validation_on_create", "before_validation_on_update", "validate",
59
- "after_validation_on_update", "after_validation_on_create", "after_validation",
60
- "before_save", "before_create", "before_update", "after_update", "after_create", "after_save",
61
- "before_destroy", "after_destroy"
62
- ]
63
-
64
- def self.included(base) #:nodoc:
65
- base.send :include, ActiveSupport::Callbacks
66
- if ActiveSupport::VERSION::STRING >= '4.1'
67
- base.define_callbacks *METHODS + [{:terminator => ->(target, result){ result == false } }]
68
- base.define_callbacks *['persist', {:terminator => ->(target, result){ result == true } }]
69
- else
70
- base.define_callbacks *METHODS + [{:terminator => 'result == false'}]
71
- base.define_callbacks *['persist', {:terminator => 'result == true'}]
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
- # If Rails 3, support the new callback syntax
75
- if base.singleton_class.method_defined?(:set_callback)
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 <<-"end_eval", __FILE__, __LINE__
78
- def self.#{method}(*methods, &block)
79
- set_callback :#{method}, *methods, &block
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
- end_eval
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
- private
87
- METHODS.each do |method|
88
- class_eval <<-"end_eval", __FILE__, __LINE__
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
- end_eval
93
- end
94
-
95
- def save_record(alternate_record = nil)
96
- r = alternate_record || record
97
- r.save_without_session_maintenance(:validate => false) if r && r.changed? && !r.readonly?
98
- end
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