authlogic 3.8.0 → 4.5.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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
  3. data/.github/ISSUE_TEMPLATE/feature_proposal.md +32 -0
  4. data/.github/triage.md +86 -0
  5. data/.gitignore +4 -3
  6. data/.rubocop.yml +109 -9
  7. data/.rubocop_todo.yml +38 -355
  8. data/.travis.yml +11 -35
  9. data/CHANGELOG.md +345 -2
  10. data/CONTRIBUTING.md +45 -14
  11. data/Gemfile +3 -2
  12. data/README.md +244 -90
  13. data/Rakefile +10 -10
  14. data/UPGRADING.md +22 -0
  15. data/authlogic.gemspec +34 -21
  16. data/doc/use_normal_rails_validation.md +82 -0
  17. data/gemfiles/Gemfile.rails-4.2.x +6 -0
  18. data/{test/gemfiles → gemfiles}/Gemfile.rails-5.1.x +2 -2
  19. data/{test/gemfiles → gemfiles}/Gemfile.rails-5.2.x +2 -2
  20. data/lib/authlogic/acts_as_authentic/base.rb +36 -24
  21. data/lib/authlogic/acts_as_authentic/email.rb +65 -31
  22. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +14 -9
  23. data/lib/authlogic/acts_as_authentic/login.rb +61 -45
  24. data/lib/authlogic/acts_as_authentic/magic_columns.rb +6 -6
  25. data/lib/authlogic/acts_as_authentic/password.rb +267 -146
  26. data/lib/authlogic/acts_as_authentic/perishable_token.rb +24 -19
  27. data/lib/authlogic/acts_as_authentic/persistence_token.rb +10 -15
  28. data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
  29. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +50 -14
  30. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +88 -60
  31. data/lib/authlogic/acts_as_authentic/single_access_token.rb +23 -11
  32. data/lib/authlogic/acts_as_authentic/validations_scope.rb +9 -6
  33. data/lib/authlogic/authenticates_many/association.rb +7 -7
  34. data/lib/authlogic/authenticates_many/base.rb +37 -21
  35. data/lib/authlogic/config.rb +21 -10
  36. data/lib/authlogic/controller_adapters/abstract_adapter.rb +38 -11
  37. data/lib/authlogic/controller_adapters/rack_adapter.rb +9 -5
  38. data/lib/authlogic/controller_adapters/rails_adapter.rb +12 -7
  39. data/lib/authlogic/controller_adapters/sinatra_adapter.rb +2 -2
  40. data/lib/authlogic/crypto_providers/aes256.rb +37 -32
  41. data/lib/authlogic/crypto_providers/bcrypt.rb +21 -15
  42. data/lib/authlogic/crypto_providers/md5.rb +4 -2
  43. data/lib/authlogic/crypto_providers/scrypt.rb +22 -17
  44. data/lib/authlogic/crypto_providers/sha1.rb +11 -5
  45. data/lib/authlogic/crypto_providers/sha256.rb +13 -9
  46. data/lib/authlogic/crypto_providers/sha512.rb +0 -21
  47. data/lib/authlogic/crypto_providers/wordpress.rb +32 -3
  48. data/lib/authlogic/crypto_providers.rb +91 -0
  49. data/lib/authlogic/i18n.rb +26 -19
  50. data/lib/authlogic/random.rb +10 -28
  51. data/lib/authlogic/regex.rb +59 -28
  52. data/lib/authlogic/session/activation.rb +10 -7
  53. data/lib/authlogic/session/active_record_trickery.rb +13 -9
  54. data/lib/authlogic/session/base.rb +15 -4
  55. data/lib/authlogic/session/brute_force_protection.rb +40 -33
  56. data/lib/authlogic/session/callbacks.rb +94 -46
  57. data/lib/authlogic/session/cookies.rb +130 -45
  58. data/lib/authlogic/session/existence.rb +21 -11
  59. data/lib/authlogic/session/foundation.rb +64 -14
  60. data/lib/authlogic/session/http_auth.rb +35 -28
  61. data/lib/authlogic/session/id.rb +9 -4
  62. data/lib/authlogic/session/klass.rb +15 -12
  63. data/lib/authlogic/session/magic_columns.rb +58 -55
  64. data/lib/authlogic/session/magic_states.rb +25 -19
  65. data/lib/authlogic/session/params.rb +42 -28
  66. data/lib/authlogic/session/password.rb +130 -120
  67. data/lib/authlogic/session/perishable_token.rb +5 -4
  68. data/lib/authlogic/session/persistence.rb +18 -12
  69. data/lib/authlogic/session/priority_record.rb +15 -12
  70. data/lib/authlogic/session/scopes.rb +51 -32
  71. data/lib/authlogic/session/session.rb +38 -28
  72. data/lib/authlogic/session/timeout.rb +13 -13
  73. data/lib/authlogic/session/unauthorized_record.rb +18 -13
  74. data/lib/authlogic/session/validation.rb +9 -9
  75. data/lib/authlogic/test_case/mock_controller.rb +5 -4
  76. data/lib/authlogic/test_case/mock_cookie_jar.rb +47 -3
  77. data/lib/authlogic/test_case/mock_request.rb +6 -3
  78. data/lib/authlogic/test_case/rails_request_adapter.rb +3 -2
  79. data/lib/authlogic/test_case.rb +70 -2
  80. data/lib/authlogic/version.rb +21 -0
  81. data/lib/authlogic.rb +51 -49
  82. data/test/acts_as_authentic_test/base_test.rb +3 -1
  83. data/test/acts_as_authentic_test/email_test.rb +43 -42
  84. data/test/acts_as_authentic_test/logged_in_status_test.rb +6 -4
  85. data/test/acts_as_authentic_test/login_test.rb +77 -80
  86. data/test/acts_as_authentic_test/magic_columns_test.rb +3 -1
  87. data/test/acts_as_authentic_test/password_test.rb +51 -37
  88. data/test/acts_as_authentic_test/perishable_token_test.rb +13 -5
  89. data/test/acts_as_authentic_test/persistence_token_test.rb +7 -1
  90. data/test/acts_as_authentic_test/restful_authentication_test.rb +14 -3
  91. data/test/acts_as_authentic_test/session_maintenance_test.rb +69 -15
  92. data/test/acts_as_authentic_test/single_access_test.rb +3 -1
  93. data/test/adapter_test.rb +23 -0
  94. data/test/authenticates_many_test.rb +3 -1
  95. data/test/config_test.rb +11 -9
  96. data/test/crypto_provider_test/aes256_test.rb +3 -1
  97. data/test/crypto_provider_test/bcrypt_test.rb +3 -1
  98. data/test/crypto_provider_test/scrypt_test.rb +3 -1
  99. data/test/crypto_provider_test/sha1_test.rb +3 -1
  100. data/test/crypto_provider_test/sha256_test.rb +3 -1
  101. data/test/crypto_provider_test/sha512_test.rb +3 -1
  102. data/test/crypto_provider_test/wordpress_test.rb +26 -0
  103. data/test/fixtures/companies.yml +2 -2
  104. data/test/fixtures/employees.yml +1 -1
  105. data/test/i18n_test.rb +6 -4
  106. data/test/libs/affiliate.rb +2 -0
  107. data/test/libs/company.rb +4 -2
  108. data/test/libs/employee.rb +2 -0
  109. data/test/libs/employee_session.rb +2 -0
  110. data/test/libs/ldaper.rb +2 -0
  111. data/test/libs/project.rb +2 -0
  112. data/test/libs/user.rb +2 -0
  113. data/test/libs/user_session.rb +4 -2
  114. data/test/random_test.rb +10 -38
  115. data/test/session_test/activation_test.rb +3 -1
  116. data/test/session_test/active_record_trickery_test.rb +7 -4
  117. data/test/session_test/brute_force_protection_test.rb +11 -9
  118. data/test/session_test/callbacks_test.rb +12 -4
  119. data/test/session_test/cookies_test.rb +48 -5
  120. data/test/session_test/existence_test.rb +18 -5
  121. data/test/session_test/foundation_test.rb +19 -1
  122. data/test/session_test/http_auth_test.rb +11 -7
  123. data/test/session_test/id_test.rb +3 -1
  124. data/test/session_test/klass_test.rb +3 -1
  125. data/test/session_test/magic_columns_test.rb +13 -13
  126. data/test/session_test/magic_states_test.rb +3 -1
  127. data/test/session_test/params_test.rb +13 -5
  128. data/test/session_test/password_test.rb +10 -8
  129. data/test/session_test/perishability_test.rb +3 -1
  130. data/test/session_test/persistence_test.rb +4 -1
  131. data/test/session_test/scopes_test.rb +16 -8
  132. data/test/session_test/session_test.rb +6 -4
  133. data/test/session_test/timeout_test.rb +4 -2
  134. data/test/session_test/unauthorized_record_test.rb +4 -2
  135. data/test/session_test/validation_test.rb +3 -1
  136. data/test/test_helper.rb +84 -45
  137. metadata +87 -73
  138. data/.github/ISSUE_TEMPLATE.md +0 -13
  139. data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
  140. data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
  141. data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
  142. data/test/gemfiles/Gemfile.rails-4.2.x +0 -7
  143. data/test/gemfiles/Gemfile.rails-5.0.x +0 -6
@@ -1,27 +1,6 @@
1
1
  require "digest/sha2"
2
2
 
3
3
  module Authlogic
4
- # The acts_as_authentic method has a crypto_provider option. This allows you
5
- # to use any type of encryption you like. Just create a class with a class
6
- # level encrypt and matches? method. See example below.
7
- #
8
- # === Example
9
- #
10
- # class MyAwesomeEncryptionMethod
11
- # def self.encrypt(*tokens)
12
- # # The tokens passed will be an array of objects, what type of object
13
- # # is irrelevant, just do what you need to do with them and return a
14
- # # single encrypted string. For example, you will most likely join all
15
- # # of the objects into a single string and then encrypt that string.
16
- # end
17
- #
18
- # def self.matches?(crypted, *tokens)
19
- # # Return true if the crypted string matches the tokens. Depending on
20
- # # your algorithm you might decrypt the string then compare it to the
21
- # # token, or you might encrypt the tokens and make sure it matches the
22
- # # crypted string, its up to you.
23
- # end
24
- # end
25
4
  module CryptoProviders
26
5
  # = Sha512
27
6
  #
@@ -1,15 +1,44 @@
1
- require 'digest/md5'
1
+ require "digest/md5"
2
+
3
+ ::ActiveSupport::Deprecation.warn(
4
+ <<~EOS,
5
+ authlogic/crypto_providers/wordpress.rb is deprecated without replacement.
6
+ Yes, the entire file. Don't `require` it. Let us know ASAP if you are still
7
+ using it.
8
+
9
+ Reasons for deprecation: This file is not autoloaded by
10
+ `authlogic/crypto_providers.rb`. It's not documented. There are no tests.
11
+ So, it's likely used by a *very* small number of people, if any. It's never
12
+ had any contributions except by its original author, Jeffry Degrande, in
13
+ 2009. It is unclear why it should live in the main authlogic codebase. It
14
+ could be in a separate gem, authlogic-wordpress, or it could just live in
15
+ Jeffry's codebase, if he still even needs it, in 2018, nine years later.
16
+ EOS
17
+ caller(1)
18
+ )
19
+
2
20
  module Authlogic
3
21
  module CryptoProviders
22
+ # Crypto provider to transition from wordpress user accounts. Written by
23
+ # Jeffry Degrande in 2009. First released in 2.1.3.
24
+ #
25
+ # Problems:
26
+ #
27
+ # - There are no tests.
28
+ # - We can't even figure out how to run this without it crashing.
29
+ # - Presumably it implements some spec, but it doesn't mention which.
30
+ # - It is not documented anywhere.
31
+ # - There is no PR associated with this, and no discussion about it could be found.
32
+ #
4
33
  class Wordpress
5
34
  class << self
6
- ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
35
+ ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".freeze
7
36
 
8
37
  def matches?(crypted, *tokens)
9
38
  stretches = 1 << ITOA64.index(crypted[3, 1])
10
39
  plain, salt = *tokens
11
40
  hashed = Digest::MD5.digest(salt + plain)
12
- stretches.times do |i|
41
+ stretches.times do
13
42
  hashed = Digest::MD5.digest(hashed + plain)
14
43
  end
15
44
  crypted[0, 12] + encode_64(hashed, 16) == crypted
@@ -1,4 +1,25 @@
1
1
  module Authlogic
2
+ # The acts_as_authentic method has a crypto_provider option. This allows you
3
+ # to use any type of encryption you like. Just create a class with a class
4
+ # level encrypt and matches? method. See example below.
5
+ #
6
+ # === Example
7
+ #
8
+ # class MyAwesomeEncryptionMethod
9
+ # def self.encrypt(*tokens)
10
+ # # The tokens passed will be an array of objects, what type of object
11
+ # # is irrelevant, just do what you need to do with them and return a
12
+ # # single encrypted string. For example, you will most likely join all
13
+ # # of the objects into a single string and then encrypt that string.
14
+ # end
15
+ #
16
+ # def self.matches?(crypted, *tokens)
17
+ # # Return true if the crypted string matches the tokens. Depending on
18
+ # # your algorithm you might decrypt the string then compare it to the
19
+ # # token, or you might encrypt the tokens and make sure it matches the
20
+ # # crypted string, its up to you.
21
+ # end
22
+ # end
2
23
  module CryptoProviders
3
24
  autoload :MD5, "authlogic/crypto_providers/md5"
4
25
  autoload :Sha1, "authlogic/crypto_providers/sha1"
@@ -7,5 +28,75 @@ module Authlogic
7
28
  autoload :BCrypt, "authlogic/crypto_providers/bcrypt"
8
29
  autoload :AES256, "authlogic/crypto_providers/aes256"
9
30
  autoload :SCrypt, "authlogic/crypto_providers/scrypt"
31
+ # crypto_providers/wordpress.rb has never been autoloaded, and now it is
32
+ # deprecated.
33
+
34
+ # Guide users to choose a better crypto provider.
35
+ class Guidance
36
+ AES256_DEPRECATED = <<~EOS.freeze
37
+ You have selected AES256 as your authlogic crypto provider. This
38
+ choice is not suitable for password storage.
39
+
40
+ Authlogic will drop its AES256 crypto provider in the next major
41
+ version. If you're unable to transition away from AES256 please let us
42
+ know immediately.
43
+
44
+ We recommend using a one-way algorithm instead. There are many choices;
45
+ we recommend scrypt. Use the transition_from_crypto_providers option
46
+ to make this painless for your users.
47
+ EOS
48
+ BUILTIN_PROVIDER_PREFIX = "Authlogic::CryptoProviders::".freeze
49
+ NONADAPTIVE_ALGORITHM = <<~EOS.freeze
50
+ You have selected %s as your authlogic crypto provider. This algorithm
51
+ does not have any practical known attacks against it. However, there are
52
+ better choices.
53
+
54
+ Authlogic has no plans yet to deprecate this crypto provider. However,
55
+ we recommend transitioning to a more secure, adaptive hashing algorithm,
56
+ like scrypt. Adaptive algorithms are designed to slow down brute force
57
+ attacks, and over time the iteration count can be increased to make it
58
+ slower, so it remains resistant to brute-force search attacks even in
59
+ the face of increasing computation power.
60
+
61
+ Use the transition_from_crypto_providers option to make the transition
62
+ painless for your users.
63
+ EOS
64
+ VULNERABLE_ALGORITHM = <<~EOS.freeze
65
+ You have selected %s as your authlogic crypto provider. It is a poor
66
+ choice because there are known attacks against this algorithm.
67
+
68
+ Authlogic has no plans yet to deprecate this crypto provider. However,
69
+ we recommend transitioning to a secure hashing algorithm. We recommend
70
+ an adaptive algorithm, like scrypt.
71
+
72
+ Use the transition_from_crypto_providers option to make the transition
73
+ painless for your users.
74
+ EOS
75
+
76
+ def initialize(provider)
77
+ @provider = provider
78
+ end
79
+
80
+ def impart_wisdom
81
+ return unless @provider.is_a?(Class)
82
+
83
+ # We can only impart wisdom about our own built-in providers.
84
+ absolute_name = @provider.name
85
+ return unless absolute_name.start_with?(BUILTIN_PROVIDER_PREFIX)
86
+
87
+ # Inspect the string name of the provider, rather than using the
88
+ # constants in our `when` clauses. If we used the constants, we'd
89
+ # negate the benefits of the `autoload` above.
90
+ name = absolute_name.demodulize
91
+ case name
92
+ when "AES256"
93
+ ::ActiveSupport::Deprecation.warn(AES256_DEPRECATED)
94
+ when "MD5", "Sha1"
95
+ warn(format(VULNERABLE_ALGORITHM, name))
96
+ when "Sha256", "Sha512"
97
+ warn(format(NONADAPTIVE_ALGORITHM, name))
98
+ end
99
+ end
100
+ end
10
101
  end
11
102
  end
@@ -1,42 +1,48 @@
1
1
  require "authlogic/i18n/translator"
2
2
 
3
3
  module Authlogic
4
- # This class allows any message in Authlogic to use internationalization. In earlier
5
- # versions of Authlogic each message was translated via configuration. This cluttered up
6
- # the configuration and cluttered up Authlogic. So all translation has been extracted
7
- # out into this class. Now all messages pass through this class, making it much easier
8
- # to implement in I18n library / plugin you want. Use this as a layer that sits between
9
- # Authlogic and whatever I18n library you want to use.
4
+ # This class allows any message in Authlogic to use internationalization. In
5
+ # earlier versions of Authlogic each message was translated via configuration.
6
+ # This cluttered up the configuration and cluttered up Authlogic. So all
7
+ # translation has been extracted out into this class. Now all messages pass
8
+ # through this class, making it much easier to implement in I18n library /
9
+ # plugin you want. Use this as a layer that sits between Authlogic and
10
+ # whatever I18n library you want to use.
10
11
  #
11
- # By default this uses the rails I18n library, if it exists. If it doesn't exist it just
12
- # returns the default English message. The Authlogic I18n class works EXACTLY like the
13
- # rails I18n class. This is because the arguments are delegated to this class.
12
+ # By default this uses the rails I18n library, if it exists. If it doesn't
13
+ # exist it just returns the default English message. The Authlogic I18n class
14
+ # works EXACTLY like the rails I18n class. This is because the arguments are
15
+ # delegated to this class.
14
16
  #
15
17
  # Here is how all messages are translated internally with Authlogic:
16
18
  #
17
19
  # Authlogic::I18n.t('error_messages.password_invalid', :default => "is invalid")
18
20
  #
19
- # If you use a different I18n library just replace the build-in I18n::Translator class
20
- # with your own. For example:
21
+ # If you use a different I18n library just replace the build-in
22
+ # I18n::Translator class with your own. For example:
21
23
  #
22
24
  # class MyAuthlogicI18nTranslator
23
25
  # def translate(key, options = {})
24
- # # you will have key which will be something like: "error_messages.password_invalid"
25
- # # you will also have options[:default], which will be the default English version of the message
26
+ # # you will have key which will be something like:
27
+ # # "error_messages.password_invalid"
28
+ # # you will also have options[:default], which will be the default
29
+ # # English version of the message
26
30
  # # do whatever you want here with the arguments passed to you.
27
31
  # end
28
32
  # end
29
33
  #
30
34
  # Authlogic::I18n.translator = MyAuthlogicI18nTranslator.new
31
35
  #
32
- # That it's! Here is a complete list of the keys that are passed. Just define these however you wish:
36
+ # That it's! Here is a complete list of the keys that are passed. Just define
37
+ # these however you wish:
33
38
  #
34
39
  # authlogic:
35
40
  # error_messages:
36
41
  # login_blank: can not be blank
37
42
  # login_not_found: is not valid
38
43
  # login_invalid: should use only letters, numbers, spaces, and .-_@+ please.
39
- # consecutive_failed_logins_limit_exceeded: Consecutive failed logins limit exceeded, account is disabled.
44
+ # consecutive_failed_logins_limit_exceeded: >
45
+ # Consecutive failed logins limit exceeded, account is disabled.
40
46
  # email_invalid: should look like an email address.
41
47
  # email_invalid_international: should look like an international email address.
42
48
  # password_blank: can not be blank
@@ -46,6 +52,7 @@ module Authlogic
46
52
  # not_approved: Your account is not approved
47
53
  # no_authentication_details: You did not provide any details for authentication.
48
54
  # general_credentials_error: Login/Password combination is not valid
55
+ # session_invalid: Your session is invalid and has the following errors:
49
56
  # models:
50
57
  # user_session: UserSession (or whatever name you are using)
51
58
  # attributes:
@@ -79,11 +86,11 @@ module Authlogic
79
86
  @@translator = translator
80
87
  end
81
88
 
82
- # All message translation is passed to this method. The first argument is the key
83
- # for the message. The second is options, see the rails I18n library for a list of
84
- # options used.
89
+ # All message translation is passed to this method. The first argument is
90
+ # the key for the message. The second is options, see the rails I18n
91
+ # library for a list of options used.
85
92
  def translate(key, options = {})
86
- translator.translate key, { :scope => I18n.scope }.merge(options)
93
+ translator.translate key, { scope: I18n.scope }.merge(options)
87
94
  end
88
95
  alias :t :translate
89
96
  end
@@ -1,34 +1,16 @@
1
+ require "securerandom"
2
+
1
3
  module Authlogic
2
- # Handles generating random strings. If SecureRandom is installed it will default to
3
- # this and use it instead. SecureRandom comes with ActiveSupport. So if you are using
4
- # this in a rails app you should have this library.
4
+ # Generates random strings using ruby's SecureRandom library.
5
5
  module Random
6
- extend self
7
-
8
- SecureRandom = (defined?(::SecureRandom) && ::SecureRandom) ||
9
- (defined?(::ActiveSupport::SecureRandom) && ::ActiveSupport::SecureRandom)
10
-
11
- if SecureRandom
12
- def hex_token
13
- SecureRandom.hex(64)
14
- end
15
-
16
- def friendly_token
17
- # use base64url as defined by RFC4648
18
- SecureRandom.base64(15).tr('+/=', '').strip.delete("\n")
19
- end
20
- else
21
- def hex_token
22
- Authlogic::CryptoProviders::Sha512.encrypt(Time.now.to_s + (1..10).collect { rand.to_s }.join)
23
- end
24
-
25
- FRIENDLY_CHARS = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
6
+ def self.hex_token
7
+ SecureRandom.hex(64)
8
+ end
26
9
 
27
- def friendly_token
28
- newpass = ""
29
- 1.upto(20) { |i| newpass << FRIENDLY_CHARS[rand(FRIENDLY_CHARS.size - 1)] }
30
- newpass
31
- end
10
+ # Returns a string in base64url format as defined by RFC-3548 and RFC-4648.
11
+ # We call this a "friendly" token because it is short and safe for URLs.
12
+ def self.friendly_token
13
+ SecureRandom.urlsafe_base64(15)
32
14
  end
33
15
  end
34
16
  end
@@ -1,48 +1,79 @@
1
- # encoding: utf-8
2
1
  module Authlogic
3
- # This is a module the contains regular expressions used throughout Authlogic. The point of extracting
4
- # them out into their own module is to make them easily available to you for other uses. Ex:
2
+ # This is a module the contains regular expressions used throughout Authlogic.
3
+ # The point of extracting them out into their own module is to make them
4
+ # easily available to you for other uses. Ex:
5
5
  #
6
6
  # validates_format_of :my_email_field, :with => Authlogic::Regex.email
7
7
  module Regex
8
- # A general email regular expression. It allows top level domains (TLD) to be from 2 -
9
- # 24 in length. The decisions behind this regular expression were made by analyzing
10
- # the list of top-level domains maintained by IANA and by reading this website:
11
- # http://www.regular-expressions.info/email.html, which is an excellent resource for
12
- # regular expressions.
13
- def self.email
14
- @email_regex ||= begin
15
- email_name_regex = '[A-Z0-9_\.&%\+\-\']+'
16
- domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
17
- domain_tld_regex = '(?:[A-Z]{2,25})'
18
- /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
19
- end
20
- end
8
+ # A general email regular expression. It allows top level domains (TLD) to
9
+ # be from 2 - 24 in length. The decisions behind this regular expression
10
+ # were made by analyzing the list of top-level domains maintained by IANA
11
+ # and by reading this website:
12
+ # http://www.regular-expressions.info/email.html, which is an excellent
13
+ # resource for regular expressions.
14
+ EMAIL = /
15
+ \A
16
+ [A-Z0-9_.&%+\-']+ # mailbox
17
+ @
18
+ (?:[A-Z0-9\-]+\.)+ # subdomains
19
+ (?:[A-Z]{2,25}) # TLD
20
+ \z
21
+ /ix
21
22
 
22
- # A draft regular expression for internationalized email addresses.
23
- # Given that the standard may be in flux, this simply emulates @email_regex but rather than
24
- # allowing specific characters for each part, it instead disallows the complement set of characters:
23
+ # A draft regular expression for internationalized email addresses. Given
24
+ # that the standard may be in flux, this simply emulates @email_regex but
25
+ # rather than allowing specific characters for each part, it instead
26
+ # disallows the complement set of characters:
27
+ #
25
28
  # - email_name_regex disallows: @[]^ !"#$()*,/:;<=>?`{|}~\ and control characters
26
29
  # - domain_head_regex disallows: _%+ and all characters in email_name_regex
27
30
  # - domain_tld_regex disallows: 0123456789- and all characters in domain_head_regex
31
+ #
28
32
  # http://en.wikipedia.org/wiki/Email_address#Internationalization
29
33
  # http://tools.ietf.org/html/rfc6530
30
34
  # http://www.unicode.org/faq/idn.html
31
35
  # http://ruby-doc.org/core-2.1.5/Regexp.html#class-Regexp-label-Character+Classes
32
36
  # http://en.wikipedia.org/wiki/Unicode_character_property#General_Category
37
+ EMAIL_NONASCII = /
38
+ \A
39
+ [^[:cntrl:][@\[\]\^ \!"\#$\(\)*,\/:;<=>?`{|}~\\]]+ # mailbox
40
+ @
41
+ (?:[^[:cntrl:][@\[\]\^ \!\"\#$&\(\)*,\/:;<=>\?`{|}~\\_.%+']]+\.)+ # subdomains
42
+ (?:[^[:cntrl:][@\[\]\^ \!\"\#$&\(\)*,\/:;<=>\?`{|}~\\_.%+\-'0-9]]{2,25}) # TLD
43
+ \z
44
+ /x
45
+
46
+ # A simple regular expression that only allows for letters, numbers, spaces, and
47
+ # .-_@+. Just a standard login / username regular expression.
48
+ LOGIN = /\A[a-zA-Z0-9_][a-zA-Z0-9\.+\-_@ ]+\z/
49
+
50
+ # Accessing the above constants using the following methods is deprecated.
51
+
52
+ # @deprecated
53
+ def self.email
54
+ ::ActiveSupport::Deprecation.warn(
55
+ "Authlogic::Regex.email is deprecated, use Authlogic::Regex::EMAIL",
56
+ caller(1)
57
+ )
58
+ EMAIL
59
+ end
60
+
61
+ # @deprecated
33
62
  def self.email_nonascii
34
- @email_nonascii_regex ||= begin
35
- email_name_regex = '[^[:cntrl:][@\[\]\^ \!\"#$\(\)*,/:;<=>\?`{|}~\\\]]+'
36
- domain_head_regex = '(?:[^[:cntrl:][@\[\]\^ \!\"#$&\(\)*,/:;<=>\?`{|}~\\\_\.%\+\']]+\.)+'
37
- domain_tld_regex = '(?:[^[:cntrl:][@\[\]\^ \!\"#$&\(\)*,/:;<=>\?`{|}~\\\_\.%\+\-\'0-9]]{2,25})'
38
- /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/
39
- end
63
+ ::ActiveSupport::Deprecation.warn(
64
+ "Authlogic::Regex.email_nonascii is deprecated, use Authlogic::Regex::EMAIL_NONASCII",
65
+ caller(1)
66
+ )
67
+ EMAIL_NONASCII
40
68
  end
41
69
 
42
- # A simple regular expression that only allows for letters, numbers, spaces, and .-_@+. Just a standard login / username
43
- # regular expression.
70
+ # @deprecated
44
71
  def self.login
45
- /\A[a-zA-Z0-9_][a-zA-Z0-9\.+\-_@ ]+\z/
72
+ ::ActiveSupport::Deprecation.warn(
73
+ "Authlogic::Regex.login is deprecated, use Authlogic::Regex::LOGIN",
74
+ caller(1)
75
+ )
76
+ LOGIN
46
77
  end
47
78
  end
48
79
  end
@@ -1,4 +1,4 @@
1
- require 'request_store'
1
+ require "request_store"
2
2
 
3
3
  module Authlogic
4
4
  module Session
@@ -9,8 +9,11 @@ module Authlogic
9
9
  # you are using a supported framework, Authlogic takes care of this for you.
10
10
  module Activation
11
11
  class NotActivatedError < ::StandardError # :nodoc:
12
- def initialize(session)
13
- 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
+ )
14
17
  end
15
18
  end
16
19
 
@@ -55,15 +58,15 @@ module Authlogic
55
58
  module InstanceMethods
56
59
  # Making sure we are activated before we start creating objects
57
60
  def initialize(*args)
58
- raise NotActivatedError.new(self) unless self.class.activated?
61
+ raise NotActivatedError unless self.class.activated?
59
62
  super
60
63
  end
61
64
 
62
65
  private
63
66
 
64
- def controller
65
- self.class.controller
66
- end
67
+ def controller
68
+ self.class.controller
69
+ end
67
70
  end
68
71
  end
69
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,11 +23,12 @@ 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
@@ -34,7 +37,8 @@ module Authlogic
34
37
  end
35
38
 
36
39
  module InstanceMethods
37
- # 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.
38
42
  def new_record?
39
43
  new_session?
40
44
  end
@@ -1,7 +1,18 @@
1
1
  module Authlogic
2
2
  module Session # :nodoc:
3
- # This is the base class Authlogic, where all modules are included. For information on functionality 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
@@ -15,8 +26,8 @@ module Authlogic
15
26
  include Session
16
27
  include HttpAuth
17
28
 
18
- # Included in a specific order so magic states gets ran after a record is found
19
- # TODO: What does "magic states gets ran" mean? Be specific.
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.
20
31
  include Password
21
32
  include UnauthorizedRecord
22
33
  include MagicStates
@@ -25,8 +25,8 @@ module Authlogic
25
25
  klass.class_eval do
26
26
  extend Config
27
27
  include InstanceMethods
28
- validate :reset_failed_login_count, :if => :reset_failed_login_count?
29
- 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?
30
30
  end
31
31
  end
32
32
 
@@ -73,47 +73,54 @@ module Authlogic
73
73
  # with configuration. By default they will be banned for 2 hours. During
74
74
  # that 2 hour period this method will return true.
75
75
  def being_brute_force_protected?
76
- exceeded_failed_logins_limit? && (failed_login_ban_for <= 0 ||
77
- (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
+ )
78
82
  end
79
83
 
80
84
  private
81
85
 
82
- def exceeded_failed_logins_limit?
83
- !attempted_record.nil? && attempted_record.respond_to?(:failed_login_count) && consecutive_failed_logins_limit > 0 &&
84
- attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
85
- end
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
86
93
 
87
- def reset_failed_login_count?
88
- exceeded_failed_logins_limit? && !being_brute_force_protected?
89
- end
94
+ def reset_failed_login_count?
95
+ exceeded_failed_logins_limit? && !being_brute_force_protected?
96
+ end
90
97
 
91
- def reset_failed_login_count
92
- attempted_record.failed_login_count = 0
93
- end
98
+ def reset_failed_login_count
99
+ attempted_record.failed_login_count = 0
100
+ end
94
101
 
95
- def validate_failed_logins
96
- # Clear all other error messages, as they are irrelevant at this point and can
97
- # only provide additional information that is not needed
98
- errors.clear
99
- errors.add(
100
- :base,
101
- I18n.t(
102
- 'error_messages.consecutive_failed_logins_limit_exceeded',
103
- :default => "Consecutive failed logins limit exceeded, account has been" +
104
- (failed_login_ban_for == 0 ? "" : " temporarily") +
105
- " disabled."
106
- )
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."
107
113
  )
108
- end
114
+ )
115
+ end
109
116
 
110
- def consecutive_failed_logins_limit
111
- self.class.consecutive_failed_logins_limit
112
- end
117
+ def consecutive_failed_logins_limit
118
+ self.class.consecutive_failed_logins_limit
119
+ end
113
120
 
114
- def failed_login_ban_for
115
- self.class.failed_login_ban_for
116
- end
121
+ def failed_login_ban_for
122
+ self.class.failed_login_ban_for
123
+ end
117
124
  end
118
125
  end
119
126
  end