rodauth 2.36.0 → 2.38.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/base.rb +52 -9
- data/lib/rodauth/features/change_login.rb +2 -2
- data/lib/rodauth/features/create_account.rb +2 -2
- data/lib/rodauth/features/email_auth.rb +5 -5
- data/lib/rodauth/features/internal_request.rb +4 -4
- data/lib/rodauth/features/json.rb +5 -0
- data/lib/rodauth/features/jwt.rb +7 -11
- data/lib/rodauth/features/lockout.rb +5 -5
- data/lib/rodauth/features/login.rb +1 -1
- data/lib/rodauth/features/login_password_requirements_base.rb +13 -0
- data/lib/rodauth/features/reset_password.rb +5 -5
- data/lib/rodauth/features/sms_codes.rb +1 -1
- data/lib/rodauth/features/two_factor_base.rb +6 -13
- data/lib/rodauth/features/verify_account.rb +6 -6
- data/lib/rodauth/features/webauthn_autofill.rb +2 -1
- data/lib/rodauth/features/webauthn_login.rb +1 -1
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +8 -2
- metadata +3 -261
- data/CHANGELOG +0 -521
- data/README.rdoc +0 -1555
- data/doc/account_expiration.rdoc +0 -41
- data/doc/active_sessions.rdoc +0 -56
- data/doc/argon2.rdoc +0 -54
- data/doc/audit_logging.rdoc +0 -44
- data/doc/base.rdoc +0 -123
- data/doc/change_login.rdoc +0 -25
- data/doc/change_password.rdoc +0 -26
- data/doc/change_password_notify.rdoc +0 -14
- data/doc/close_account.rdoc +0 -26
- data/doc/confirm_password.rdoc +0 -32
- data/doc/create_account.rdoc +0 -27
- data/doc/disallow_common_passwords.rdoc +0 -17
- data/doc/disallow_password_reuse.rdoc +0 -30
- data/doc/email_auth.rdoc +0 -55
- data/doc/email_base.rdoc +0 -18
- data/doc/error_reasons.rdoc +0 -77
- data/doc/guides/admin_activation.rdoc +0 -46
- data/doc/guides/already_authenticated.rdoc +0 -10
- data/doc/guides/alternative_login.rdoc +0 -46
- data/doc/guides/change_table_and_column_names.rdoc +0 -19
- data/doc/guides/create_account_programmatically.rdoc +0 -38
- data/doc/guides/delay_password.rdoc +0 -25
- data/doc/guides/email_only.rdoc +0 -16
- data/doc/guides/i18n.rdoc +0 -29
- data/doc/guides/internals.rdoc +0 -233
- data/doc/guides/links.rdoc +0 -12
- data/doc/guides/login_return.rdoc +0 -37
- data/doc/guides/migrate_password_hash_algorithm.rdoc +0 -15
- data/doc/guides/password_column.rdoc +0 -25
- data/doc/guides/password_confirmation.rdoc +0 -37
- data/doc/guides/password_requirements.rdoc +0 -43
- data/doc/guides/paths.rdoc +0 -51
- data/doc/guides/query_params.rdoc +0 -9
- data/doc/guides/redirects.rdoc +0 -17
- data/doc/guides/registration_field.rdoc +0 -68
- data/doc/guides/render_confirmation.rdoc +0 -17
- data/doc/guides/require_mfa.rdoc +0 -30
- data/doc/guides/reset_password_autologin.rdoc +0 -21
- data/doc/guides/share_configuration.rdoc +0 -34
- data/doc/guides/status_column.rdoc +0 -28
- data/doc/guides/totp_or_recovery.rdoc +0 -16
- data/doc/http_basic_auth.rdoc +0 -18
- data/doc/internal_request.rdoc +0 -539
- data/doc/json.rdoc +0 -56
- data/doc/jwt.rdoc +0 -52
- data/doc/jwt_cors.rdoc +0 -22
- data/doc/jwt_refresh.rdoc +0 -58
- data/doc/lockout.rdoc +0 -73
- data/doc/login.rdoc +0 -39
- data/doc/login_password_requirements_base.rdoc +0 -44
- data/doc/logout.rdoc +0 -22
- data/doc/otp.rdoc +0 -93
- data/doc/otp_lockout_email.rdoc +0 -30
- data/doc/otp_modify_email.rdoc +0 -19
- data/doc/otp_unlock.rdoc +0 -58
- data/doc/password_complexity.rdoc +0 -34
- data/doc/password_expiration.rdoc +0 -38
- data/doc/password_grace_period.rdoc +0 -24
- data/doc/password_pepper.rdoc +0 -52
- data/doc/path_class_methods.rdoc +0 -10
- data/doc/recovery_codes.rdoc +0 -61
- data/doc/release_notes/1.0.0.txt +0 -443
- data/doc/release_notes/1.1.0.txt +0 -8
- data/doc/release_notes/1.10.0.txt +0 -80
- data/doc/release_notes/1.11.0.txt +0 -32
- data/doc/release_notes/1.12.0.txt +0 -61
- data/doc/release_notes/1.13.0.txt +0 -34
- data/doc/release_notes/1.14.0.txt +0 -19
- data/doc/release_notes/1.15.0.txt +0 -21
- data/doc/release_notes/1.16.0.txt +0 -31
- data/doc/release_notes/1.17.0.txt +0 -23
- data/doc/release_notes/1.18.0.txt +0 -26
- data/doc/release_notes/1.19.0.txt +0 -116
- data/doc/release_notes/1.2.0.txt +0 -18
- data/doc/release_notes/1.20.0.txt +0 -175
- data/doc/release_notes/1.21.0.txt +0 -12
- data/doc/release_notes/1.22.0.txt +0 -11
- data/doc/release_notes/1.23.0.txt +0 -32
- data/doc/release_notes/1.3.0.txt +0 -21
- data/doc/release_notes/1.4.0.txt +0 -11
- data/doc/release_notes/1.5.0.txt +0 -74
- data/doc/release_notes/1.6.0.txt +0 -37
- data/doc/release_notes/1.7.0.txt +0 -6
- data/doc/release_notes/1.8.0.txt +0 -14
- data/doc/release_notes/1.9.0.txt +0 -15
- data/doc/release_notes/2.0.0.txt +0 -361
- data/doc/release_notes/2.1.0.txt +0 -31
- data/doc/release_notes/2.10.0.txt +0 -47
- data/doc/release_notes/2.11.0.txt +0 -31
- data/doc/release_notes/2.12.0.txt +0 -17
- data/doc/release_notes/2.13.0.txt +0 -19
- data/doc/release_notes/2.14.0.txt +0 -17
- data/doc/release_notes/2.15.0.txt +0 -48
- data/doc/release_notes/2.16.0.txt +0 -20
- data/doc/release_notes/2.17.0.txt +0 -10
- data/doc/release_notes/2.18.0.txt +0 -27
- data/doc/release_notes/2.19.0.txt +0 -61
- data/doc/release_notes/2.2.0.txt +0 -39
- data/doc/release_notes/2.20.0.txt +0 -10
- data/doc/release_notes/2.21.0.txt +0 -28
- data/doc/release_notes/2.22.0.txt +0 -43
- data/doc/release_notes/2.23.0.txt +0 -15
- data/doc/release_notes/2.24.0.txt +0 -15
- data/doc/release_notes/2.25.0.txt +0 -8
- data/doc/release_notes/2.26.0.txt +0 -45
- data/doc/release_notes/2.27.0.txt +0 -35
- data/doc/release_notes/2.28.0.txt +0 -16
- data/doc/release_notes/2.29.0.txt +0 -27
- data/doc/release_notes/2.3.0.txt +0 -37
- data/doc/release_notes/2.30.0.txt +0 -15
- data/doc/release_notes/2.31.0.txt +0 -47
- data/doc/release_notes/2.32.0.txt +0 -65
- data/doc/release_notes/2.33.0.txt +0 -18
- data/doc/release_notes/2.34.0.txt +0 -36
- data/doc/release_notes/2.35.0.txt +0 -22
- data/doc/release_notes/2.36.0.txt +0 -35
- data/doc/release_notes/2.4.0.txt +0 -22
- data/doc/release_notes/2.5.0.txt +0 -20
- data/doc/release_notes/2.6.0.txt +0 -37
- data/doc/release_notes/2.7.0.txt +0 -33
- data/doc/release_notes/2.8.0.txt +0 -20
- data/doc/release_notes/2.9.0.txt +0 -21
- data/doc/remember.rdoc +0 -79
- data/doc/reset_password.rdoc +0 -66
- data/doc/reset_password_notify.rdoc +0 -17
- data/doc/session_expiration.rdoc +0 -28
- data/doc/single_session.rdoc +0 -37
- data/doc/sms_codes.rdoc +0 -138
- data/doc/two_factor_base.rdoc +0 -70
- data/doc/update_password_hash.rdoc +0 -7
- data/doc/verify_account.rdoc +0 -67
- data/doc/verify_account_grace_period.rdoc +0 -19
- data/doc/verify_login_change.rdoc +0 -59
- data/doc/webauthn.rdoc +0 -118
- data/doc/webauthn_autofill.rdoc +0 -19
- data/doc/webauthn_login.rdoc +0 -16
- data/doc/webauthn_modify_email.rdoc +0 -19
- data/doc/webauthn_verify_account.rdoc +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f79db7dec7147665538bf0b4f8e0c6c554d88396b294f19e5a5ed26543c31d1c
|
|
4
|
+
data.tar.gz: ea377861679a55895bc325b1cf3932970b0de9c773615ba2daecb5805704ae02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e13b4dc188867866ee0eda53765091bb48f583d65957e488737b612bde4cf4ae9caa18edd2d88b3c609a7a3e25a51eec42fc5aa25e4083ea51c283310a36cb9
|
|
7
|
+
data.tar.gz: d50e275056bf3196a025e1933ab97ecd88d784c80ec97412cf046bb9718f558da4b321f7fbf52ba09d3554059157ef67f3f407625fd65c237fd2334e170066a7
|
|
@@ -67,6 +67,7 @@ module Rodauth
|
|
|
67
67
|
auth_value_method :unopen_account_error_status, 403
|
|
68
68
|
translatable_method :unverified_account_message, "unverified account, please verify account before logging in"
|
|
69
69
|
auth_value_method :default_field_attributes, ''
|
|
70
|
+
auth_value_method :use_template_fixed_locals?, true
|
|
70
71
|
|
|
71
72
|
redirect(:require_login){"#{prefix}/login"}
|
|
72
73
|
|
|
@@ -98,6 +99,7 @@ module Rodauth
|
|
|
98
99
|
:inputmode_for_field?,
|
|
99
100
|
:logged_in?,
|
|
100
101
|
:login_required,
|
|
102
|
+
:normalize_login,
|
|
101
103
|
:null_byte_parameter_value,
|
|
102
104
|
:open_account?,
|
|
103
105
|
:over_max_bytesize_param_value,
|
|
@@ -321,7 +323,7 @@ module Rodauth
|
|
|
321
323
|
end
|
|
322
324
|
|
|
323
325
|
def clear_session
|
|
324
|
-
if
|
|
326
|
+
if use_scope_clear_session?
|
|
325
327
|
scope.clear_session
|
|
326
328
|
else
|
|
327
329
|
session.clear
|
|
@@ -408,6 +410,7 @@ module Rodauth
|
|
|
408
410
|
|
|
409
411
|
def button_opts(value, opts)
|
|
410
412
|
opts = Hash[template_opts].merge!(opts)
|
|
413
|
+
_merge_fixed_locals_opts(opts, button_fixed_locals)
|
|
411
414
|
opts[:locals] = {:value=>value, :opts=>opts}
|
|
412
415
|
opts[:cache] = cache_templates
|
|
413
416
|
opts[:cache_key] = :rodauth_button
|
|
@@ -505,6 +508,15 @@ module Rodauth
|
|
|
505
508
|
nil
|
|
506
509
|
end
|
|
507
510
|
|
|
511
|
+
# The normalized value of the login parameter
|
|
512
|
+
def login_param_value
|
|
513
|
+
normalize_login(param(login_param))
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def normalize_login(login)
|
|
517
|
+
login
|
|
518
|
+
end
|
|
519
|
+
|
|
508
520
|
# Return nil by default for values with null bytes
|
|
509
521
|
def null_byte_parameter_value(key, value)
|
|
510
522
|
nil
|
|
@@ -532,6 +544,12 @@ module Rodauth
|
|
|
532
544
|
has_password? ? ['password'] : []
|
|
533
545
|
end
|
|
534
546
|
|
|
547
|
+
def has_password?
|
|
548
|
+
return @has_password if defined?(@has_password)
|
|
549
|
+
return false unless account || session_value
|
|
550
|
+
@has_password = !!get_password_hash
|
|
551
|
+
end
|
|
552
|
+
|
|
535
553
|
private
|
|
536
554
|
|
|
537
555
|
def _around_rodauth
|
|
@@ -545,6 +563,20 @@ module Rodauth
|
|
|
545
563
|
s
|
|
546
564
|
end
|
|
547
565
|
|
|
566
|
+
if RUBY_VERSION >= '2.1'
|
|
567
|
+
def button_fixed_locals
|
|
568
|
+
'(value:, opts:)'
|
|
569
|
+
end
|
|
570
|
+
# :nocov:
|
|
571
|
+
else
|
|
572
|
+
# Work on Ruby 2.0 when using Tilt 2.6+, as Ruby 2.0 does
|
|
573
|
+
# not support required keyword arguments.
|
|
574
|
+
def button_fixed_locals
|
|
575
|
+
'(value: nil, opts: nil)'
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
# :nocov:
|
|
579
|
+
|
|
548
580
|
def database_function_password_match?(name, hash_id, password, salt)
|
|
549
581
|
db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
|
|
550
582
|
end
|
|
@@ -708,12 +740,6 @@ module Rodauth
|
|
|
708
740
|
end
|
|
709
741
|
end
|
|
710
742
|
|
|
711
|
-
def has_password?
|
|
712
|
-
return @has_password if defined?(@has_password)
|
|
713
|
-
return false unless account || session_value
|
|
714
|
-
@has_password = !!get_password_hash
|
|
715
|
-
end
|
|
716
|
-
|
|
717
743
|
def password_hash_using_salt(password, salt)
|
|
718
744
|
BCrypt::Engine.hash_secret(password, salt)
|
|
719
745
|
end
|
|
@@ -756,7 +782,7 @@ module Rodauth
|
|
|
756
782
|
end
|
|
757
783
|
|
|
758
784
|
def compute_raw_hmac(data)
|
|
759
|
-
raise
|
|
785
|
+
raise ConfigurationError, "hmac_secret not set" unless hmac_secret
|
|
760
786
|
compute_raw_hmac_with_secret(data, hmac_secret)
|
|
761
787
|
end
|
|
762
788
|
|
|
@@ -869,9 +895,13 @@ module Rodauth
|
|
|
869
895
|
false
|
|
870
896
|
end
|
|
871
897
|
|
|
898
|
+
def use_scope_clear_session?
|
|
899
|
+
scope.respond_to?(:clear_session)
|
|
900
|
+
end
|
|
901
|
+
|
|
872
902
|
def require_response(meth)
|
|
873
903
|
send(meth)
|
|
874
|
-
raise
|
|
904
|
+
raise ConfigurationError, "#{meth.to_s.sub(/\A_/, '')} overridden without returning a response (should use redirect or request.halt)."
|
|
875
905
|
end
|
|
876
906
|
|
|
877
907
|
def set_session_value(key, value)
|
|
@@ -898,6 +928,7 @@ module Rodauth
|
|
|
898
928
|
|
|
899
929
|
def _view_opts(page)
|
|
900
930
|
opts = template_opts.dup
|
|
931
|
+
_merge_fixed_locals_opts(opts, '(rodauth: self.rodauth)')
|
|
901
932
|
opts[:locals] = opts[:locals] ? opts[:locals].dup : {}
|
|
902
933
|
opts[:locals][:rodauth] = self
|
|
903
934
|
opts[:cache] = cache_templates
|
|
@@ -905,6 +936,14 @@ module Rodauth
|
|
|
905
936
|
_template_opts(opts, page)
|
|
906
937
|
end
|
|
907
938
|
|
|
939
|
+
def _merge_fixed_locals_opts(opts, fixed_locals)
|
|
940
|
+
if use_template_fixed_locals? && !opts[:locals]
|
|
941
|
+
fixed_locals_opts = {default_fixed_locals: fixed_locals}
|
|
942
|
+
fixed_locals_opts.merge!(opts[:template_opts]) if opts[:template_opts]
|
|
943
|
+
opts[:template_opts] = fixed_locals_opts
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
908
947
|
# Set the template path only if there isn't an overridden template in the application.
|
|
909
948
|
# Result should replace existing template opts.
|
|
910
949
|
def _template_opts(opts, page)
|
|
@@ -916,6 +955,10 @@ module Rodauth
|
|
|
916
955
|
end
|
|
917
956
|
|
|
918
957
|
def _view(meth, page)
|
|
958
|
+
unless scope.respond_to?(meth)
|
|
959
|
+
raise ConfigurationError, "attempted to render a built-in view/email template (#{page.inspect}), but rendering is disabled"
|
|
960
|
+
end
|
|
961
|
+
|
|
919
962
|
scope.send(meth, _view_opts(page))
|
|
920
963
|
end
|
|
921
964
|
end
|
|
@@ -36,12 +36,12 @@ module Rodauth
|
|
|
36
36
|
throw_error_reason(:invalid_password, invalid_password_error_status, password_param, invalid_password_message)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
login =
|
|
39
|
+
login = login_param_value
|
|
40
40
|
unless login_meets_requirements?(login)
|
|
41
41
|
throw_error_status(invalid_field_error_status, login_param, login_does_not_meet_requirements_message)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
if require_login_confirmation? && login
|
|
44
|
+
if require_login_confirmation? && !login_confirmation_matches?(login, param(login_confirm_param))
|
|
45
45
|
throw_error_reason(:logins_do_not_match, unmatched_field_error_status, login_param, logins_do_not_match_message)
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -40,12 +40,12 @@ module Rodauth
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
r.post do
|
|
43
|
-
login =
|
|
43
|
+
login = login_param_value
|
|
44
44
|
password = param(password_param)
|
|
45
45
|
new_account(login)
|
|
46
46
|
|
|
47
47
|
catch_error do
|
|
48
|
-
if require_login_confirmation? && login
|
|
48
|
+
if require_login_confirmation? && !login_confirmation_matches?(login, param(login_confirm_param))
|
|
49
49
|
throw_error_reason(:logins_do_not_match, unmatched_field_error_status, login_param, logins_do_not_match_message)
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -56,7 +56,7 @@ module Rodauth
|
|
|
56
56
|
before_email_auth_request_route
|
|
57
57
|
|
|
58
58
|
r.post do
|
|
59
|
-
if account_from_login(
|
|
59
|
+
if account_from_login(login_param_value) && open_account?
|
|
60
60
|
_email_auth_request
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -163,6 +163,10 @@ module Rodauth
|
|
|
163
163
|
methods
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
+
def email_auth_email_recently_sent?
|
|
167
|
+
(email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within)
|
|
168
|
+
end
|
|
169
|
+
|
|
166
170
|
private
|
|
167
171
|
|
|
168
172
|
def _multi_phase_login_forms
|
|
@@ -171,10 +175,6 @@ module Rodauth
|
|
|
171
175
|
forms
|
|
172
176
|
end
|
|
173
177
|
|
|
174
|
-
def email_auth_email_recently_sent?
|
|
175
|
-
(email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
178
|
def _email_auth_request
|
|
179
179
|
if email_auth_email_recently_sent?
|
|
180
180
|
set_redirect_error_flash email_auth_email_recently_sent_error_flash
|
|
@@ -223,14 +223,14 @@ module Rodauth
|
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
def _handle_account_id_for_login(_)
|
|
226
|
-
raise InternalRequestError, "no login provided" unless
|
|
227
|
-
raise InternalRequestError, "no account for login" unless account = account_from_login(
|
|
226
|
+
raise InternalRequestError, "no login provided" unless param_or_nil(login_param)
|
|
227
|
+
raise InternalRequestError, "no account for login" unless account = account_from_login(login_param_value)
|
|
228
228
|
_return_from_internal_request(account[account_id_column])
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def _handle_account_exists?(_)
|
|
232
|
-
raise InternalRequestError, "no login provided" unless
|
|
233
|
-
_return_from_internal_request(!!account_from_login(
|
|
232
|
+
raise InternalRequestError, "no login provided" unless param_or_nil(login_param)
|
|
233
|
+
_return_from_internal_request(!!account_from_login(login_param_value))
|
|
234
234
|
end
|
|
235
235
|
|
|
236
236
|
def _handle_lock_account(_)
|
data/lib/rodauth/features/jwt.rb
CHANGED
|
@@ -60,14 +60,11 @@ module Rodauth
|
|
|
60
60
|
|
|
61
61
|
def clear_session
|
|
62
62
|
super
|
|
63
|
-
if use_jwt?
|
|
64
|
-
session.clear
|
|
65
|
-
set_jwt
|
|
66
|
-
end
|
|
63
|
+
set_jwt if use_jwt?
|
|
67
64
|
end
|
|
68
65
|
|
|
69
66
|
def jwt_secret
|
|
70
|
-
raise
|
|
67
|
+
raise ConfigurationError, "jwt_secret not set"
|
|
71
68
|
end
|
|
72
69
|
|
|
73
70
|
def jwt_session_hash
|
|
@@ -104,16 +101,11 @@ module Rodauth
|
|
|
104
101
|
|
|
105
102
|
private
|
|
106
103
|
|
|
107
|
-
def check_csrf?
|
|
108
|
-
return false if use_jwt?
|
|
109
|
-
super
|
|
110
|
-
end
|
|
111
|
-
|
|
112
104
|
def _jwt_decode_opts
|
|
113
105
|
jwt_decode_opts
|
|
114
106
|
end
|
|
115
107
|
|
|
116
|
-
if JWT
|
|
108
|
+
if JWT.gem_version >= Gem::Version.new("2.4")
|
|
117
109
|
def _jwt_decode_secrets
|
|
118
110
|
secrets = [jwt_secret, jwt_old_secret]
|
|
119
111
|
secrets.compact!
|
|
@@ -158,5 +150,9 @@ module Rodauth
|
|
|
158
150
|
def set_jwt
|
|
159
151
|
set_jwt_token(session_jwt)
|
|
160
152
|
end
|
|
153
|
+
|
|
154
|
+
def use_scope_clear_session?
|
|
155
|
+
super && !use_jwt?
|
|
156
|
+
end
|
|
161
157
|
end
|
|
162
158
|
end
|
|
@@ -70,7 +70,7 @@ module Rodauth
|
|
|
70
70
|
before_unlock_account_request_route
|
|
71
71
|
|
|
72
72
|
r.post do
|
|
73
|
-
if account_from_login(
|
|
73
|
+
if account_from_login(login_param_value) && get_unlock_account_key
|
|
74
74
|
if unlock_account_email_recently_sent?
|
|
75
75
|
set_redirect_error_flash unlock_account_email_recently_sent_error_flash
|
|
76
76
|
redirect unlock_account_email_recently_sent_redirect
|
|
@@ -237,6 +237,10 @@ module Rodauth
|
|
|
237
237
|
account_lockouts_ds.update(account_lockouts_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if account_lockouts_email_last_sent_column
|
|
238
238
|
end
|
|
239
239
|
|
|
240
|
+
def unlock_account_email_recently_sent?
|
|
241
|
+
(email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within)
|
|
242
|
+
end
|
|
243
|
+
|
|
240
244
|
private
|
|
241
245
|
|
|
242
246
|
attr_reader :unlock_account_key_value
|
|
@@ -278,10 +282,6 @@ module Rodauth
|
|
|
278
282
|
return_response unlock_account_request_view
|
|
279
283
|
end
|
|
280
284
|
|
|
281
|
-
def unlock_account_email_recently_sent?
|
|
282
|
-
(email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within)
|
|
283
|
-
end
|
|
284
|
-
|
|
285
285
|
def use_date_arithmetic?
|
|
286
286
|
super || db.database_type == :mysql
|
|
287
287
|
end
|
|
@@ -45,7 +45,7 @@ module Rodauth
|
|
|
45
45
|
view = :login_view
|
|
46
46
|
|
|
47
47
|
catch_error do
|
|
48
|
-
unless account_from_login(
|
|
48
|
+
unless account_from_login(login_param_value)
|
|
49
49
|
throw_error_reason(:no_matching_login, no_matching_login_error_status, login_param, no_matching_login_message)
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -36,6 +36,7 @@ module Rodauth
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
auth_methods(
|
|
39
|
+
:login_confirmation_matches?,
|
|
39
40
|
:login_meets_requirements?,
|
|
40
41
|
:login_valid_email?,
|
|
41
42
|
:password_hash,
|
|
@@ -126,6 +127,18 @@ module Rodauth
|
|
|
126
127
|
@login_requirement_message = message
|
|
127
128
|
end
|
|
128
129
|
|
|
130
|
+
if RUBY_VERSION >= '2.4'
|
|
131
|
+
def login_confirmation_matches?(login, login_confirmation)
|
|
132
|
+
login.casecmp?(login_confirmation)
|
|
133
|
+
end
|
|
134
|
+
# :nocov:
|
|
135
|
+
else
|
|
136
|
+
def login_confirmation_matches?(login, login_confirmation)
|
|
137
|
+
login.casecmp(login_confirmation) == 0
|
|
138
|
+
end
|
|
139
|
+
# :nocov:
|
|
140
|
+
end
|
|
141
|
+
|
|
129
142
|
def login_meets_length_requirements?(login)
|
|
130
143
|
if login_minimum_length > login.length
|
|
131
144
|
set_login_requirement_error_message(:login_too_short, login_too_short_message)
|
|
@@ -69,7 +69,7 @@ module Rodauth
|
|
|
69
69
|
|
|
70
70
|
r.post do
|
|
71
71
|
catch_error do
|
|
72
|
-
unless account_from_login(
|
|
72
|
+
unless account_from_login(login_param_value)
|
|
73
73
|
throw_error_reason(:no_matching_login, no_matching_login_error_status, login_param, no_matching_login_message)
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -204,16 +204,16 @@ module Rodauth
|
|
|
204
204
|
end
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
def reset_password_email_recently_sent?
|
|
208
|
+
(email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within)
|
|
209
|
+
end
|
|
210
|
+
|
|
207
211
|
private
|
|
208
212
|
|
|
209
213
|
def _login_form_footer_links
|
|
210
214
|
super << [20, reset_password_request_path, reset_password_request_link_text]
|
|
211
215
|
end
|
|
212
216
|
|
|
213
|
-
def reset_password_email_recently_sent?
|
|
214
|
-
(email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within)
|
|
215
|
-
end
|
|
216
|
-
|
|
217
217
|
attr_reader :reset_password_key_value
|
|
218
218
|
|
|
219
219
|
def after_login_failure
|
|
@@ -514,7 +514,7 @@ module Rodauth
|
|
|
514
514
|
end
|
|
515
515
|
|
|
516
516
|
def sms_send(phone, message)
|
|
517
|
-
raise
|
|
517
|
+
raise ConfigurationError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work"
|
|
518
518
|
end
|
|
519
519
|
|
|
520
520
|
def update_sms(values)
|
|
@@ -124,23 +124,12 @@ module Rodauth
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def authenticated?
|
|
127
|
-
|
|
128
|
-
return false unless super
|
|
129
|
-
|
|
130
|
-
# True if already authenticated via 2nd factor
|
|
131
|
-
return true if two_factor_authenticated?
|
|
132
|
-
|
|
133
|
-
# True if authenticated via single factor and 2nd factor not setup
|
|
134
|
-
!uses_two_factor_authentication?
|
|
127
|
+
super && !two_factor_partially_authenticated?
|
|
135
128
|
end
|
|
136
129
|
|
|
137
130
|
def require_authentication
|
|
138
131
|
super
|
|
139
|
-
|
|
140
|
-
# Avoid database query if already authenticated via 2nd factor
|
|
141
|
-
return if two_factor_authenticated?
|
|
142
|
-
|
|
143
|
-
require_two_factor_authenticated if uses_two_factor_authentication?
|
|
132
|
+
require_two_factor_authenticated if two_factor_partially_authenticated?
|
|
144
133
|
end
|
|
145
134
|
|
|
146
135
|
def require_two_factor_setup
|
|
@@ -188,6 +177,10 @@ module Rodauth
|
|
|
188
177
|
end
|
|
189
178
|
end
|
|
190
179
|
|
|
180
|
+
def two_factor_partially_authenticated?
|
|
181
|
+
logged_in? && !two_factor_authenticated? && uses_two_factor_authentication?
|
|
182
|
+
end
|
|
183
|
+
|
|
191
184
|
def two_factor_authenticated?
|
|
192
185
|
authenticated_by && authenticated_by.length >= 2
|
|
193
186
|
end
|
|
@@ -71,7 +71,7 @@ module Rodauth
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
r.post do
|
|
74
|
-
if account_from_login(
|
|
74
|
+
if account_from_login(login_param_value) && allow_resending_verify_account_email?
|
|
75
75
|
if verify_account_email_recently_sent?
|
|
76
76
|
set_redirect_error_flash verify_account_email_recently_sent_error_flash
|
|
77
77
|
redirect verify_account_email_recently_sent_redirect
|
|
@@ -240,20 +240,20 @@ module Rodauth
|
|
|
240
240
|
send_verify_account_email
|
|
241
241
|
end
|
|
242
242
|
|
|
243
|
+
def verify_account_email_recently_sent?
|
|
244
|
+
account && (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within)
|
|
245
|
+
end
|
|
246
|
+
|
|
243
247
|
private
|
|
244
248
|
|
|
245
249
|
def _login_form_footer_links
|
|
246
250
|
links = super
|
|
247
|
-
if !param_or_nil(login_param) || ((account || account_from_login(
|
|
251
|
+
if !param_or_nil(login_param) || ((account || account_from_login(login_param_value)) && allow_resending_verify_account_email?)
|
|
248
252
|
links << [30, verify_account_resend_path, verify_account_resend_link_text]
|
|
249
253
|
end
|
|
250
254
|
links
|
|
251
255
|
end
|
|
252
256
|
|
|
253
|
-
def verify_account_email_recently_sent?
|
|
254
|
-
(email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
257
|
attr_reader :verify_account_key_value
|
|
258
258
|
|
|
259
259
|
def before_login_attempt
|
|
@@ -4,6 +4,7 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:webauthn_autofill, :WebauthnAutofill) do
|
|
5
5
|
depends :webauthn_login
|
|
6
6
|
|
|
7
|
+
auth_value_method :webauthn_autofill?, true
|
|
7
8
|
auth_value_method :webauthn_autofill_js, File.binread(File.expand_path('../../../../javascript/webauthn_autofill.js', __FILE__)).freeze
|
|
8
9
|
|
|
9
10
|
translatable_method :webauthn_invalid_webauthn_id_message, "no webauthn key with given id found"
|
|
@@ -37,7 +38,7 @@ module Rodauth
|
|
|
37
38
|
|
|
38
39
|
def _login_form_footer
|
|
39
40
|
footer = super
|
|
40
|
-
footer += render("webauthn-autofill")
|
|
41
|
+
footer += render("webauthn-autofill") if webauthn_autofill? && !valid_login_entered?
|
|
41
42
|
footer
|
|
42
43
|
end
|
|
43
44
|
|
data/lib/rodauth/version.rb
CHANGED
data/lib/rodauth.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
|
|
5
5
|
module Rodauth
|
|
6
|
+
class ConfigurationError < StandardError; end
|
|
7
|
+
|
|
6
8
|
def self.lib(opts={}, &block)
|
|
7
9
|
require 'roda'
|
|
8
10
|
c = Class.new(Roda)
|
|
@@ -402,7 +404,11 @@ module Rodauth
|
|
|
402
404
|
end
|
|
403
405
|
|
|
404
406
|
module InstanceMethods
|
|
405
|
-
def
|
|
407
|
+
def default_rodauth_name
|
|
408
|
+
nil
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def rodauth(name=default_rodauth_name)
|
|
406
412
|
if name
|
|
407
413
|
(@_rodauths ||= {})[name] ||= self.class.rodauth(name).new(self)
|
|
408
414
|
else
|
|
@@ -440,7 +446,7 @@ module Rodauth
|
|
|
440
446
|
end
|
|
441
447
|
|
|
442
448
|
module RequestMethods
|
|
443
|
-
def rodauth(name=
|
|
449
|
+
def rodauth(name=scope.default_rodauth_name)
|
|
444
450
|
scope.rodauth(name).route!
|
|
445
451
|
end
|
|
446
452
|
end
|