authlogic 3.8.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
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