rodauth 2.9.0 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +6 -0
- data/README.rdoc +8 -4
- data/doc/argon2.rdoc +49 -0
- data/doc/base.rdoc +1 -1
- data/doc/guides/migrate_password_hash_algorithm.rdoc +15 -0
- data/doc/login_password_requirements_base.rdoc +1 -1
- data/doc/release_notes/2.10.0.txt +47 -0
- data/lib/rodauth/features/argon2.rb +69 -0
- data/lib/rodauth/features/base.rb +5 -1
- data/lib/rodauth/features/disallow_password_reuse.rb +20 -7
- data/lib/rodauth/features/login_password_requirements_base.rb +4 -0
- data/lib/rodauth/features/update_password_hash.rb +1 -1
- data/lib/rodauth/migrations.rb +31 -5
- data/lib/rodauth/version.rb +1 -1
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5286a570d72b6bc951ca489e12a696a4c2348d789d09f2782cfbd3521e63ee6d
|
4
|
+
data.tar.gz: be5d4e5bef18f6f62978bc84e07da1c4b31db4f03bf104d01abf8510812c5719
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98a849b965bfee4d80b8b6f6fc286d22f57673bf37da4e2cb338c114275795b665e46b561781f688dbb2ddfdd312e8b393f545d1920ac9f83024e042ff97cee0
|
7
|
+
data.tar.gz: 46da808a9d2e38c647339eed1554183b0e175fb3dbf4fd04d49d78015ff523bc0960091b3643bbc3913cfce88b7f78a240bd1857002de889a2d9455f106e4e03
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 2.10.0 (2021-02-22)
|
2
|
+
|
3
|
+
* Add argon2 feature to allow use of the argon2 password hash algorithm instead of bcrypt (AlexeyMatskevich, jeremyevans) (#147)
|
4
|
+
|
5
|
+
* Avoid unnecessary previous password queries when using disallow_password_reuse feature with create_account or verify_account features (AlexeyMatskevich, jeremyevans) (#148)
|
6
|
+
|
1
7
|
=== 2.9.0 (2021-01-22)
|
2
8
|
|
3
9
|
* Split jwt feature into json and jwt features, with the json feature using standard session support (janko, jeremyevans) (#145)
|
data/README.rdoc
CHANGED
@@ -57,6 +57,7 @@ HTML and JSON API for all supported features.
|
|
57
57
|
* JWT Refresh (Access & Refresh Token)
|
58
58
|
* JWT CORS (Cross-Origin Resource Sharing)
|
59
59
|
* Update Password Hash (when hash cost changes)
|
60
|
+
* Argon2
|
60
61
|
* HTTP Basic Auth
|
61
62
|
* Change Password Notify
|
62
63
|
|
@@ -80,8 +81,10 @@ rack_csrf :: Used for CSRF support if the :csrf=>:rack_csrf plugin
|
|
80
81
|
option is given (the default is to use Roda's route_csrf
|
81
82
|
plugin, as that allows for more secure request-specific
|
82
83
|
tokens).
|
83
|
-
bcrypt :: Used by default for password
|
84
|
+
bcrypt :: Used by default for password hashing, can be skipped
|
84
85
|
if password_match? is overridden for custom authentication.
|
86
|
+
argon2 :: Used by the argon2 feature as alternative to bcrypt for
|
87
|
+
password hashing.
|
85
88
|
mail :: Used by default for mailing in the reset password, verify
|
86
89
|
account, verify_login_change, change_password_notify,
|
87
90
|
lockout, and email_auth features.
|
@@ -106,7 +109,7 @@ correctly without it. There may be cases where you cannot use
|
|
106
109
|
this feature, such as when using a different database or when you
|
107
110
|
do not have full control over the database you are using.
|
108
111
|
|
109
|
-
Passwords are hashed using bcrypt, and the password hashes are
|
112
|
+
Passwords are hashed using bcrypt by default, and the password hashes are
|
110
113
|
kept in a separate table from the accounts table, with a foreign key
|
111
114
|
referencing the accounts table. Two database functions are added,
|
112
115
|
one to retrieve the salt for a password, and the other to check
|
@@ -333,7 +336,7 @@ things for the schema changes:
|
|
333
336
|
foreign_key :id, Sequel[:${DATABASE_NAME}][:accounts], :primary_key=>true, :type=>:Bignum
|
334
337
|
String :password_hash, :null=>false
|
335
338
|
end
|
336
|
-
Rodauth.create_database_authentication_functions(self, :table_name=>
|
339
|
+
Rodauth.create_database_authentication_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_password_hashes])
|
337
340
|
|
338
341
|
# if using the disallow_password_reuse feature:
|
339
342
|
create_table(:account_previous_password_hashes) do
|
@@ -341,7 +344,7 @@ things for the schema changes:
|
|
341
344
|
foreign_key :account_id, Sequel[:${DATABASE_NAME}][:accounts], :type=>:Bignum
|
342
345
|
String :password_hash, :null=>false
|
343
346
|
end
|
344
|
-
Rodauth.create_database_previous_password_check_functions(self, :table_name=>
|
347
|
+
Rodauth.create_database_previous_password_check_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_previous_password_hashes])
|
345
348
|
|
346
349
|
You'll also need to use the following Rodauth configuration methods so that the
|
347
350
|
app account calls functions in a separate schema:
|
@@ -863,6 +866,7 @@ view the appropriate file in the doc directory.
|
|
863
866
|
* {Account Expiration}[rdoc-ref:doc/account_expiration.rdoc]
|
864
867
|
* {Active Sessions}[rdoc-ref:doc/active_sessions.rdoc]
|
865
868
|
* {Audit Logging}[rdoc-ref:doc/audit_logging.rdoc]
|
869
|
+
* {Argon2}[rdoc-ref:doc/argon2.rdoc]
|
866
870
|
* {Change Login}[rdoc-ref:doc/change_login.rdoc]
|
867
871
|
* {Change Password}[rdoc-ref:doc/change_password.rdoc]
|
868
872
|
* {Change Password Notify}[rdoc-ref:doc/change_password_notify.rdoc]
|
data/doc/argon2.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= Documentation for Argon2 Feature
|
2
|
+
|
3
|
+
The argon2 feature adds the ability to replace the bcrypt password hash
|
4
|
+
algorithm with argon2 (specifically, argon2id). Argon2 is an alternative to
|
5
|
+
bcrypt that offers the ability to be memory-hard. However, if you are storing
|
6
|
+
password hashes in a table that the database user does not have access to
|
7
|
+
(the recommended way to use Rodauth), argon2 does not offer significant
|
8
|
+
security advantages over bcrypt.
|
9
|
+
|
10
|
+
If you are using this feature with Rodauth's database authentication functions,
|
11
|
+
you need to make sure that the database authentication functions are configured
|
12
|
+
to support argon2 in addition to bcrypt. You can do this by passing the
|
13
|
+
+:argon2+ option when calling the method to define the database functions.
|
14
|
+
In this example, +DB+ should be your Sequel::Database object:
|
15
|
+
|
16
|
+
require 'rodauth/migrations'
|
17
|
+
|
18
|
+
# If the functions are already defined and you are not using PostgreSQL,
|
19
|
+
# you need to drop the existing functions.
|
20
|
+
Rodauth.drop_database_authentication_functions(DB)
|
21
|
+
|
22
|
+
# If you are using the disallow_password_reuse feature, also drop the
|
23
|
+
# database functions related to that if not using PostgreSQL:
|
24
|
+
Rodauth.drop_database_previous_password_check_functions(DB)
|
25
|
+
|
26
|
+
# Define new functions that support argon2:
|
27
|
+
Rodauth.create_database_authentication_functions(DB, argon2: true)
|
28
|
+
|
29
|
+
# If you are using the disallow_password_reuse feature, also define
|
30
|
+
# new functions that support argon2 for that:
|
31
|
+
Rodauth.create_database_previous_password_check_functions(DB, argon2: true)
|
32
|
+
|
33
|
+
The argon2 feature provides the ability to allow for a gradual migration
|
34
|
+
from transitioning from bcrypt to argon2 and vice-versa, if you are using the
|
35
|
+
update_password_hash.
|
36
|
+
|
37
|
+
Argon2 is more configurable than bcrypt in terms of password hash cost
|
38
|
+
speficiation. Instead of specifying the password_hash_cost value as
|
39
|
+
an integer, you must specify the password hash cost as a hash, such as
|
40
|
+
(<tt>{t_cost: 2, m_cost: 16}</tt>).
|
41
|
+
|
42
|
+
If you are using the argon2 feature and if you have no bcrypt passwords in
|
43
|
+
your database, you should use <tt>require_bcrypt? false</tt> in your
|
44
|
+
Rodauth configuration to prevent loading the bcrypt library, which will save
|
45
|
+
memory.
|
46
|
+
|
47
|
+
== Auth Value Methods
|
48
|
+
|
49
|
+
use_argon2? :: Whether to use the argon2 password hash algorithm for new passwords (true by default). The only reason to set this to false is if you have existing passwords using argon2 that you want to support, but want to use bcrypt for new passwords.
|
data/doc/base.rdoc
CHANGED
@@ -15,7 +15,7 @@ domain :: The domain to use, required by some other features. It is recommended
|
|
15
15
|
hmac_secret :: This sets the secret to use for all of Rodauth's HMACs. This is not set by default, in which case Rodauth does not use HMACs for additional security. However, it is highly recommended that you set this, and some features require it.
|
16
16
|
mark_input_fields_as_required? :: Whether input fields should be marked as required, so browsers will not allow submission without filling out the field (default: true).
|
17
17
|
prefix :: The routing prefix used for Rodauth routes. If you are calling in a routing subtree, this should be set to the root path of the subtree. This should include a leading slash if set, but not a trailing slash.
|
18
|
-
require_bcrypt? :: Set to false to not require bcrypt, useful if using custom authentication.
|
18
|
+
require_bcrypt? :: Set to false to not require bcrypt, useful if using custom authentication or when using the argon2 feature without existing bcrypt password hashes.
|
19
19
|
session_key :: The key in the session hash storing the primary key of the logged in account.
|
20
20
|
session_key_prefix :: The string that will be prepended to the default value for all session keys.
|
21
21
|
skip_status_checks? :: Whether status checks should be skipped for accounts. Defaults to true unless enabling the verify_account or close_account features.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= Migrate users passwords from bcrypt to argon2 or back
|
2
|
+
|
3
|
+
If you are currently using the default bcrypt password hash algorithm, and want to
|
4
|
+
gradually migrate to the argon2 password hash algorithm, you can use both the argon2
|
5
|
+
and update_password_hash features:
|
6
|
+
|
7
|
+
plugin :rodauth do
|
8
|
+
enable :login, :update_password_hash, :argon2
|
9
|
+
end
|
10
|
+
|
11
|
+
When a user with a current bcrypt password hash next successfully uses their
|
12
|
+
password, their password hash will be migrated to argon2.
|
13
|
+
|
14
|
+
If for some reason you want to migrate back from argon2 to bcrypt, you can set
|
15
|
+
<tt>use_argon2? false</tt> in your Rodauth configuration.
|
@@ -19,7 +19,7 @@ logins_do_not_match_message :: The error message to display when login and login
|
|
19
19
|
password_confirm_label :: The label to use for password confirmations.
|
20
20
|
password_confirm_param :: The parameter name to use for password confirmations.
|
21
21
|
password_does_not_meet_requirements_message :: The error message to display when the password does not meet the requirements you have set.
|
22
|
-
password_hash_cost :: The
|
22
|
+
password_hash_cost :: The cost to use for the password hash algorithm. This should be an integer when using bcrypt (the default), and a hash if using argon2 (supported by the argon2 feature).
|
23
23
|
password_minimum_length :: The minimum length for passwords, 6 by default.
|
24
24
|
password_too_short_message :: The error message fragment to show if the password is too short.
|
25
25
|
passwords_do_not_match_message :: The error message to display when password and password confirmation do not match.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An argon2 feature has been added that supports using the argon2
|
4
|
+
password hashing algorithm instead of the bcrypt password hashing
|
5
|
+
algorithm. While argon2 does not provide an advantage over bcrypt
|
6
|
+
if the attacker cannot access the password hashes directly (which
|
7
|
+
is how Rodauth is recommended to be used), in cases where attackers
|
8
|
+
can access the password hashes directly, argon2 is thought to be
|
9
|
+
more difficult or expensive to crack due to requiring more memory
|
10
|
+
(bcrypt is not a memory-hard password hash algorithm).
|
11
|
+
|
12
|
+
If you are using this feature with Rodauth's database authentication
|
13
|
+
functions, you need to make sure that the database authentication
|
14
|
+
functions are configured to support argon2 in addition to bcrypt.
|
15
|
+
You can do this by passing the :argon2 option when calling the
|
16
|
+
method to define the database functions. In this example, DB should
|
17
|
+
be your Sequel::Database object (this could be self if used in a
|
18
|
+
Sequel migration):
|
19
|
+
|
20
|
+
require 'rodauth/migrations'
|
21
|
+
|
22
|
+
# If the functions are already defined and you are not using PostgreSQL,
|
23
|
+
# you need to drop the existing functions.
|
24
|
+
Rodauth.drop_database_authentication_functions(DB)
|
25
|
+
|
26
|
+
# If you are using the disallow_password_reuse feature, also drop the
|
27
|
+
# database functions related to that if you are not using PostgreSQL:
|
28
|
+
Rodauth.drop_database_previous_password_check_functions(DB)
|
29
|
+
|
30
|
+
# Define new functions that support argon2:
|
31
|
+
Rodauth.create_database_authentication_functions(DB, argon2: true)
|
32
|
+
|
33
|
+
# If you are using the disallow_password_reuse feature, also define
|
34
|
+
# new functions that support argon2 for that:
|
35
|
+
Rodauth.create_database_previous_password_check_functions(DB, argon2: true)
|
36
|
+
|
37
|
+
You can transparently migrate bcrypt password hashes to argon2
|
38
|
+
password hashes whenever a user successfully uses their password
|
39
|
+
by using the argon2 feature in combination with the
|
40
|
+
update_password_hash feature.
|
41
|
+
|
42
|
+
= Other Improvements
|
43
|
+
|
44
|
+
* Unnecessary queries to determine whether the new password matches
|
45
|
+
a previous password are now skipped when using the create_account
|
46
|
+
or verify_account features with the disallow_password_reuse
|
47
|
+
feature.
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'argon2'
|
4
|
+
|
5
|
+
# :nocov:
|
6
|
+
if !defined?(Argon2::VERSION) || Argon2::VERSION < '2'
|
7
|
+
raise LoadError, "argon2 version 1.x not supported as it does not support argon2id hashes"
|
8
|
+
end
|
9
|
+
# :nocov:
|
10
|
+
|
11
|
+
module Rodauth
|
12
|
+
Feature.define(:argon2, :Argon2) do
|
13
|
+
depends :login_password_requirements_base
|
14
|
+
|
15
|
+
auth_value_method :use_argon2?, true
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def password_hash_cost
|
20
|
+
return super unless use_argon2?
|
21
|
+
argon2_hash_cost
|
22
|
+
end
|
23
|
+
|
24
|
+
def password_hash(password)
|
25
|
+
return super unless use_argon2?
|
26
|
+
::Argon2::Password.new(password_hash_cost).create(password)
|
27
|
+
end
|
28
|
+
|
29
|
+
def password_hash_match?(hash, password)
|
30
|
+
return super unless argon2_hash_algorithm?(hash)
|
31
|
+
argon2_password_hash_match?(hash, password)
|
32
|
+
end
|
33
|
+
|
34
|
+
def password_hash_using_salt(password, salt)
|
35
|
+
return super unless argon2_hash_algorithm?(salt)
|
36
|
+
|
37
|
+
argon2_params = Hash[extract_password_hash_cost(salt)]
|
38
|
+
argon2_params[:salt_do_not_supply] = Base64.decode64(salt.split('$').last)
|
39
|
+
::Argon2::Password.new(argon2_params).create(password)
|
40
|
+
end
|
41
|
+
|
42
|
+
def extract_password_hash_cost(hash)
|
43
|
+
return super unless argon2_hash_algorithm?(hash )
|
44
|
+
|
45
|
+
/\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+)/ =~ hash
|
46
|
+
{ t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i }
|
47
|
+
end
|
48
|
+
|
49
|
+
if ENV['RACK_ENV'] == 'test'
|
50
|
+
def argon2_hash_cost
|
51
|
+
{t_cost: 1, m_cost: 3}
|
52
|
+
end
|
53
|
+
# :nocov:
|
54
|
+
else
|
55
|
+
def argon2_hash_cost
|
56
|
+
{t_cost: 2, m_cost: 16}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
# :nocov:
|
60
|
+
|
61
|
+
def argon2_hash_algorithm?(hash)
|
62
|
+
hash.start_with?('$argon2id$')
|
63
|
+
end
|
64
|
+
|
65
|
+
def argon2_password_hash_match?(hash, password)
|
66
|
+
::Argon2::Password.verify_password(password, hash)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -465,7 +465,7 @@ module Rodauth
|
|
465
465
|
end
|
466
466
|
|
467
467
|
def database_function_password_match?(name, hash_id, password, salt)
|
468
|
-
db.get(Sequel.function(function_name(name), hash_id,
|
468
|
+
db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
|
469
469
|
end
|
470
470
|
|
471
471
|
def password_hash_match?(hash, password)
|
@@ -593,6 +593,10 @@ module Rodauth
|
|
593
593
|
@has_password = !!get_password_hash
|
594
594
|
end
|
595
595
|
|
596
|
+
def password_hash_using_salt(password, salt)
|
597
|
+
BCrypt::Engine.hash_secret(password, salt)
|
598
|
+
end
|
599
|
+
|
596
600
|
# Get the password hash for the user. When using database authentication functions,
|
597
601
|
# note that only the salt is returned.
|
598
602
|
def get_password_hash
|
@@ -24,13 +24,16 @@ module Rodauth
|
|
24
24
|
|
25
25
|
def add_previous_password_hash(hash)
|
26
26
|
ds = previous_password_ds
|
27
|
-
keep_before = ds.reverse(previous_password_id_column).
|
28
|
-
limit(nil, previous_passwords_to_check).
|
29
|
-
get(previous_password_id_column)
|
30
27
|
|
31
|
-
|
32
|
-
ds.
|
33
|
-
|
28
|
+
unless @dont_check_previous_password
|
29
|
+
keep_before = ds.reverse(previous_password_id_column).
|
30
|
+
limit(nil, previous_passwords_to_check).
|
31
|
+
get(previous_password_id_column)
|
32
|
+
|
33
|
+
if keep_before
|
34
|
+
ds.where(Sequel.expr(previous_password_id_column) <= keep_before).
|
35
|
+
delete
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
# This should never raise uniqueness violations, as it uses a serial primary key
|
@@ -39,7 +42,7 @@ module Rodauth
|
|
39
42
|
|
40
43
|
def password_meets_requirements?(password)
|
41
44
|
super &&
|
42
|
-
password_doesnt_match_previous_password?(password)
|
45
|
+
(@dont_check_previous_password || password_doesnt_match_previous_password?(password))
|
43
46
|
end
|
44
47
|
|
45
48
|
private
|
@@ -71,6 +74,16 @@ module Rodauth
|
|
71
74
|
previous_password_ds.delete
|
72
75
|
end
|
73
76
|
|
77
|
+
def before_create_account_route
|
78
|
+
super if defined?(super)
|
79
|
+
@dont_check_previous_password = true
|
80
|
+
end
|
81
|
+
|
82
|
+
def before_verify_account_route
|
83
|
+
super if defined?(super)
|
84
|
+
@dont_check_previous_password = true
|
85
|
+
end
|
86
|
+
|
74
87
|
def after_create_account
|
75
88
|
if account_password_hash_column && !(respond_to?(:verify_account_set_password?) && verify_account_set_password?)
|
76
89
|
add_previous_password_hash(password_hash(param(password_param)))
|
data/lib/rodauth/migrations.rb
CHANGED
@@ -4,7 +4,8 @@ module Rodauth
|
|
4
4
|
def self.create_database_authentication_functions(db, opts={})
|
5
5
|
table_name = opts[:table_name] || :account_password_hashes
|
6
6
|
get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
|
7
|
-
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
7
|
+
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
8
|
+
argon2 = opts[:argon2]
|
8
9
|
|
9
10
|
case db.database_type
|
10
11
|
when :postgres
|
@@ -14,12 +15,21 @@ module Rodauth
|
|
14
15
|
when 'uuid' then :uuid
|
15
16
|
else :int8
|
16
17
|
end
|
18
|
+
table_name = db.literal(table_name) unless table_name.is_a?(String)
|
17
19
|
|
20
|
+
argon_sql = <<END
|
21
|
+
CASE
|
22
|
+
WHEN password_hash ~ '^\\$argon2id'
|
23
|
+
THEN substring(password_hash from '\\$argon2id\\$v=\\d+\\$m=\\d+,t=\\d+,p=\\d+\\$.+\\$')
|
24
|
+
ELSE substr(password_hash, 0, 30)
|
25
|
+
END INTO salt
|
26
|
+
END
|
18
27
|
db.run <<END
|
19
28
|
CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
|
20
29
|
DECLARE salt text;
|
21
30
|
BEGIN
|
22
|
-
SELECT
|
31
|
+
SELECT
|
32
|
+
#{argon2 ? argon_sql : "substr(password_hash, 0, 30) INTO salt"}
|
23
33
|
FROM #{table_name}
|
24
34
|
WHERE acct_id = id;
|
25
35
|
RETURN salt;
|
@@ -43,12 +53,20 @@ SECURITY DEFINER
|
|
43
53
|
SET search_path = #{search_path};
|
44
54
|
END
|
45
55
|
when :mysql
|
56
|
+
argon_sql = <<END
|
57
|
+
CASE
|
58
|
+
WHEN password_hash REGEXP '^.argon2id'
|
59
|
+
THEN left(password_hash, CHAR_LENGTH(password_hash) - INSTR(REVERSE(password_hash), '$'))
|
60
|
+
ELSE substr(password_hash, 1, 30)
|
61
|
+
END
|
62
|
+
END
|
46
63
|
db.run <<END
|
47
64
|
CREATE FUNCTION #{get_salt_name}(acct_id int8) RETURNS varchar(255)
|
48
65
|
SQL SECURITY DEFINER
|
49
66
|
READS SQL DATA
|
50
67
|
BEGIN
|
51
|
-
RETURN (SELECT
|
68
|
+
RETURN (SELECT
|
69
|
+
#{argon2 ? argon_sql : "substr(password_hash, 1, 30)"}
|
52
70
|
FROM #{table_name}
|
53
71
|
WHERE acct_id = id);
|
54
72
|
END;
|
@@ -71,13 +89,21 @@ RETURN valid;
|
|
71
89
|
END;
|
72
90
|
END
|
73
91
|
when :mssql
|
92
|
+
argon_sql = <<END
|
93
|
+
CASE
|
94
|
+
WHEN password_hash LIKE '[$]argon2id%'
|
95
|
+
THEN left(password_hash, len(password_hash) - charindex('$', reverse(password_hash)))
|
96
|
+
ELSE substring(password_hash, 0, 30)
|
97
|
+
END
|
98
|
+
END
|
74
99
|
db.run <<END
|
75
100
|
CREATE FUNCTION #{get_salt_name}(@account_id bigint) RETURNS nvarchar(255)
|
76
101
|
WITH EXECUTE AS OWNER
|
77
102
|
AS
|
78
103
|
BEGIN
|
79
104
|
DECLARE @salt nvarchar(255);
|
80
|
-
SELECT @salt =
|
105
|
+
SELECT @salt =
|
106
|
+
#{argon2 ? argon_sql : "substring(password_hash, 0, 30)"}
|
81
107
|
FROM #{table_name}
|
82
108
|
WHERE id = @account_id;
|
83
109
|
RETURN @salt;
|
@@ -107,7 +133,7 @@ END
|
|
107
133
|
def self.drop_database_authentication_functions(db, opts={})
|
108
134
|
table_name = opts[:table_name] || :account_password_hashes
|
109
135
|
get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
|
110
|
-
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
136
|
+
valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
|
111
137
|
|
112
138
|
case db.database_type
|
113
139
|
when :postgres
|
data/lib/rodauth/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: argon2
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: mail
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,14 +154,14 @@ dependencies:
|
|
140
154
|
name: webauthn
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
|
-
- - "
|
157
|
+
- - ">="
|
144
158
|
- !ruby/object:Gem::Version
|
145
159
|
version: '2'
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
|
-
- - "
|
164
|
+
- - ">="
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '2'
|
153
167
|
- !ruby/object:Gem::Dependency
|
@@ -239,6 +253,7 @@ extra_rdoc_files:
|
|
239
253
|
- MIT-LICENSE
|
240
254
|
- doc/account_expiration.rdoc
|
241
255
|
- doc/active_sessions.rdoc
|
256
|
+
- doc/argon2.rdoc
|
242
257
|
- doc/audit_logging.rdoc
|
243
258
|
- doc/base.rdoc
|
244
259
|
- doc/change_login.rdoc
|
@@ -305,6 +320,7 @@ extra_rdoc_files:
|
|
305
320
|
- doc/release_notes/1.9.0.txt
|
306
321
|
- doc/release_notes/2.0.0.txt
|
307
322
|
- doc/release_notes/2.1.0.txt
|
323
|
+
- doc/release_notes/2.10.0.txt
|
308
324
|
- doc/release_notes/2.2.0.txt
|
309
325
|
- doc/release_notes/2.3.0.txt
|
310
326
|
- doc/release_notes/2.4.0.txt
|
@@ -320,6 +336,7 @@ files:
|
|
320
336
|
- dict/top-10_000-passwords.txt
|
321
337
|
- doc/account_expiration.rdoc
|
322
338
|
- doc/active_sessions.rdoc
|
339
|
+
- doc/argon2.rdoc
|
323
340
|
- doc/audit_logging.rdoc
|
324
341
|
- doc/base.rdoc
|
325
342
|
- doc/change_login.rdoc
|
@@ -342,6 +359,7 @@ files:
|
|
342
359
|
- doc/guides/internals.rdoc
|
343
360
|
- doc/guides/links.rdoc
|
344
361
|
- doc/guides/login_return.rdoc
|
362
|
+
- doc/guides/migrate_password_hash_algorithm.rdoc
|
345
363
|
- doc/guides/password_column.rdoc
|
346
364
|
- doc/guides/password_confirmation.rdoc
|
347
365
|
- doc/guides/password_requirements.rdoc
|
@@ -394,6 +412,7 @@ files:
|
|
394
412
|
- doc/release_notes/1.9.0.txt
|
395
413
|
- doc/release_notes/2.0.0.txt
|
396
414
|
- doc/release_notes/2.1.0.txt
|
415
|
+
- doc/release_notes/2.10.0.txt
|
397
416
|
- doc/release_notes/2.2.0.txt
|
398
417
|
- doc/release_notes/2.3.0.txt
|
399
418
|
- doc/release_notes/2.4.0.txt
|
@@ -421,6 +440,7 @@ files:
|
|
421
440
|
- lib/rodauth.rb
|
422
441
|
- lib/rodauth/features/account_expiration.rb
|
423
442
|
- lib/rodauth/features/active_sessions.rb
|
443
|
+
- lib/rodauth/features/argon2.rb
|
424
444
|
- lib/rodauth/features/audit_logging.rb
|
425
445
|
- lib/rodauth/features/base.rb
|
426
446
|
- lib/rodauth/features/change_login.rb
|