authlogic 3.4.6 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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