rodauth 2.46.0 → 2.48.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/rodauth/features/active_sessions.rb +2 -2
- data/lib/rodauth/features/argon2.rb +6 -6
- data/lib/rodauth/features/audit_logging.rb +4 -3
- data/lib/rodauth/features/base.rb +84 -30
- data/lib/rodauth/features/change_password.rb +11 -2
- data/lib/rodauth/features/confirm_password.rb +1 -1
- data/lib/rodauth/features/create_account.rb +2 -1
- data/lib/rodauth/features/disallow_password_reuse.rb +2 -2
- data/lib/rodauth/features/email_auth.rb +13 -4
- data/lib/rodauth/features/email_base.rb +36 -1
- data/lib/rodauth/features/http_basic_auth.rb +6 -1
- data/lib/rodauth/features/internal_request.rb +4 -0
- data/lib/rodauth/features/json.rb +5 -1
- data/lib/rodauth/features/jwt.rb +9 -3
- data/lib/rodauth/features/jwt_cors.rb +2 -0
- data/lib/rodauth/features/jwt_refresh.rb +33 -18
- data/lib/rodauth/features/lockout.rb +24 -21
- data/lib/rodauth/features/login.rb +12 -4
- data/lib/rodauth/features/login_password_requirements_base.rb +43 -13
- data/lib/rodauth/features/otp.rb +21 -12
- data/lib/rodauth/features/otp_unlock.rb +2 -2
- data/lib/rodauth/features/password_complexity.rb +2 -2
- data/lib/rodauth/features/password_expiration.rb +0 -1
- data/lib/rodauth/features/password_grace_period.rb +8 -0
- data/lib/rodauth/features/password_pepper.rb +11 -1
- data/lib/rodauth/features/recovery_codes.rb +4 -1
- data/lib/rodauth/features/remember.rb +4 -2
- data/lib/rodauth/features/require_domain.rb +12 -0
- data/lib/rodauth/features/require_hmac_secret.rb +12 -0
- data/lib/rodauth/features/reset_password.rb +25 -20
- data/lib/rodauth/features/single_session.rb +6 -2
- data/lib/rodauth/features/sms_codes.rb +4 -1
- data/lib/rodauth/features/two_factor_base.rb +2 -1
- data/lib/rodauth/features/update_password_hash.rb +5 -1
- data/lib/rodauth/features/verify_account.rb +29 -20
- data/lib/rodauth/features/verify_login_change.rb +19 -11
- data/lib/rodauth/features/webauthn.rb +16 -11
- data/lib/rodauth/features/webauthn_login.rb +1 -10
- data/lib/rodauth/features/webauthn_verify_account.rb +0 -4
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +14 -12
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9a5a66fe82750a982d493fd5e6558b4fef6f94f1761257b3242ff53014eb81b
|
|
4
|
+
data.tar.gz: ecd3f766f3b4d96acd2c7e651d71d1ad0cbb4e60e49db3d5e396a304866b733f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4471aa257cc7761633fca706db570b61cf4315f22d512c90a2419666f2c1bfd97647196a71fa8fd0d85f1cf16cede1beb69dce20ba6d98a29c52727aa6342c37
|
|
7
|
+
data.tar.gz: 0c3ac3e520ab620528ec8dd125cdee8092adea4ad6a2e1c877e30a10598219d0ad8abcb15eb40e1509561587323d8f05f653b68b3d7d112bd2cf44c9a6b8e6e5
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:active_sessions, :ActiveSessions) do
|
|
5
|
-
depends :logout
|
|
5
|
+
depends :logout, :require_hmac_secret
|
|
6
6
|
|
|
7
7
|
error_flash 'This session has been logged out'
|
|
8
8
|
redirect
|
|
@@ -235,7 +235,7 @@ module Rodauth
|
|
|
235
235
|
|
|
236
236
|
def active_sessions_ds
|
|
237
237
|
db[active_sessions_table].
|
|
238
|
-
where(active_sessions_account_id_column=>
|
|
238
|
+
where(active_sessions_account_id_column=>account_id_or_session_value)
|
|
239
239
|
end
|
|
240
240
|
|
|
241
241
|
def use_date_arithmetic?
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require 'argon2'
|
|
4
4
|
|
|
5
|
-
# :
|
|
5
|
+
# simplecov:disable
|
|
6
6
|
if !defined?(Argon2::VERSION) || Argon2::VERSION < '2'
|
|
7
7
|
raise LoadError, "argon2 version 1.x not supported as it does not support argon2id hashes"
|
|
8
8
|
end
|
|
9
|
-
# :
|
|
9
|
+
# simplecov:enable
|
|
10
10
|
|
|
11
11
|
module Rodauth
|
|
12
12
|
Feature.define(:argon2, :Argon2) do
|
|
@@ -36,12 +36,12 @@ module Rodauth
|
|
|
36
36
|
def argon2_salt_option
|
|
37
37
|
:salt_do_not_supply
|
|
38
38
|
end
|
|
39
|
-
# :
|
|
39
|
+
# simplecov:disable
|
|
40
40
|
else
|
|
41
41
|
def argon2_salt_option
|
|
42
42
|
:salt_for_testing_purposes_only
|
|
43
43
|
end
|
|
44
|
-
# :
|
|
44
|
+
# simplecov:enable
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def password_hash_cost
|
|
@@ -78,7 +78,7 @@ module Rodauth
|
|
|
78
78
|
def argon2_hash_cost
|
|
79
79
|
{ t_cost: 1, m_cost: 5, p_cost: 1 }
|
|
80
80
|
end
|
|
81
|
-
# :
|
|
81
|
+
# simplecov:disable
|
|
82
82
|
else
|
|
83
83
|
def argon2_hash_cost
|
|
84
84
|
{ t_cost: 2, m_cost: 16, p_cost: 1 }
|
|
@@ -102,7 +102,7 @@ module Rodauth
|
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
|
-
# :
|
|
105
|
+
# simplecov:enable
|
|
106
106
|
|
|
107
107
|
def argon2_hash_algorithm?(hash)
|
|
108
108
|
hash.start_with?('$argon2id$')
|
|
@@ -30,8 +30,9 @@ module Rodauth
|
|
|
30
30
|
|
|
31
31
|
def hook_action(hook_type, action)
|
|
32
32
|
super
|
|
33
|
+
|
|
33
34
|
# In after_logout, session is already cleared, so use before_logout in that case
|
|
34
|
-
if (hook_type == :after || action == :logout) && (id =
|
|
35
|
+
if (hook_type == :after || action == :logout) && (id = account_id_or_session_value)
|
|
35
36
|
add_audit_log(id, action)
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -82,9 +83,9 @@ module Rodauth
|
|
|
82
83
|
|
|
83
84
|
def audit_log_ds
|
|
84
85
|
ds = db[audit_logging_table]
|
|
85
|
-
# :
|
|
86
|
+
# simplecov:disable
|
|
86
87
|
if db.database_type == :postgres
|
|
87
|
-
# :
|
|
88
|
+
# simplecov:enable
|
|
88
89
|
# For PostgreSQL, use RETURNING NULL. This allows the feature
|
|
89
90
|
# to be used with INSERT but not SELECT permissions on the
|
|
90
91
|
# table, useful for audit logging where the database user
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
begin
|
|
4
4
|
require "rack/version"
|
|
5
|
-
# :
|
|
5
|
+
# simplecov:disable
|
|
6
6
|
rescue LoadError
|
|
7
7
|
require "rack"
|
|
8
8
|
else
|
|
@@ -13,7 +13,7 @@ else
|
|
|
13
13
|
require "rack"
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
-
# :
|
|
16
|
+
# simplecov:enable
|
|
17
17
|
|
|
18
18
|
module Rodauth
|
|
19
19
|
Feature.define(:base, :Base) do
|
|
@@ -68,6 +68,7 @@ module Rodauth
|
|
|
68
68
|
auth_value_method :prefix, ''
|
|
69
69
|
auth_value_method :session_key_prefix, nil
|
|
70
70
|
auth_value_method :require_bcrypt?, true
|
|
71
|
+
auth_value_method :return_to_path_max_size, 2048
|
|
71
72
|
auth_value_method :mark_input_fields_as_required?, true
|
|
72
73
|
auth_value_method :mark_input_fields_with_autocomplete?, true
|
|
73
74
|
auth_value_method :mark_input_fields_with_inputmode?, true
|
|
@@ -101,6 +102,7 @@ module Rodauth
|
|
|
101
102
|
|
|
102
103
|
auth_methods(
|
|
103
104
|
:account_id,
|
|
105
|
+
:account_reload,
|
|
104
106
|
:account_session_value,
|
|
105
107
|
:already_logged_in,
|
|
106
108
|
:authenticated?,
|
|
@@ -115,6 +117,7 @@ module Rodauth
|
|
|
115
117
|
:inputmode_for_field?,
|
|
116
118
|
:logged_in?,
|
|
117
119
|
:login_required,
|
|
120
|
+
:missing_recommended_configuration,
|
|
118
121
|
:normalize_login,
|
|
119
122
|
:null_byte_parameter_value,
|
|
120
123
|
:open_account?,
|
|
@@ -130,7 +133,8 @@ module Rodauth
|
|
|
130
133
|
:set_error_reason,
|
|
131
134
|
:set_title,
|
|
132
135
|
:translate,
|
|
133
|
-
:update_session
|
|
136
|
+
:update_session,
|
|
137
|
+
:valid_return_to_path?
|
|
134
138
|
)
|
|
135
139
|
|
|
136
140
|
auth_private_methods(
|
|
@@ -164,7 +168,9 @@ module Rodauth
|
|
|
164
168
|
:@current_route,
|
|
165
169
|
:@field_errors,
|
|
166
170
|
:@password_field_autocomplete_value,
|
|
167
|
-
:@has_password
|
|
171
|
+
:@has_password,
|
|
172
|
+
:@account_retrieval_type,
|
|
173
|
+
:@existing_password_hash_or_salt
|
|
168
174
|
)
|
|
169
175
|
|
|
170
176
|
attr_reader :scope
|
|
@@ -309,7 +315,7 @@ module Rodauth
|
|
|
309
315
|
alias logged_in? session_value
|
|
310
316
|
|
|
311
317
|
def account_from_login(login)
|
|
312
|
-
|
|
318
|
+
set_account(_account_from_login(login), :login)
|
|
313
319
|
end
|
|
314
320
|
|
|
315
321
|
def open_account?
|
|
@@ -337,10 +343,12 @@ module Rodauth
|
|
|
337
343
|
end
|
|
338
344
|
|
|
339
345
|
def check_already_logged_in
|
|
346
|
+
# RODAUTH3: Move to login feature
|
|
340
347
|
already_logged_in if logged_in?
|
|
341
348
|
end
|
|
342
349
|
|
|
343
350
|
def already_logged_in
|
|
351
|
+
# RODAUTH3: Move to login feature, halt or redirect by default
|
|
344
352
|
nil
|
|
345
353
|
end
|
|
346
354
|
|
|
@@ -414,15 +422,15 @@ module Rodauth
|
|
|
414
422
|
end
|
|
415
423
|
|
|
416
424
|
def account!
|
|
417
|
-
account
|
|
425
|
+
@account_retrieval_type ? @account : (session_value && account_from_session)
|
|
418
426
|
end
|
|
419
427
|
|
|
420
428
|
def account_from_session
|
|
421
|
-
|
|
429
|
+
set_account(_account_from_session, :session)
|
|
422
430
|
end
|
|
423
431
|
|
|
424
432
|
def account_from_id(id, status_id=nil)
|
|
425
|
-
|
|
433
|
+
set_account(_account_from_id(id, status_id), :id)
|
|
426
434
|
end
|
|
427
435
|
|
|
428
436
|
def check_csrf
|
|
@@ -435,9 +443,9 @@ module Rodauth
|
|
|
435
443
|
if use_request_specific_csrf_tokens?
|
|
436
444
|
scope.csrf_tag(path)
|
|
437
445
|
else
|
|
438
|
-
# :
|
|
446
|
+
# simplecov:disable
|
|
439
447
|
scope.csrf_tag
|
|
440
|
-
# :
|
|
448
|
+
# simplecov:enable
|
|
441
449
|
end
|
|
442
450
|
end
|
|
443
451
|
|
|
@@ -486,7 +494,7 @@ module Rodauth
|
|
|
486
494
|
end
|
|
487
495
|
|
|
488
496
|
def password_match?(password)
|
|
489
|
-
if hash = get_password_hash
|
|
497
|
+
if @existing_password_hash_or_salt = hash = get_password_hash
|
|
490
498
|
if account_password_hash_column || !use_database_authentication_functions?
|
|
491
499
|
password_hash_match?(hash, password)
|
|
492
500
|
else
|
|
@@ -579,7 +587,7 @@ module Rodauth
|
|
|
579
587
|
|
|
580
588
|
def has_password?
|
|
581
589
|
return @has_password unless @has_password.nil?
|
|
582
|
-
return false unless
|
|
590
|
+
return false unless account_id_or_session_value
|
|
583
591
|
@has_password = !!get_password_hash
|
|
584
592
|
end
|
|
585
593
|
|
|
@@ -599,6 +607,14 @@ module Rodauth
|
|
|
599
607
|
s
|
|
600
608
|
end
|
|
601
609
|
|
|
610
|
+
def account_id_or_session_value
|
|
611
|
+
if @account
|
|
612
|
+
account_id
|
|
613
|
+
elsif !@account_retrieval_type
|
|
614
|
+
session_value
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
602
618
|
if Rack.release >= '3'
|
|
603
619
|
def set_response_header(key, value)
|
|
604
620
|
response.headers[key] = value
|
|
@@ -607,7 +623,7 @@ module Rodauth
|
|
|
607
623
|
def convert_response_header_key(key)
|
|
608
624
|
key
|
|
609
625
|
end
|
|
610
|
-
# :
|
|
626
|
+
# simplecov:disable
|
|
611
627
|
else
|
|
612
628
|
def set_response_header(key, value)
|
|
613
629
|
response.headers[convert_response_header_key(key)] = value
|
|
@@ -627,19 +643,20 @@ module Rodauth
|
|
|
627
643
|
Content-Type
|
|
628
644
|
Content-Length
|
|
629
645
|
WWW-Authenticate
|
|
646
|
+
Vary
|
|
630
647
|
END
|
|
631
648
|
mixed_case_headers.freeze
|
|
632
649
|
define_method(:convert_response_header_key) do |key|
|
|
633
650
|
mixed_case_headers.fetch(key, key)
|
|
634
651
|
end
|
|
635
652
|
end
|
|
636
|
-
# :
|
|
653
|
+
# simplecov:enable
|
|
637
654
|
|
|
638
655
|
if RUBY_VERSION >= '2.1'
|
|
639
656
|
def button_fixed_locals
|
|
640
657
|
'(value:, opts:)'
|
|
641
658
|
end
|
|
642
|
-
# :
|
|
659
|
+
# simplecov:disable
|
|
643
660
|
else
|
|
644
661
|
# Work on Ruby 2.0 when using Tilt 2.6+, as Ruby 2.0 does
|
|
645
662
|
# not support required keyword arguments.
|
|
@@ -647,7 +664,28 @@ module Rodauth
|
|
|
647
664
|
'(value: nil, opts: nil)'
|
|
648
665
|
end
|
|
649
666
|
end
|
|
650
|
-
# :
|
|
667
|
+
# simplecov:enable
|
|
668
|
+
|
|
669
|
+
def missing_recommended_configuration(msg)
|
|
670
|
+
# RODAUTH3: switch to raise ConfigurationError
|
|
671
|
+
warn msg
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
def account_reload(current_retrieval_type, new_retrieval_type)
|
|
675
|
+
return unless @current_route
|
|
676
|
+
|
|
677
|
+
if current_retrieval_type == new_retrieval_type
|
|
678
|
+
warn "account retrieved multiple times during rodauth route with retrieval type #{current_retrieval_type}"
|
|
679
|
+
else
|
|
680
|
+
raise Error, "account retrieved multiple times during rodauth route, original retrieval type: #{current_retrieval_type}, new retrieval type: #{new_retrieval_type}"
|
|
681
|
+
end
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def set_account(account, retrieval_type)
|
|
685
|
+
account_reload(@account_retrieval_type, retrieval_type) if @account_retrieval_type
|
|
686
|
+
@account_retrieval_type = retrieval_type
|
|
687
|
+
@account = account
|
|
688
|
+
end
|
|
651
689
|
|
|
652
690
|
def database_function_password_match?(name, hash_id, password, salt)
|
|
653
691
|
db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
|
|
@@ -789,9 +827,9 @@ module Rodauth
|
|
|
789
827
|
when :postgres, :mysql, :mssql
|
|
790
828
|
true
|
|
791
829
|
else
|
|
792
|
-
# :
|
|
830
|
+
# simplecov:disable
|
|
793
831
|
false
|
|
794
|
-
# :
|
|
832
|
+
# simplecov:enable
|
|
795
833
|
end
|
|
796
834
|
end
|
|
797
835
|
|
|
@@ -805,9 +843,9 @@ module Rodauth
|
|
|
805
843
|
|
|
806
844
|
def function_name(name)
|
|
807
845
|
if db.database_type == :mssql
|
|
808
|
-
# :
|
|
846
|
+
# simplecov:disable
|
|
809
847
|
"dbo.#{name}"
|
|
810
|
-
# :
|
|
848
|
+
# simplecov:enable
|
|
811
849
|
else
|
|
812
850
|
name
|
|
813
851
|
end
|
|
@@ -823,14 +861,18 @@ module Rodauth
|
|
|
823
861
|
if account_password_hash_column
|
|
824
862
|
account[account_password_hash_column] if account!
|
|
825
863
|
elsif use_database_authentication_functions?
|
|
826
|
-
db.get(Sequel.function(function_name(:rodauth_get_salt),
|
|
864
|
+
db.get(Sequel.function(function_name(:rodauth_get_salt), account_id_or_session_value))
|
|
827
865
|
else
|
|
828
|
-
# :
|
|
866
|
+
# simplecov:disable
|
|
829
867
|
password_hash_ds.get(password_hash_column)
|
|
830
|
-
# :
|
|
868
|
+
# simplecov:enable
|
|
831
869
|
end
|
|
832
870
|
end
|
|
833
871
|
|
|
872
|
+
def new_password_matches_current_password?(password)
|
|
873
|
+
password_match?(password)
|
|
874
|
+
end
|
|
875
|
+
|
|
834
876
|
def _account_from_login(login)
|
|
835
877
|
ds = account_table_ds.where(login_column=>login)
|
|
836
878
|
ds = ds.select(*account_select) if account_select
|
|
@@ -895,7 +937,7 @@ module Rodauth
|
|
|
895
937
|
end
|
|
896
938
|
|
|
897
939
|
def password_hash_ds
|
|
898
|
-
db[password_hash_table].where(password_hash_id_column=>
|
|
940
|
+
db[password_hash_table].where(password_hash_id_column=>account_id_or_session_value)
|
|
899
941
|
end
|
|
900
942
|
|
|
901
943
|
# This is needed for jdbc/sqlite, which returns timestamp columns as strings
|
|
@@ -934,9 +976,9 @@ module Rodauth
|
|
|
934
976
|
# UniqueConstraintViolation.
|
|
935
977
|
def unique_constraint_violation_class
|
|
936
978
|
if db.adapter_scheme == :jdbc && db.database_type == :sqlite
|
|
937
|
-
# :
|
|
979
|
+
# simplecov:disable
|
|
938
980
|
Sequel::ConstraintViolation
|
|
939
|
-
# :
|
|
981
|
+
# simplecov:enable
|
|
940
982
|
else
|
|
941
983
|
Sequel::UniqueConstraintViolation
|
|
942
984
|
end
|
|
@@ -952,9 +994,9 @@ module Rodauth
|
|
|
952
994
|
# CURRENT_TIMESTAMP.
|
|
953
995
|
def set_deadline_value(hash, column, interval)
|
|
954
996
|
if set_deadline_values?
|
|
955
|
-
# :
|
|
997
|
+
# simplecov:disable
|
|
956
998
|
hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval)
|
|
957
|
-
# :
|
|
999
|
+
# simplecov:enable
|
|
958
1000
|
end
|
|
959
1001
|
end
|
|
960
1002
|
|
|
@@ -968,9 +1010,21 @@ module Rodauth
|
|
|
968
1010
|
false
|
|
969
1011
|
end
|
|
970
1012
|
|
|
971
|
-
|
|
1013
|
+
def valid_return_to_path?(path)
|
|
1014
|
+
path[0] == "/" &&
|
|
1015
|
+
path[1] != "/" &&
|
|
1016
|
+
path.bytesize <= return_to_path_max_size
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
def set_session_return_to_path(session_key, path=request.fullpath)
|
|
1020
|
+
if request.get? && valid_return_to_path?(path)
|
|
1021
|
+
set_session_value(session_key, path)
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
# simplecov:disable
|
|
972
1026
|
if RUBY_VERSION >= '4.0'
|
|
973
|
-
# :
|
|
1027
|
+
# simplecov:enable
|
|
974
1028
|
def instance_variables_to_inspect
|
|
975
1029
|
instance_variables.reject{|v| instance_variable_get(v).nil?}
|
|
976
1030
|
end
|
|
@@ -17,6 +17,7 @@ module Rodauth
|
|
|
17
17
|
|
|
18
18
|
translatable_method :new_password_label, 'New Password'
|
|
19
19
|
auth_value_method :new_password_param, 'new-password'
|
|
20
|
+
translatable_method :concurrent_password_update_message, 'a concurrent password update prevented the password change'
|
|
20
21
|
|
|
21
22
|
auth_value_methods(
|
|
22
23
|
:change_password_requires_password?,
|
|
@@ -38,15 +39,19 @@ module Rodauth
|
|
|
38
39
|
if change_password_requires_password? && !password_match?(param(password_param))
|
|
39
40
|
throw_error_reason(:invalid_previous_password, invalid_password_error_status, password_param, invalid_previous_password_message)
|
|
40
41
|
end
|
|
42
|
+
existing_hash = @existing_password_hash_or_salt || get_password_hash
|
|
41
43
|
|
|
42
44
|
password = param(new_password_param)
|
|
43
45
|
if require_password_confirmation? && password != param(password_confirm_param)
|
|
44
46
|
throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, new_password_param, passwords_do_not_match_message)
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
if
|
|
49
|
+
if new_password_matches_current_password?(password)
|
|
48
50
|
throw_error_reason(:same_as_existing_password, invalid_field_error_status, new_password_param, same_as_existing_password_message)
|
|
49
51
|
end
|
|
52
|
+
# Don't allow same as existing password check for new password to override
|
|
53
|
+
# known existing password hash/salt
|
|
54
|
+
@existing_password_hash_or_salt = existing_hash
|
|
50
55
|
|
|
51
56
|
unless password_meets_requirements?(password)
|
|
52
57
|
throw_error_status(invalid_field_error_status, new_password_param, password_does_not_meet_requirements_message)
|
|
@@ -54,7 +59,11 @@ module Rodauth
|
|
|
54
59
|
|
|
55
60
|
transaction do
|
|
56
61
|
before_change_password
|
|
57
|
-
|
|
62
|
+
begin
|
|
63
|
+
set_password(password)
|
|
64
|
+
rescue Rodauth::SetPasswordFailure
|
|
65
|
+
throw_error_reason(:concurrent_password_update, invalid_password_error_status, new_password_param, concurrent_password_update_message)
|
|
66
|
+
end
|
|
58
67
|
clear_tokens(:change_password)
|
|
59
68
|
after_change_password
|
|
60
69
|
end
|
|
@@ -55,7 +55,7 @@ module Rodauth
|
|
|
55
55
|
set_redirect_error_status(password_authentication_required_error_status)
|
|
56
56
|
set_error_reason :password_authentication_required
|
|
57
57
|
set_redirect_error_flash password_authentication_required_error_flash
|
|
58
|
-
|
|
58
|
+
set_session_return_to_path(confirm_password_redirect_session_key)
|
|
59
59
|
redirect password_authentication_required_redirect
|
|
60
60
|
end
|
|
61
61
|
end
|
|
@@ -59,11 +59,11 @@ module Rodauth
|
|
|
59
59
|
database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt)
|
|
60
60
|
end
|
|
61
61
|
else
|
|
62
|
-
# :
|
|
62
|
+
# simplecov:disable
|
|
63
63
|
previous_password_ds.select_map(previous_password_hash_column).any? do |hash|
|
|
64
64
|
password_hash_match?(hash, password)
|
|
65
65
|
end
|
|
66
|
-
# :
|
|
66
|
+
# simplecov:enable
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
return true unless match
|
|
@@ -89,8 +89,17 @@ module Rodauth
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
r.post do
|
|
92
|
-
key =
|
|
93
|
-
|
|
92
|
+
key = session_param(email_auth_session_key, email_auth_key_param)
|
|
93
|
+
remove_session_value(email_auth_session_key)
|
|
94
|
+
select_key_for_update!
|
|
95
|
+
|
|
96
|
+
valid = transaction do
|
|
97
|
+
if key && account_from_email_auth_key(key)
|
|
98
|
+
remove_email_auth_key == 1
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
unless valid
|
|
94
103
|
set_redirect_error_status(invalid_key_error_status)
|
|
95
104
|
set_error_reason :invalid_email_auth_key
|
|
96
105
|
set_redirect_error_flash email_auth_error_flash
|
|
@@ -131,7 +140,7 @@ module Rodauth
|
|
|
131
140
|
end
|
|
132
141
|
|
|
133
142
|
def account_from_email_auth_key(key)
|
|
134
|
-
|
|
143
|
+
set_account(_account_from_email_auth_key(key), :email_auth)
|
|
135
144
|
end
|
|
136
145
|
|
|
137
146
|
def email_auth_email_link
|
|
@@ -141,7 +150,7 @@ module Rodauth
|
|
|
141
150
|
def get_email_auth_key(id)
|
|
142
151
|
ds = email_auth_ds(id)
|
|
143
152
|
ds.where(Sequel::CURRENT_TIMESTAMP > email_auth_deadline_column).delete
|
|
144
|
-
ds.get(email_auth_key_column)
|
|
153
|
+
apply_key_for_update(ds).get(email_auth_key_column)
|
|
145
154
|
end
|
|
146
155
|
|
|
147
156
|
def email_auth_request_form
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:email_base, :EmailBase) do
|
|
5
|
+
depends :require_domain, :require_hmac_secret
|
|
6
|
+
|
|
5
7
|
translatable_method :email_subject_prefix, ''
|
|
6
8
|
auth_value_method :require_mail?, true
|
|
7
9
|
auth_value_method :allow_raw_email_token?, false
|
|
@@ -9,7 +11,8 @@ module Rodauth
|
|
|
9
11
|
redirect :default_post_email
|
|
10
12
|
|
|
11
13
|
auth_value_methods(
|
|
12
|
-
:email_from
|
|
14
|
+
:email_from,
|
|
15
|
+
:allow_param_fallback_for_session_param?
|
|
13
16
|
)
|
|
14
17
|
|
|
15
18
|
auth_methods(
|
|
@@ -18,6 +21,10 @@ module Rodauth
|
|
|
18
21
|
:send_email
|
|
19
22
|
)
|
|
20
23
|
|
|
24
|
+
uses_instance_variables(
|
|
25
|
+
:@key_for_update
|
|
26
|
+
)
|
|
27
|
+
|
|
21
28
|
def post_configure
|
|
22
29
|
super
|
|
23
30
|
require 'mail' if require_mail?
|
|
@@ -58,10 +65,38 @@ module Rodauth
|
|
|
58
65
|
"#{account_id}#{token_separator}#{convert_email_token_key(key)}"
|
|
59
66
|
end
|
|
60
67
|
|
|
68
|
+
def session_param(session_key, param_name)
|
|
69
|
+
unless value = session[session_key]
|
|
70
|
+
if allow_param_fallback_for_session_param?
|
|
71
|
+
value = param(param_name)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def allow_param_fallback_for_session_param?
|
|
79
|
+
return super if defined?(super)
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
|
|
61
83
|
def convert_email_token_key(key)
|
|
62
84
|
convert_token_key(key)
|
|
63
85
|
end
|
|
64
86
|
|
|
87
|
+
def select_key_for_update!
|
|
88
|
+
@key_for_update = true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def apply_key_for_update(ds)
|
|
92
|
+
if @key_for_update
|
|
93
|
+
@key_for_update = nil
|
|
94
|
+
ds.for_update
|
|
95
|
+
else
|
|
96
|
+
ds
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
65
100
|
def account_from_key(token, status_id=nil)
|
|
66
101
|
id, key = split_token(token)
|
|
67
102
|
return unless key && (id = convert_token_id(id))
|
|
@@ -4,13 +4,14 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:http_basic_auth, :HttpBasicAuth) do
|
|
5
5
|
auth_value_method :http_basic_auth_realm, "protected"
|
|
6
6
|
auth_value_method :require_http_basic_auth?, false
|
|
7
|
+
auth_value_method :logged_in_fallback_to_http_basic_auth?, true # RODAUTH3: false
|
|
7
8
|
|
|
8
9
|
uses_instance_variables(:@checked_http_basic_auth)
|
|
9
10
|
|
|
10
11
|
def logged_in?
|
|
11
12
|
ret = super
|
|
12
13
|
|
|
13
|
-
if !ret && @checked_http_basic_auth.nil?
|
|
14
|
+
if !ret && logged_in_fallback_to_http_basic_auth? && @checked_http_basic_auth.nil?
|
|
14
15
|
http_basic_auth
|
|
15
16
|
ret = super
|
|
16
17
|
end
|
|
@@ -33,6 +34,10 @@ module Rodauth
|
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
def account_from_session
|
|
38
|
+
@checked_http_basic_auth ? @account : super
|
|
39
|
+
end
|
|
40
|
+
|
|
36
41
|
def http_basic_auth
|
|
37
42
|
unless @checked_http_basic_auth.nil?
|
|
38
43
|
return (@checked_http_basic_auth ? true : nil)
|
|
@@ -8,7 +8,7 @@ module Rodauth
|
|
|
8
8
|
translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
|
|
9
9
|
auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
|
|
10
10
|
auth_value_method :json_check_accept?, true
|
|
11
|
-
auth_value_method :json_request_content_type_regexp, /\
|
|
11
|
+
auth_value_method :json_request_content_type_regexp, /\Aapplication\/(?:vnd\.api\+)?json\b/i
|
|
12
12
|
auth_value_method :json_response_content_type, 'application/json'
|
|
13
13
|
auth_value_method :json_response_custom_error_status?, true
|
|
14
14
|
auth_value_method :json_response_error_status, 400
|
|
@@ -81,6 +81,10 @@ module Rodauth
|
|
|
81
81
|
super
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def allow_param_fallback_for_session_param?
|
|
85
|
+
use_json?
|
|
86
|
+
end
|
|
87
|
+
|
|
84
88
|
def _set_otp_unlock_info
|
|
85
89
|
if use_json?
|
|
86
90
|
json_response[:num_successes] = otp_unlock_num_successes
|
data/lib/rodauth/features/jwt.rb
CHANGED
|
@@ -113,12 +113,12 @@ module Rodauth
|
|
|
113
113
|
secrets.compact!
|
|
114
114
|
secrets
|
|
115
115
|
end
|
|
116
|
-
# :
|
|
116
|
+
# simplecov:disable
|
|
117
117
|
else
|
|
118
118
|
def _jwt_decode_secrets
|
|
119
119
|
jwt_secret
|
|
120
120
|
end
|
|
121
|
-
# :
|
|
121
|
+
# simplecov:enable
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def jwt_payload
|
|
@@ -145,7 +145,13 @@ module Rodauth
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def return_json_response
|
|
148
|
-
|
|
148
|
+
case response.status
|
|
149
|
+
when 405, 406
|
|
150
|
+
response.headers.delete('authorization')
|
|
151
|
+
else
|
|
152
|
+
set_jwt
|
|
153
|
+
end
|
|
154
|
+
|
|
149
155
|
super
|
|
150
156
|
end
|
|
151
157
|
|