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