authlogic 5.0.4 → 6.4.1
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/abstract_adapter.rb +21 -0
- 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 +203 -93
- 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 +58 -4
- data/lib/authlogic/test_case/mock_request.rb +10 -0
- data/lib/authlogic/test_case/rails_request_adapter.rb +7 -1
- data/lib/authlogic/version.rb +1 -1
- metadata +53 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e65309a22f2adc25c9c61f10910d6db41fe2c3f6b9d8037977bdfa094b90dd53
|
4
|
+
data.tar.gz: 2e5bb549974be424ad83ae20de60775bcb1b66c207031919caf978de7e5801ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1198b8ff9bf45e98e8748365ba67112f249c738952b84a39a32b835353481a37b3b006106ff31031d5a78f2952fffa746c8764639f50ebc3cc0ae99f84f74b98
|
7
|
+
data.tar.gz: 9ab8911d8838f3ddf6b8b011d498b08941caba5e20d2c62c1d2ec9c8b17f446c68f7585db88bd0c3497ae26cd11cd6f565eb9da3ae27bd3faee5de7815c62fd6
|
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
|
@@ -8,6 +8,7 @@ module Authlogic
|
|
8
8
|
class AbstractAdapter
|
9
9
|
E_COOKIE_DOMAIN_ADAPTER = "The cookie_domain method has not been " \
|
10
10
|
"implemented by the controller adapter"
|
11
|
+
ENV_SESSION_OPTIONS = "rack.session.options"
|
11
12
|
|
12
13
|
attr_accessor :controller
|
13
14
|
|
@@ -44,6 +45,26 @@ module Authlogic
|
|
44
45
|
request.content_type
|
45
46
|
end
|
46
47
|
|
48
|
+
# Inform Rack that we would like a new session ID to be assigned. Changes
|
49
|
+
# the ID, but not the contents of the session.
|
50
|
+
#
|
51
|
+
# The `:renew` option is read by `rack/session/abstract/id.rb`.
|
52
|
+
#
|
53
|
+
# This is how Devise (via warden) implements defense against Session
|
54
|
+
# Fixation. Our implementation is copied directly from the warden gem
|
55
|
+
# (set_user in warden/proxy.rb)
|
56
|
+
def renew_session_id
|
57
|
+
env = request.env
|
58
|
+
options = env[ENV_SESSION_OPTIONS]
|
59
|
+
if options
|
60
|
+
if options.frozen?
|
61
|
+
env[ENV_SESSION_OPTIONS] = options.merge(renew: true).freeze
|
62
|
+
else
|
63
|
+
options[:renew] = true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
47
68
|
def session
|
48
69
|
controller.session
|
49
70
|
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
|