authlogic 5.0.4 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/authlogic.rb +1 -0
- data/lib/authlogic/acts_as_authentic/base.rb +16 -1
- data/lib/authlogic/acts_as_authentic/password.rb +15 -5
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +5 -3
- data/lib/authlogic/controller_adapters/rails_adapter.rb +1 -1
- data/lib/authlogic/crypto_providers/md5.rb +3 -0
- data/lib/authlogic/crypto_providers/md5/v2.rb +35 -0
- data/lib/authlogic/crypto_providers/sha1.rb +3 -0
- data/lib/authlogic/crypto_providers/sha1/v2.rb +41 -0
- data/lib/authlogic/crypto_providers/sha256.rb +3 -0
- data/lib/authlogic/crypto_providers/sha256/v2.rb +58 -0
- data/lib/authlogic/crypto_providers/sha512.rb +3 -0
- data/lib/authlogic/crypto_providers/sha512/v2.rb +39 -0
- data/lib/authlogic/errors.rb +50 -0
- data/lib/authlogic/i18n/translator.rb +1 -1
- data/lib/authlogic/session/base.rb +111 -55
- data/lib/authlogic/test_case.rb +1 -0
- data/lib/authlogic/test_case/mock_api_controller.rb +52 -0
- data/lib/authlogic/test_case/mock_controller.rb +1 -1
- data/lib/authlogic/test_case/mock_cookie_jar.rb +37 -0
- data/lib/authlogic/test_case/mock_request.rb +4 -0
- data/lib/authlogic/version.rb +1 -1
- metadata +33 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3db4f35b09d1723bab91b36afb8fbd79c1583896b19186846f8b1b25cb7793e
|
4
|
+
data.tar.gz: a517af1c9f5341e9bd58722711f7046fb51dfd2c1440e072f81170be196d2518
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd2fa0ad62c54eb721a8d3fb1d85ca1aa59b122bed688eca908a4cde2487fce1a5c084ffa365fd3b975d576f99a6a86bd243f950a1f2d07ddc1b6f171afed345
|
7
|
+
data.tar.gz: 519fcf4568fee21a0a43c9f7ec5ea740edcb84cf5cb95f48bf5a1819a1c091ba882f2f54f169497c20cf3821eb556a537687330950643b1b7d4f0d2a138961f0
|
data/lib/authlogic.rb
CHANGED
@@ -31,8 +31,8 @@ module Authlogic
|
|
31
31
|
#
|
32
32
|
# See the various sub modules for the configuration they provide.
|
33
33
|
def acts_as_authentic
|
34
|
-
return unless db_setup?
|
35
34
|
yield self if block_given?
|
35
|
+
return unless db_setup?
|
36
36
|
acts_as_authentic_modules.each { |mod| include mod }
|
37
37
|
end
|
38
38
|
|
@@ -65,12 +65,27 @@ module Authlogic
|
|
65
65
|
self.acts_as_authentic_modules = modules
|
66
66
|
end
|
67
67
|
|
68
|
+
# Some Authlogic modules requires a database connection with a existing
|
69
|
+
# users table by the moment when you call the `acts_as_authentic`
|
70
|
+
# method. If you try to call `acts_as_authentic` without a database
|
71
|
+
# connection, it will raise a `Authlogic::ModelSetupError`.
|
72
|
+
#
|
73
|
+
# If you rely on the User model before the database is setup correctly,
|
74
|
+
# set this field to false.
|
75
|
+
# * <tt>Default:</tt> false
|
76
|
+
# * <tt>Accepts:</tt> Boolean
|
77
|
+
def raise_on_model_setup_error(value = nil)
|
78
|
+
rw_config(:raise_on_model_setup_error, value, false)
|
79
|
+
end
|
80
|
+
alias raise_on_model_setup_error= raise_on_model_setup_error
|
81
|
+
|
68
82
|
private
|
69
83
|
|
70
84
|
def db_setup?
|
71
85
|
column_names
|
72
86
|
true
|
73
87
|
rescue StandardError
|
88
|
+
raise ModelSetupError if raise_on_model_setup_error
|
74
89
|
false
|
75
90
|
end
|
76
91
|
|
@@ -102,20 +102,30 @@ module Authlogic
|
|
102
102
|
# The family of adaptive hash functions (BCrypt, SCrypt, PBKDF2) is the
|
103
103
|
# best choice for password storage today. We recommend SCrypt. Other
|
104
104
|
# one-way functions like SHA512 are inferior, but widely used.
|
105
|
-
#
|
105
|
+
# Reversible functions like AES256 are the worst choice, and we no
|
106
106
|
# longer support them.
|
107
107
|
#
|
108
108
|
# You can use the `transition_from_crypto_providers` option to gradually
|
109
109
|
# transition to a better crypto provider without causing your users any
|
110
110
|
# pain.
|
111
111
|
#
|
112
|
-
# * <tt>Default:</tt>
|
112
|
+
# * <tt>Default:</tt> There is no longer a default value. Prior to
|
113
|
+
# Authlogic 6, the default was `CryptoProviders::SCrypt`. If you try
|
114
|
+
# to read this config option before setting it, it will raise a
|
115
|
+
# `NilCryptoProvider` error. See that error's message for further
|
116
|
+
# details, and rationale for this change.
|
113
117
|
# * <tt>Accepts:</tt> Class
|
114
|
-
def crypto_provider
|
118
|
+
def crypto_provider
|
119
|
+
acts_as_authentic_config[:crypto_provider].tap { |provider|
|
120
|
+
raise NilCryptoProvider if provider.nil?
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def crypto_provider=(value)
|
125
|
+
raise NilCryptoProvider if value.nil?
|
115
126
|
CryptoProviders::Guidance.new(value).impart_wisdom
|
116
|
-
rw_config(:crypto_provider, value
|
127
|
+
rw_config(:crypto_provider, value)
|
117
128
|
end
|
118
|
-
alias crypto_provider= crypto_provider
|
119
129
|
|
120
130
|
# Let's say you originally encrypted your passwords with Sha1. Sha1 is
|
121
131
|
# starting to join the party with MD5 and you want to switch to
|
@@ -93,9 +93,9 @@ module Authlogic
|
|
93
93
|
end
|
94
94
|
|
95
95
|
# Save the record and skip session maintenance all together.
|
96
|
-
def save_without_session_maintenance(
|
96
|
+
def save_without_session_maintenance(**options)
|
97
97
|
self.skip_session_maintenance = true
|
98
|
-
result = save(
|
98
|
+
result = save(**options)
|
99
99
|
self.skip_session_maintenance = false
|
100
100
|
result
|
101
101
|
end
|
@@ -176,7 +176,9 @@ module Authlogic
|
|
176
176
|
end
|
177
177
|
|
178
178
|
def log_in_after_password_change?
|
179
|
-
|
179
|
+
persisted? &&
|
180
|
+
will_save_change_to_persistence_token? &&
|
181
|
+
self.class.log_in_after_password_change
|
180
182
|
end
|
181
183
|
end
|
182
184
|
end
|
@@ -14,7 +14,7 @@ module Authlogic
|
|
14
14
|
# Returns a `ActionDispatch::Cookies::CookieJar`. See the AC guide
|
15
15
|
# http://guides.rubyonrails.org/action_controller_overview.html#cookies
|
16
16
|
def cookies
|
17
|
-
controller.send(:cookies)
|
17
|
+
controller.respond_to?(:cookies, true) ? controller.send(:cookies) : nil
|
18
18
|
end
|
19
19
|
|
20
20
|
def cookie_domain
|
@@ -6,6 +6,9 @@ module Authlogic
|
|
6
6
|
module CryptoProviders
|
7
7
|
# A poor choice. There are known attacks against this algorithm.
|
8
8
|
class MD5
|
9
|
+
# V2 hashes the digest bytes in repeated stretches instead of hex characters.
|
10
|
+
autoload :V2, File.join(__dir__, "md5", "v2")
|
11
|
+
|
9
12
|
class << self
|
10
13
|
attr_accessor :join_token
|
11
14
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/md5"
|
4
|
+
|
5
|
+
module Authlogic
|
6
|
+
module CryptoProviders
|
7
|
+
class MD5
|
8
|
+
# A poor choice. There are known attacks against this algorithm.
|
9
|
+
class V2
|
10
|
+
class << self
|
11
|
+
attr_accessor :join_token
|
12
|
+
|
13
|
+
# The number of times to loop through the encryption.
|
14
|
+
def stretches
|
15
|
+
@stretches ||= 1
|
16
|
+
end
|
17
|
+
attr_writer :stretches
|
18
|
+
|
19
|
+
# Turns your raw password into a MD5 hash.
|
20
|
+
def encrypt(*tokens)
|
21
|
+
digest = tokens.flatten.join(join_token)
|
22
|
+
stretches.times { digest = Digest::MD5.digest(digest) }
|
23
|
+
digest.unpack1("H*")
|
24
|
+
end
|
25
|
+
|
26
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
27
|
+
# were used to encrypt.
|
28
|
+
def matches?(crypted, *tokens)
|
29
|
+
encrypt(*tokens) == crypted
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -6,6 +6,9 @@ module Authlogic
|
|
6
6
|
module CryptoProviders
|
7
7
|
# A poor choice. There are known attacks against this algorithm.
|
8
8
|
class Sha1
|
9
|
+
# V2 hashes the digest bytes in repeated stretches instead of hex characters.
|
10
|
+
autoload :V2, File.join(__dir__, "sha1", "v2")
|
11
|
+
|
9
12
|
class << self
|
10
13
|
def join_token
|
11
14
|
@join_token ||= "--"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/sha1"
|
4
|
+
|
5
|
+
module Authlogic
|
6
|
+
module CryptoProviders
|
7
|
+
class Sha1
|
8
|
+
# A poor choice. There are known attacks against this algorithm.
|
9
|
+
class V2
|
10
|
+
class << self
|
11
|
+
def join_token
|
12
|
+
@join_token ||= "--"
|
13
|
+
end
|
14
|
+
attr_writer :join_token
|
15
|
+
|
16
|
+
# The number of times to loop through the encryption.
|
17
|
+
def stretches
|
18
|
+
@stretches ||= 10
|
19
|
+
end
|
20
|
+
attr_writer :stretches
|
21
|
+
|
22
|
+
# Turns your raw password into a Sha1 hash.
|
23
|
+
def encrypt(*tokens)
|
24
|
+
tokens = tokens.flatten
|
25
|
+
digest = tokens.shift
|
26
|
+
stretches.times do
|
27
|
+
digest = Digest::SHA1.digest([digest, *tokens].join(join_token))
|
28
|
+
end
|
29
|
+
digest.unpack1("H*")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
33
|
+
# were used to encrypt.
|
34
|
+
def matches?(crypted, *tokens)
|
35
|
+
encrypt(*tokens) == crypted
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -29,6 +29,9 @@ module Authlogic
|
|
29
29
|
#
|
30
30
|
# Uses the Sha256 hash algorithm to encrypt passwords.
|
31
31
|
class Sha256
|
32
|
+
# V2 hashes the digest bytes in repeated stretches instead of hex characters.
|
33
|
+
autoload :V2, File.join(__dir__, "sha256", "v2")
|
34
|
+
|
32
35
|
class << self
|
33
36
|
attr_accessor :join_token
|
34
37
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/sha2"
|
4
|
+
|
5
|
+
module Authlogic
|
6
|
+
# The acts_as_authentic method has a crypto_provider option. This allows you
|
7
|
+
# to use any type of encryption you like. Just create a class with a class
|
8
|
+
# level encrypt and matches? method. See example below.
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# class MyAwesomeEncryptionMethod
|
13
|
+
# def self.encrypt(*tokens)
|
14
|
+
# # the tokens passed will be an array of objects, what type of object
|
15
|
+
# # is irrelevant, just do what you need to do with them and return a
|
16
|
+
# # single encrypted string. for example, you will most likely join all
|
17
|
+
# # of the objects into a single string and then encrypt that string
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def self.matches?(crypted, *tokens)
|
21
|
+
# # return true if the crypted string matches the tokens. Depending on
|
22
|
+
# # your algorithm you might decrypt the string then compare it to the
|
23
|
+
# # token, or you might encrypt the tokens and make sure it matches the
|
24
|
+
# # crypted string, its up to you.
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
module CryptoProviders
|
28
|
+
class Sha256
|
29
|
+
# = Sha256
|
30
|
+
#
|
31
|
+
# Uses the Sha256 hash algorithm to encrypt passwords.
|
32
|
+
class V2
|
33
|
+
class << self
|
34
|
+
attr_accessor :join_token
|
35
|
+
|
36
|
+
# The number of times to loop through the encryption.
|
37
|
+
def stretches
|
38
|
+
@stretches ||= 20
|
39
|
+
end
|
40
|
+
attr_writer :stretches
|
41
|
+
|
42
|
+
# Turns your raw password into a Sha256 hash.
|
43
|
+
def encrypt(*tokens)
|
44
|
+
digest = tokens.flatten.join(join_token)
|
45
|
+
stretches.times { digest = Digest::SHA256.digest(digest) }
|
46
|
+
digest.unpack1("H*")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
50
|
+
# were used to encrypt.
|
51
|
+
def matches?(crypted, *tokens)
|
52
|
+
encrypt(*tokens) == crypted
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -8,6 +8,9 @@ module Authlogic
|
|
8
8
|
# there are better choices. We recommend transitioning to a more secure,
|
9
9
|
# adaptive hashing algorithm, like scrypt.
|
10
10
|
class Sha512
|
11
|
+
# V2 hashes the digest bytes in repeated stretches instead of hex characters.
|
12
|
+
autoload :V2, File.join(__dir__, "sha512", "v2")
|
13
|
+
|
11
14
|
class << self
|
12
15
|
attr_accessor :join_token
|
13
16
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/sha2"
|
4
|
+
|
5
|
+
module Authlogic
|
6
|
+
module CryptoProviders
|
7
|
+
class Sha512
|
8
|
+
# SHA-512 does not have any practical known attacks against it. However,
|
9
|
+
# there are better choices. We recommend transitioning to a more secure,
|
10
|
+
# adaptive hashing algorithm, like scrypt.
|
11
|
+
class V2
|
12
|
+
class << self
|
13
|
+
attr_accessor :join_token
|
14
|
+
|
15
|
+
# The number of times to loop through the encryption.
|
16
|
+
def stretches
|
17
|
+
@stretches ||= 20
|
18
|
+
end
|
19
|
+
attr_writer :stretches
|
20
|
+
|
21
|
+
# Turns your raw password into a Sha512 hash.
|
22
|
+
def encrypt(*tokens)
|
23
|
+
digest = tokens.flatten.join(join_token)
|
24
|
+
stretches.times do
|
25
|
+
digest = Digest::SHA512.digest(digest)
|
26
|
+
end
|
27
|
+
digest.unpack1("H*")
|
28
|
+
end
|
29
|
+
|
30
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
31
|
+
# were used to encrypt.
|
32
|
+
def matches?(crypted, *tokens)
|
33
|
+
encrypt(*tokens) == crypted
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Authlogic
|
4
|
+
# Parent class of all Authlogic errors.
|
5
|
+
class Error < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
# :nodoc:
|
9
|
+
class InvalidCryptoProvider < Error
|
10
|
+
end
|
11
|
+
|
12
|
+
# :nodoc:
|
13
|
+
class NilCryptoProvider < InvalidCryptoProvider
|
14
|
+
def message
|
15
|
+
<<~EOS
|
16
|
+
In version 5, Authlogic used SCrypt by default. As of version 6, there
|
17
|
+
is no default. We still recommend SCrypt. If you previously relied on
|
18
|
+
this default, then, in your User model (or equivalent), please set the
|
19
|
+
following:
|
20
|
+
|
21
|
+
acts_as_authentic do |c|
|
22
|
+
c.crypto_provider = ::Authlogic::CryptoProviders::SCrypt
|
23
|
+
end
|
24
|
+
|
25
|
+
Furthermore, the authlogic gem no longer depends on the scrypt gem. In
|
26
|
+
your Gemfile, please add scrypt.
|
27
|
+
|
28
|
+
gem "scrypt", "~> 3.0"
|
29
|
+
|
30
|
+
We have made this change in Authlogic 6 so that users of other crypto
|
31
|
+
providers no longer need to install the scrypt gem.
|
32
|
+
EOS
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# :nodoc:
|
37
|
+
class ModelSetupError < Error
|
38
|
+
def message
|
39
|
+
<<-EOS
|
40
|
+
You must establish a database connection and run the migrations before
|
41
|
+
using acts_as_authentic. If you need to load the User model before the
|
42
|
+
database is set up correctly, please set the following:
|
43
|
+
|
44
|
+
acts_as_authentic do |c|
|
45
|
+
c.raise_on_model_setup_error = false
|
46
|
+
end
|
47
|
+
EOS
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -198,7 +198,7 @@ module Authlogic
|
|
198
198
|
# 2. Enable logging out on timeouts
|
199
199
|
#
|
200
200
|
# class UserSession < Authlogic::Session::Base
|
201
|
-
# logout_on_timeout true # default
|
201
|
+
# logout_on_timeout true # default is false
|
202
202
|
# end
|
203
203
|
#
|
204
204
|
# This will require a user to log back in if they are inactive for more than
|
@@ -351,7 +351,7 @@ module Authlogic
|
|
351
351
|
- https://github.com/binarylogic/authlogic/pull/558
|
352
352
|
- https://github.com/binarylogic/authlogic/pull/577
|
353
353
|
EOS
|
354
|
-
VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict"].freeze
|
354
|
+
VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict", "None"].freeze
|
355
355
|
|
356
356
|
# Callbacks
|
357
357
|
# =========
|
@@ -415,10 +415,10 @@ module Authlogic
|
|
415
415
|
before_save :set_last_request_at
|
416
416
|
|
417
417
|
after_save :reset_perishable_token!
|
418
|
-
after_save :save_cookie
|
418
|
+
after_save :save_cookie, if: :cookie_enabled?
|
419
419
|
after_save :update_session
|
420
420
|
|
421
|
-
after_destroy :destroy_cookie
|
421
|
+
after_destroy :destroy_cookie, if: :cookie_enabled?
|
422
422
|
after_destroy :update_session
|
423
423
|
|
424
424
|
# `validate` callbacks, in deliberate order. For example,
|
@@ -438,8 +438,7 @@ module Authlogic
|
|
438
438
|
|
439
439
|
class << self
|
440
440
|
attr_accessor(
|
441
|
-
:configured_password_methods
|
442
|
-
:configured_klass_methods
|
441
|
+
:configured_password_methods
|
443
442
|
)
|
444
443
|
end
|
445
444
|
attr_accessor(
|
@@ -472,14 +471,9 @@ module Authlogic
|
|
472
471
|
!controller.nil?
|
473
472
|
end
|
474
473
|
|
475
|
-
#
|
474
|
+
# Allow users to log in via HTTP basic authentication.
|
476
475
|
#
|
477
|
-
#
|
478
|
-
# disabled is if you are not comfortable having your users provide their
|
479
|
-
# raw username and password. Whatever the reason, you can disable it
|
480
|
-
# here.
|
481
|
-
#
|
482
|
-
# * <tt>Default:</tt> true
|
476
|
+
# * <tt>Default:</tt> false
|
483
477
|
# * <tt>Accepts:</tt> Boolean
|
484
478
|
def allow_http_basic_auth(value = nil)
|
485
479
|
rw_config(:allow_http_basic_auth, value, false)
|
@@ -954,13 +948,27 @@ module Authlogic
|
|
954
948
|
# Should the cookie be signed? If the controller adapter supports it, this is a
|
955
949
|
# measure against cookie tampering.
|
956
950
|
def sign_cookie(value = nil)
|
957
|
-
if value && !controller.cookies.respond_to?(:signed)
|
951
|
+
if value && controller && !controller.cookies.respond_to?(:signed)
|
958
952
|
raise "Signed cookies not supported with #{controller.class}!"
|
959
953
|
end
|
960
954
|
rw_config(:sign_cookie, value, false)
|
961
955
|
end
|
962
956
|
alias sign_cookie= sign_cookie
|
963
957
|
|
958
|
+
# Should the cookie be encrypted? If the controller adapter supports it, this is a
|
959
|
+
# measure to hide the contents of the cookie (e.g. persistence_token)
|
960
|
+
def encrypt_cookie(value = nil)
|
961
|
+
if value && controller && !controller.cookies.respond_to?(:encrypted)
|
962
|
+
raise "Encrypted cookies not supported with #{controller.class}!"
|
963
|
+
end
|
964
|
+
if value && sign_cookie
|
965
|
+
raise "It is recommended to use encrypt_cookie instead of sign_cookie. " \
|
966
|
+
"You may not enable both options."
|
967
|
+
end
|
968
|
+
rw_config(:encrypt_cookie, value, false)
|
969
|
+
end
|
970
|
+
alias encrypt_cookie= encrypt_cookie
|
971
|
+
|
964
972
|
# Works exactly like cookie_key, but for sessions. See cookie_key for more info.
|
965
973
|
#
|
966
974
|
# * <tt>Default:</tt> cookie_key
|
@@ -1065,24 +1073,10 @@ module Authlogic
|
|
1065
1073
|
# Constructor
|
1066
1074
|
# ===========
|
1067
1075
|
|
1068
|
-
# rubocop:disable Metrics/AbcSize
|
1069
1076
|
def initialize(*args)
|
1070
1077
|
@id = nil
|
1071
1078
|
self.scope = self.class.scope
|
1072
|
-
|
1073
|
-
# Creating an alias method for the "record" method based on the klass
|
1074
|
-
# name, so that we can do:
|
1075
|
-
#
|
1076
|
-
# session.user
|
1077
|
-
#
|
1078
|
-
# instead of:
|
1079
|
-
#
|
1080
|
-
# session.record
|
1081
|
-
unless self.class.configured_klass_methods
|
1082
|
-
self.class.send(:alias_method, klass_name.demodulize.underscore.to_sym, :record)
|
1083
|
-
self.class.configured_klass_methods = true
|
1084
|
-
end
|
1085
|
-
|
1079
|
+
define_record_alias_method
|
1086
1080
|
raise Activation::NotActivatedError unless self.class.activated?
|
1087
1081
|
unless self.class.configured_password_methods
|
1088
1082
|
configure_password_methods
|
@@ -1091,7 +1085,6 @@ module Authlogic
|
|
1091
1085
|
instance_variable_set("@#{password_field}", nil)
|
1092
1086
|
self.credentials = args
|
1093
1087
|
end
|
1094
|
-
# rubocop:enable Metrics/AbcSize
|
1095
1088
|
|
1096
1089
|
# Public instance methods
|
1097
1090
|
# =======================
|
@@ -1480,6 +1473,23 @@ module Authlogic
|
|
1480
1473
|
sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
|
1481
1474
|
end
|
1482
1475
|
|
1476
|
+
# If the cookie should be encrypted
|
1477
|
+
def encrypt_cookie
|
1478
|
+
return @encrypt_cookie if defined?(@encrypt_cookie)
|
1479
|
+
@encrypt_cookie = self.class.encrypt_cookie
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
# Accepts a boolean as to whether the cookie should be encrypted. If true
|
1483
|
+
# the cookie will be saved in an encrypted state.
|
1484
|
+
def encrypt_cookie=(value)
|
1485
|
+
@encrypt_cookie = value
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
# See encrypt_cookie
|
1489
|
+
def encrypt_cookie?
|
1490
|
+
encrypt_cookie == true || encrypt_cookie == "true" || encrypt_cookie == "1"
|
1491
|
+
end
|
1492
|
+
|
1483
1493
|
# The scope of the current object
|
1484
1494
|
def scope
|
1485
1495
|
@scope ||= {}
|
@@ -1497,24 +1507,21 @@ module Authlogic
|
|
1497
1507
|
# Determines if the information you provided for authentication is valid
|
1498
1508
|
# or not. If there is a problem with the information provided errors will
|
1499
1509
|
# be added to the errors object and this method will return false.
|
1510
|
+
#
|
1511
|
+
# @api public
|
1500
1512
|
def valid?
|
1501
1513
|
errors.clear
|
1502
1514
|
self.attempted_record = nil
|
1503
|
-
|
1504
|
-
run_callbacks(:before_validation)
|
1505
|
-
run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
|
1515
|
+
run_the_before_validation_callbacks
|
1506
1516
|
|
1507
1517
|
# Run the `validate` callbacks, eg. `validate_by_password`.
|
1508
1518
|
# This is when `attempted_record` is set.
|
1509
1519
|
run_callbacks(:validate)
|
1510
1520
|
|
1511
1521
|
ensure_authentication_attempted
|
1512
|
-
|
1513
1522
|
if errors.empty?
|
1514
|
-
|
1515
|
-
run_callbacks(:after_validation)
|
1523
|
+
run_the_after_validation_callbacks
|
1516
1524
|
end
|
1517
|
-
|
1518
1525
|
save_record(attempted_record)
|
1519
1526
|
errors.empty?
|
1520
1527
|
end
|
@@ -1616,14 +1623,22 @@ module Authlogic
|
|
1616
1623
|
# @api private
|
1617
1624
|
# @return ::Authlogic::CookieCredentials or if no cookie is found, nil
|
1618
1625
|
def cookie_credentials
|
1626
|
+
return unless cookie_enabled?
|
1627
|
+
|
1619
1628
|
cookie_value = cookie_jar[cookie_key]
|
1620
1629
|
unless cookie_value.nil?
|
1621
1630
|
::Authlogic::CookieCredentials.parse(cookie_value)
|
1622
1631
|
end
|
1623
1632
|
end
|
1624
1633
|
|
1634
|
+
def cookie_enabled?
|
1635
|
+
!controller.cookies.nil?
|
1636
|
+
end
|
1637
|
+
|
1625
1638
|
def cookie_jar
|
1626
|
-
if self.class.
|
1639
|
+
if self.class.encrypt_cookie
|
1640
|
+
controller.cookies.encrypted
|
1641
|
+
elsif self.class.sign_cookie
|
1627
1642
|
controller.cookies.signed
|
1628
1643
|
else
|
1629
1644
|
controller.cookies
|
@@ -1641,15 +1656,23 @@ module Authlogic
|
|
1641
1656
|
self.class.send(:attr_reader, login_field) unless respond_to?(login_field)
|
1642
1657
|
end
|
1643
1658
|
|
1659
|
+
# @api private
|
1644
1660
|
def define_password_field_methods
|
1645
1661
|
return unless password_field
|
1646
|
-
|
1647
|
-
|
1662
|
+
define_password_field_writer_method
|
1663
|
+
define_password_field_reader_methods
|
1664
|
+
end
|
1648
1665
|
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1666
|
+
# The password should not be accessible publicly. This way forms using
|
1667
|
+
# form_for don't fill the password with the attempted password. To prevent
|
1668
|
+
# this we just create this method that is private.
|
1669
|
+
#
|
1670
|
+
# @api private
|
1671
|
+
def define_password_field_reader_methods
|
1672
|
+
unless respond_to?(password_field)
|
1673
|
+
# Deliberate no-op method, see rationale above.
|
1674
|
+
self.class.send(:define_method, password_field) {}
|
1675
|
+
end
|
1653
1676
|
self.class.class_eval(
|
1654
1677
|
<<-EOS, __FILE__, __LINE__ + 1
|
1655
1678
|
private
|
@@ -1660,6 +1683,28 @@ module Authlogic
|
|
1660
1683
|
)
|
1661
1684
|
end
|
1662
1685
|
|
1686
|
+
def define_password_field_writer_method
|
1687
|
+
unless respond_to?("#{password_field}=")
|
1688
|
+
self.class.send(:attr_writer, password_field)
|
1689
|
+
end
|
1690
|
+
end
|
1691
|
+
|
1692
|
+
# Creating an alias method for the "record" method based on the klass
|
1693
|
+
# name, so that we can do:
|
1694
|
+
#
|
1695
|
+
# session.user
|
1696
|
+
#
|
1697
|
+
# instead of:
|
1698
|
+
#
|
1699
|
+
# session.record
|
1700
|
+
#
|
1701
|
+
# @api private
|
1702
|
+
def define_record_alias_method
|
1703
|
+
noun = klass_name.demodulize.underscore.to_sym
|
1704
|
+
return if respond_to?(noun)
|
1705
|
+
self.class.send(:alias_method, noun, :record)
|
1706
|
+
end
|
1707
|
+
|
1663
1708
|
def destroy_cookie
|
1664
1709
|
controller.cookies.delete cookie_key, domain: controller.cookie_domain
|
1665
1710
|
end
|
@@ -1705,13 +1750,8 @@ module Authlogic
|
|
1705
1750
|
|
1706
1751
|
# @api private
|
1707
1752
|
def generate_cookie_for_saving
|
1708
|
-
creds = ::Authlogic::CookieCredentials.new(
|
1709
|
-
record.persistence_token,
|
1710
|
-
record.send(record.class.primary_key),
|
1711
|
-
remember_me? ? remember_me_until : nil
|
1712
|
-
)
|
1713
1753
|
{
|
1714
|
-
value:
|
1754
|
+
value: generate_cookie_value.to_s,
|
1715
1755
|
expires: remember_me_until,
|
1716
1756
|
secure: secure,
|
1717
1757
|
httponly: httponly,
|
@@ -1720,6 +1760,14 @@ module Authlogic
|
|
1720
1760
|
}
|
1721
1761
|
end
|
1722
1762
|
|
1763
|
+
def generate_cookie_value
|
1764
|
+
::Authlogic::CookieCredentials.new(
|
1765
|
+
record.persistence_token,
|
1766
|
+
record.send(record.class.primary_key),
|
1767
|
+
remember_me? ? remember_me_until : nil
|
1768
|
+
)
|
1769
|
+
end
|
1770
|
+
|
1723
1771
|
# Returns a Proc to be executed by
|
1724
1772
|
# `ActionController::HttpAuthentication::Basic` when credentials are
|
1725
1773
|
# present in the HTTP request.
|
@@ -1898,6 +1946,18 @@ module Authlogic
|
|
1898
1946
|
attempted_record.failed_login_count = 0
|
1899
1947
|
end
|
1900
1948
|
|
1949
|
+
# @api private
|
1950
|
+
def run_the_after_validation_callbacks
|
1951
|
+
run_callbacks(new_session? ? :after_validation_on_create : :after_validation_on_update)
|
1952
|
+
run_callbacks(:after_validation)
|
1953
|
+
end
|
1954
|
+
|
1955
|
+
# @api private
|
1956
|
+
def run_the_before_validation_callbacks
|
1957
|
+
run_callbacks(:before_validation)
|
1958
|
+
run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
|
1959
|
+
end
|
1960
|
+
|
1901
1961
|
# `args[0]` is the name of a model method, like
|
1902
1962
|
# `find_by_single_access_token` or `find_by_smart_case_login_field`.
|
1903
1963
|
def search_for_record(*args)
|
@@ -1935,11 +1995,7 @@ module Authlogic
|
|
1935
1995
|
end
|
1936
1996
|
|
1937
1997
|
def save_cookie
|
1938
|
-
|
1939
|
-
controller.cookies.signed[cookie_key] = generate_cookie_for_saving
|
1940
|
-
else
|
1941
|
-
controller.cookies[cookie_key] = generate_cookie_for_saving
|
1942
|
-
end
|
1998
|
+
cookie_jar[cookie_key] = generate_cookie_for_saving
|
1943
1999
|
end
|
1944
2000
|
|
1945
2001
|
# @api private
|
data/lib/authlogic/test_case.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + "/test_case/rails_request_adapter"
|
4
|
+
require File.dirname(__FILE__) + "/test_case/mock_api_controller"
|
4
5
|
require File.dirname(__FILE__) + "/test_case/mock_cookie_jar"
|
5
6
|
require File.dirname(__FILE__) + "/test_case/mock_controller"
|
6
7
|
require File.dirname(__FILE__) + "/test_case/mock_logger"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Authlogic
|
4
|
+
module TestCase
|
5
|
+
# Basically acts like an API controller but doesn't do anything.
|
6
|
+
# Authlogic can interact with this, do it's thing and then you can look at
|
7
|
+
# the controller object to see if anything changed.
|
8
|
+
class MockAPIController < ControllerAdapters::AbstractAdapter
|
9
|
+
attr_writer :request_content_type
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
end
|
13
|
+
|
14
|
+
# Expected API controller has no cookies method.
|
15
|
+
undef :cookies
|
16
|
+
|
17
|
+
def cookie_domain
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def logger
|
22
|
+
@logger ||= MockLogger.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def params
|
26
|
+
@params ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def request
|
30
|
+
@request ||= MockRequest.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def request_content_type
|
34
|
+
@request_content_type ||= "text/html"
|
35
|
+
end
|
36
|
+
|
37
|
+
def session
|
38
|
+
@session ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
# If method is defined, it causes below behavior...
|
42
|
+
# controller = Authlogic::ControllerAdapters::RailsAdapter.new(
|
43
|
+
# Authlogic::TestCase::MockAPIController.new
|
44
|
+
# )
|
45
|
+
# controller.responds_to_single_access_allowed? #=> true
|
46
|
+
# controller.single_access_allowed?
|
47
|
+
# #=> NoMethodError: undefined method `single_access_allowed?' for nil:NilClass
|
48
|
+
#
|
49
|
+
undef :single_access_allowed?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -23,6 +23,10 @@ module Authlogic
|
|
23
23
|
def signed
|
24
24
|
@signed ||= MockSignedCookieJar.new(self)
|
25
25
|
end
|
26
|
+
|
27
|
+
def encrypted
|
28
|
+
@encrypted ||= MockEncryptedCookieJar.new(self)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
# A mock of `ActionDispatch::Cookies::SignedKeyRotatingCookieJar`
|
@@ -35,6 +39,7 @@ module Authlogic
|
|
35
39
|
|
36
40
|
def initialize(parent_jar)
|
37
41
|
@parent_jar = parent_jar
|
42
|
+
parent_jar.each { |k, v| self[k] = v }
|
38
43
|
end
|
39
44
|
|
40
45
|
def [](val)
|
@@ -51,5 +56,37 @@ module Authlogic
|
|
51
56
|
@parent_jar[key] = options
|
52
57
|
end
|
53
58
|
end
|
59
|
+
|
60
|
+
# Which ActionDispatch class is this a mock of?
|
61
|
+
# TODO: Document as with other mocks above.
|
62
|
+
class MockEncryptedCookieJar < MockCookieJar
|
63
|
+
attr_reader :parent_jar # helper for testing
|
64
|
+
|
65
|
+
def initialize(parent_jar)
|
66
|
+
@parent_jar = parent_jar
|
67
|
+
parent_jar.each { |k, v| self[k] = v }
|
68
|
+
end
|
69
|
+
|
70
|
+
def [](val)
|
71
|
+
encrypted_message = @parent_jar[val]
|
72
|
+
if encrypted_message
|
73
|
+
self.class.decrypt(encrypted_message)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def []=(key, options)
|
78
|
+
options[:value] = self.class.encrypt(options[:value])
|
79
|
+
@parent_jar[key] = options
|
80
|
+
end
|
81
|
+
|
82
|
+
# simple caesar cipher for testing
|
83
|
+
def self.encrypt(str)
|
84
|
+
str.unpack("U*").map(&:succ).pack("U*")
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.decrypt(str)
|
88
|
+
str.unpack("U*").map(&:pred).pack("U*")
|
89
|
+
end
|
90
|
+
end
|
54
91
|
end
|
55
92
|
end
|
data/lib/authlogic/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authlogic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-09-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activemodel
|
@@ -86,26 +86,6 @@ dependencies:
|
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '1.0'
|
89
|
-
- !ruby/object:Gem::Dependency
|
90
|
-
name: scrypt
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - ">="
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '1.2'
|
96
|
-
- - "<"
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
version: '4.0'
|
99
|
-
type: :runtime
|
100
|
-
prerelease: false
|
101
|
-
version_requirements: !ruby/object:Gem::Requirement
|
102
|
-
requirements:
|
103
|
-
- - ">="
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
version: '1.2'
|
106
|
-
- - "<"
|
107
|
-
- !ruby/object:Gem::Version
|
108
|
-
version: '4.0'
|
109
89
|
- !ruby/object:Gem::Dependency
|
110
90
|
name: bcrypt
|
111
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,14 +176,14 @@ dependencies:
|
|
196
176
|
requirements:
|
197
177
|
- - "~>"
|
198
178
|
- !ruby/object:Gem::Version
|
199
|
-
version: 0.
|
179
|
+
version: 0.80.1
|
200
180
|
type: :development
|
201
181
|
prerelease: false
|
202
182
|
version_requirements: !ruby/object:Gem::Requirement
|
203
183
|
requirements:
|
204
184
|
- - "~>"
|
205
185
|
- !ruby/object:Gem::Version
|
206
|
-
version: 0.
|
186
|
+
version: 0.80.1
|
207
187
|
- !ruby/object:Gem::Dependency
|
208
188
|
name: rubocop-performance
|
209
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -218,6 +198,26 @@ dependencies:
|
|
218
198
|
- - "~>"
|
219
199
|
- !ruby/object:Gem::Version
|
220
200
|
version: '1.1'
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: scrypt
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '1.2'
|
208
|
+
- - "<"
|
209
|
+
- !ruby/object:Gem::Version
|
210
|
+
version: '4.0'
|
211
|
+
type: :development
|
212
|
+
prerelease: false
|
213
|
+
version_requirements: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '1.2'
|
218
|
+
- - "<"
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '4.0'
|
221
221
|
- !ruby/object:Gem::Dependency
|
222
222
|
name: simplecov
|
223
223
|
requirement: !ruby/object:Gem::Requirement
|
@@ -252,14 +252,14 @@ dependencies:
|
|
252
252
|
requirements:
|
253
253
|
- - "~>"
|
254
254
|
- !ruby/object:Gem::Version
|
255
|
-
version: 1.
|
255
|
+
version: 1.4.0
|
256
256
|
type: :development
|
257
257
|
prerelease: false
|
258
258
|
version_requirements: !ruby/object:Gem::Requirement
|
259
259
|
requirements:
|
260
260
|
- - "~>"
|
261
261
|
- !ruby/object:Gem::Version
|
262
|
-
version: 1.
|
262
|
+
version: 1.4.0
|
263
263
|
- !ruby/object:Gem::Dependency
|
264
264
|
name: timecop
|
265
265
|
requirement: !ruby/object:Gem::Requirement
|
@@ -305,16 +305,22 @@ files:
|
|
305
305
|
- lib/authlogic/crypto_providers.rb
|
306
306
|
- lib/authlogic/crypto_providers/bcrypt.rb
|
307
307
|
- lib/authlogic/crypto_providers/md5.rb
|
308
|
+
- lib/authlogic/crypto_providers/md5/v2.rb
|
308
309
|
- lib/authlogic/crypto_providers/scrypt.rb
|
309
310
|
- lib/authlogic/crypto_providers/sha1.rb
|
311
|
+
- lib/authlogic/crypto_providers/sha1/v2.rb
|
310
312
|
- lib/authlogic/crypto_providers/sha256.rb
|
313
|
+
- lib/authlogic/crypto_providers/sha256/v2.rb
|
311
314
|
- lib/authlogic/crypto_providers/sha512.rb
|
315
|
+
- lib/authlogic/crypto_providers/sha512/v2.rb
|
316
|
+
- lib/authlogic/errors.rb
|
312
317
|
- lib/authlogic/i18n.rb
|
313
318
|
- lib/authlogic/i18n/translator.rb
|
314
319
|
- lib/authlogic/random.rb
|
315
320
|
- lib/authlogic/session/base.rb
|
316
321
|
- lib/authlogic/session/magic_column/assigns_last_request_at.rb
|
317
322
|
- lib/authlogic/test_case.rb
|
323
|
+
- lib/authlogic/test_case/mock_api_controller.rb
|
318
324
|
- lib/authlogic/test_case/mock_controller.rb
|
319
325
|
- lib/authlogic/test_case/mock_cookie_jar.rb
|
320
326
|
- lib/authlogic/test_case/mock_logger.rb
|
@@ -333,7 +339,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
333
339
|
requirements:
|
334
340
|
- - ">="
|
335
341
|
- !ruby/object:Gem::Version
|
336
|
-
version: 2.
|
342
|
+
version: 2.4.0
|
337
343
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
338
344
|
requirements:
|
339
345
|
- - ">="
|