rodauth 1.23.0 → 2.4.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/CHANGELOG +184 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +221 -79
- data/doc/account_expiration.rdoc +12 -26
- data/doc/active_sessions.rdoc +49 -0
- data/doc/audit_logging.rdoc +44 -0
- data/doc/base.rdoc +76 -128
- data/doc/change_login.rdoc +7 -14
- data/doc/change_password.rdoc +9 -13
- data/doc/change_password_notify.rdoc +2 -2
- data/doc/close_account.rdoc +9 -16
- data/doc/confirm_password.rdoc +12 -5
- data/doc/create_account.rdoc +11 -22
- data/doc/disallow_password_reuse.rdoc +6 -13
- data/doc/email_auth.rdoc +15 -14
- data/doc/email_base.rdoc +5 -15
- data/doc/guides/admin_activation.rdoc +46 -0
- data/doc/guides/already_authenticated.rdoc +10 -0
- data/doc/guides/alternative_login.rdoc +46 -0
- data/doc/guides/create_account_programmatically.rdoc +38 -0
- data/doc/guides/delay_password.rdoc +25 -0
- data/doc/guides/email_only.rdoc +16 -0
- data/doc/guides/i18n.rdoc +26 -0
- data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
- data/doc/guides/links.rdoc +12 -0
- data/doc/guides/login_return.rdoc +37 -0
- data/doc/guides/password_column.rdoc +25 -0
- data/doc/guides/password_confirmation.rdoc +37 -0
- data/doc/guides/password_requirements.rdoc +30 -0
- data/doc/guides/paths.rdoc +36 -0
- data/doc/guides/query_params.rdoc +9 -0
- data/doc/guides/redirects.rdoc +17 -0
- data/doc/guides/registration_field.rdoc +68 -0
- data/doc/guides/require_mfa.rdoc +30 -0
- data/doc/guides/reset_password_autologin.rdoc +21 -0
- data/doc/guides/status_column.rdoc +28 -0
- data/doc/guides/totp_or_recovery.rdoc +16 -0
- data/doc/http_basic_auth.rdoc +10 -1
- data/doc/jwt.rdoc +22 -22
- data/doc/jwt_cors.rdoc +2 -3
- data/doc/jwt_refresh.rdoc +23 -8
- data/doc/lockout.rdoc +17 -15
- data/doc/login.rdoc +17 -2
- data/doc/login_password_requirements_base.rdoc +18 -37
- data/doc/logout.rdoc +2 -2
- data/doc/otp.rdoc +25 -19
- data/doc/password_complexity.rdoc +10 -26
- data/doc/password_expiration.rdoc +11 -25
- data/doc/password_grace_period.rdoc +16 -2
- data/doc/password_pepper.rdoc +44 -0
- data/doc/recovery_codes.rdoc +18 -12
- data/doc/release_notes/2.0.0.txt +361 -0
- data/doc/release_notes/2.1.0.txt +31 -0
- data/doc/release_notes/2.2.0.txt +39 -0
- data/doc/release_notes/2.3.0.txt +37 -0
- data/doc/release_notes/2.4.0.txt +22 -0
- data/doc/remember.rdoc +40 -64
- data/doc/reset_password.rdoc +12 -9
- data/doc/session_expiration.rdoc +1 -0
- data/doc/single_session.rdoc +16 -25
- data/doc/sms_codes.rdoc +24 -14
- data/doc/two_factor_base.rdoc +60 -22
- data/doc/verify_account.rdoc +14 -12
- data/doc/verify_account_grace_period.rdoc +6 -2
- data/doc/verify_login_change.rdoc +9 -8
- data/doc/webauthn.rdoc +115 -0
- data/doc/webauthn_login.rdoc +15 -0
- data/doc/webauthn_verify_account.rdoc +9 -0
- data/javascript/webauthn_auth.js +45 -0
- data/javascript/webauthn_setup.js +35 -0
- data/lib/roda/plugins/rodauth.rb +1 -1
- data/lib/rodauth.rb +33 -28
- data/lib/rodauth/features/account_expiration.rb +5 -5
- data/lib/rodauth/features/active_sessions.rb +158 -0
- data/lib/rodauth/features/audit_logging.rb +98 -0
- data/lib/rodauth/features/base.rb +152 -49
- data/lib/rodauth/features/change_password_notify.rb +1 -1
- data/lib/rodauth/features/close_account.rb +8 -6
- data/lib/rodauth/features/confirm_password.rb +40 -2
- data/lib/rodauth/features/create_account.rb +8 -13
- data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
- data/lib/rodauth/features/disallow_password_reuse.rb +5 -3
- data/lib/rodauth/features/email_auth.rb +30 -28
- data/lib/rodauth/features/email_base.rb +3 -3
- data/lib/rodauth/features/http_basic_auth.rb +55 -35
- data/lib/rodauth/features/jwt.rb +63 -16
- data/lib/rodauth/features/jwt_cors.rb +15 -15
- data/lib/rodauth/features/jwt_refresh.rb +42 -13
- data/lib/rodauth/features/lockout.rb +11 -13
- data/lib/rodauth/features/login.rb +58 -13
- data/lib/rodauth/features/login_password_requirements_base.rb +13 -8
- data/lib/rodauth/features/otp.rb +76 -82
- data/lib/rodauth/features/password_complexity.rb +8 -13
- data/lib/rodauth/features/password_expiration.rb +1 -1
- data/lib/rodauth/features/password_grace_period.rb +17 -10
- data/lib/rodauth/features/password_pepper.rb +45 -0
- data/lib/rodauth/features/recovery_codes.rb +47 -51
- data/lib/rodauth/features/remember.rb +13 -27
- data/lib/rodauth/features/reset_password.rb +25 -25
- data/lib/rodauth/features/session_expiration.rb +7 -10
- data/lib/rodauth/features/single_session.rb +8 -6
- data/lib/rodauth/features/sms_codes.rb +58 -68
- data/lib/rodauth/features/two_factor_base.rb +134 -30
- data/lib/rodauth/features/verify_account.rb +28 -20
- data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
- data/lib/rodauth/features/verify_login_change.rb +11 -10
- data/lib/rodauth/features/webauthn.rb +505 -0
- data/lib/rodauth/features/webauthn_login.rb +70 -0
- data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
- data/lib/rodauth/migrations.rb +16 -5
- data/lib/rodauth/version.rb +2 -2
- data/templates/button.str +1 -3
- data/templates/change-login.str +1 -2
- data/templates/change-password.str +3 -5
- data/templates/close-account.str +2 -2
- data/templates/confirm-password.str +1 -1
- data/templates/create-account.str +1 -1
- data/templates/email-auth-request-form.str +1 -2
- data/templates/email-auth.str +1 -1
- data/templates/global-logout-field.str +6 -0
- data/templates/login-confirm-field.str +2 -4
- data/templates/login-display.str +3 -2
- data/templates/login-field.str +2 -4
- data/templates/login-form-footer.str +6 -0
- data/templates/login-form.str +7 -0
- data/templates/login.str +1 -9
- data/templates/logout.str +1 -1
- data/templates/multi-phase-login.str +3 -0
- data/templates/otp-auth-code-field.str +5 -3
- data/templates/otp-auth.str +1 -1
- data/templates/otp-disable.str +1 -1
- data/templates/otp-setup.str +3 -3
- data/templates/password-confirm-field.str +2 -4
- data/templates/password-field.str +2 -4
- data/templates/recovery-auth.str +3 -6
- data/templates/recovery-codes.str +1 -1
- data/templates/remember.str +15 -20
- data/templates/reset-password-request.str +2 -2
- data/templates/reset-password.str +1 -2
- data/templates/sms-auth.str +1 -1
- data/templates/sms-code-field.str +5 -3
- data/templates/sms-confirm.str +1 -2
- data/templates/sms-disable.str +1 -2
- data/templates/sms-request.str +1 -1
- data/templates/sms-setup.str +6 -4
- data/templates/two-factor-auth.str +5 -0
- data/templates/two-factor-disable.str +6 -0
- data/templates/two-factor-manage.str +16 -0
- data/templates/unlock-account-request.str +2 -2
- data/templates/unlock-account.str +1 -1
- data/templates/verify-account-resend.str +1 -1
- data/templates/verify-account.str +1 -2
- data/templates/verify-login-change.str +1 -1
- data/templates/webauthn-auth.str +11 -0
- data/templates/webauthn-remove.str +14 -0
- data/templates/webauthn-setup.str +12 -0
- metadata +96 -13
- data/doc/verify_change_login.rdoc +0 -11
- data/lib/rodauth/features/verify_change_login.rb +0 -20
|
@@ -4,7 +4,7 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:change_password_notify, :ChangePasswordNotify) do
|
|
5
5
|
depends :change_password, :email_base
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
translatable_method :password_changed_email_subject, 'Password Changed'
|
|
8
8
|
|
|
9
9
|
auth_value_methods(
|
|
10
10
|
:password_changed_email_body
|
|
@@ -33,7 +33,11 @@ module Rodauth
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
r.post do
|
|
36
|
-
|
|
36
|
+
catch_error do
|
|
37
|
+
if close_account_requires_password? && !password_match?(param(password_param))
|
|
38
|
+
throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
transaction do
|
|
38
42
|
before_close_account
|
|
39
43
|
close_account
|
|
@@ -46,12 +50,10 @@ module Rodauth
|
|
|
46
50
|
|
|
47
51
|
set_notice_flash close_account_notice_flash
|
|
48
52
|
redirect close_account_redirect
|
|
49
|
-
else
|
|
50
|
-
set_response_error_status(invalid_password_error_status)
|
|
51
|
-
set_field_error(password_param, invalid_password_message)
|
|
52
|
-
set_error_flash close_account_error_flash
|
|
53
|
-
close_account_view
|
|
54
53
|
end
|
|
54
|
+
|
|
55
|
+
set_error_flash close_account_error_flash
|
|
56
|
+
close_account_view
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
@@ -4,20 +4,26 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:confirm_password, :ConfirmPassword) do
|
|
5
5
|
notice_flash "Your password has been confirmed"
|
|
6
6
|
error_flash "There was an error confirming your password"
|
|
7
|
+
error_flash "You need to confirm your password before continuing", 'password_authentication_required'
|
|
7
8
|
loaded_templates %w'confirm-password password-field'
|
|
8
9
|
view 'confirm-password', 'Confirm Password'
|
|
9
10
|
additional_form_tags
|
|
10
11
|
button 'Confirm Password'
|
|
11
12
|
before
|
|
12
13
|
after
|
|
14
|
+
redirect(:password_authentication_required){confirm_password_path}
|
|
13
15
|
|
|
14
16
|
session_key :confirm_password_redirect_session_key, :confirm_password_redirect
|
|
17
|
+
translatable_method :confirm_password_link_text, "Enter Password"
|
|
18
|
+
auth_value_method :password_authentication_required_error_status, 401
|
|
19
|
+
|
|
15
20
|
auth_value_methods :confirm_password_redirect
|
|
16
21
|
|
|
17
22
|
auth_methods :confirm_password
|
|
18
23
|
|
|
19
24
|
route do |r|
|
|
20
|
-
|
|
25
|
+
require_login
|
|
26
|
+
require_account_session
|
|
21
27
|
before_confirm_password_route
|
|
22
28
|
|
|
23
29
|
request.get do
|
|
@@ -42,12 +48,44 @@ module Rodauth
|
|
|
42
48
|
end
|
|
43
49
|
end
|
|
44
50
|
|
|
51
|
+
def require_password_authentication
|
|
52
|
+
require_login
|
|
53
|
+
|
|
54
|
+
if require_password_authentication? && has_password?
|
|
55
|
+
set_redirect_error_status(password_authentication_required_error_status)
|
|
56
|
+
set_redirect_error_flash password_authentication_required_error_flash
|
|
57
|
+
set_session_value(confirm_password_redirect_session_key, request.fullpath)
|
|
58
|
+
redirect password_authentication_required_redirect
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
45
62
|
def confirm_password
|
|
63
|
+
authenticated_by.delete('autologin')
|
|
64
|
+
authenticated_by.delete('remember')
|
|
65
|
+
authenticated_by.delete('email_auth')
|
|
66
|
+
authenticated_by.delete('password')
|
|
67
|
+
authenticated_by.unshift("password")
|
|
68
|
+
remove_session_value(autologin_type_session_key)
|
|
46
69
|
nil
|
|
47
70
|
end
|
|
48
71
|
|
|
49
72
|
def confirm_password_redirect
|
|
50
|
-
|
|
73
|
+
remove_session_value(confirm_password_redirect_session_key) || default_redirect
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def _two_factor_auth_links
|
|
79
|
+
links = (super if defined?(super)) || []
|
|
80
|
+
if authenticated_by.length == 1 && !authenticated_by.include?('password') && has_password?
|
|
81
|
+
links << [5, confirm_password_path, confirm_password_link_text]
|
|
82
|
+
end
|
|
83
|
+
links
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def require_password_authentication?
|
|
87
|
+
return true if defined?(super) && super
|
|
88
|
+
!authenticated_by.include?('password')
|
|
51
89
|
end
|
|
52
90
|
end
|
|
53
91
|
end
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:create_account, :CreateAccount) do
|
|
5
|
-
depends :login_password_requirements_base
|
|
5
|
+
depends :login, :login_password_requirements_base
|
|
6
6
|
|
|
7
|
-
depends :login
|
|
8
7
|
notice_flash 'Your account has been created'
|
|
9
8
|
error_flash "There was an error creating your account"
|
|
10
9
|
loaded_templates %w'create-account login-field login-confirm-field password-field password-confirm-field'
|
|
@@ -16,10 +15,9 @@ module Rodauth
|
|
|
16
15
|
redirect
|
|
17
16
|
|
|
18
17
|
auth_value_method :create_account_autologin?, true
|
|
18
|
+
translatable_method :create_account_link_text, "Create a New Account"
|
|
19
19
|
auth_value_method :create_account_set_password?, true
|
|
20
20
|
|
|
21
|
-
auth_value_methods :create_account_link
|
|
22
|
-
|
|
23
21
|
auth_methods(
|
|
24
22
|
:save_account,
|
|
25
23
|
:set_new_account_password
|
|
@@ -32,6 +30,7 @@ module Rodauth
|
|
|
32
30
|
route do |r|
|
|
33
31
|
check_already_logged_in
|
|
34
32
|
before_create_account_route
|
|
33
|
+
@password_field_autocomplete_value = 'new-password'
|
|
35
34
|
|
|
36
35
|
r.get do
|
|
37
36
|
create_account_view
|
|
@@ -76,7 +75,7 @@ module Rodauth
|
|
|
76
75
|
end
|
|
77
76
|
after_create_account
|
|
78
77
|
if create_account_autologin?
|
|
79
|
-
|
|
78
|
+
autologin_session('create_account')
|
|
80
79
|
end
|
|
81
80
|
set_notice_flash create_account_notice_flash
|
|
82
81
|
redirect create_account_redirect
|
|
@@ -88,14 +87,6 @@ module Rodauth
|
|
|
88
87
|
end
|
|
89
88
|
end
|
|
90
89
|
|
|
91
|
-
def create_account_link
|
|
92
|
-
"<p><a href=\"#{create_account_path}\">Create a New Account</a></p>"
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def login_form_footer
|
|
96
|
-
super + create_account_link
|
|
97
|
-
end
|
|
98
|
-
|
|
99
90
|
def set_new_account_password(password)
|
|
100
91
|
account[account_password_hash_column] = password_hash(password)
|
|
101
92
|
end
|
|
@@ -121,6 +112,10 @@ module Rodauth
|
|
|
121
112
|
|
|
122
113
|
private
|
|
123
114
|
|
|
115
|
+
def _login_form_footer_links
|
|
116
|
+
super << [10, create_account_path, create_account_link_text]
|
|
117
|
+
end
|
|
118
|
+
|
|
124
119
|
def _new_account(login)
|
|
125
120
|
acc = {login_column=>login}
|
|
126
121
|
unless skip_status_checks?
|
|
@@ -5,7 +5,7 @@ module Rodauth
|
|
|
5
5
|
depends :login_password_requirements_base
|
|
6
6
|
|
|
7
7
|
auth_value_method :most_common_passwords_file, File.expand_path('../../../../dict/top-10_000-passwords.txt', __FILE__)
|
|
8
|
-
|
|
8
|
+
translatable_method :password_is_one_of_the_most_common_message, "is one of the most common passwords"
|
|
9
9
|
auth_value_method :most_common_passwords, nil
|
|
10
10
|
|
|
11
11
|
auth_methods :password_one_of_most_common?
|
|
@@ -4,7 +4,7 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:disallow_password_reuse, :DisallowPasswordReuse) do
|
|
5
5
|
depends :login_password_requirements_base
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
translatable_method :password_same_as_previous_password_message, "same as previous password"
|
|
8
8
|
auth_value_method :previous_password_account_id_column, :account_id
|
|
9
9
|
auth_value_method :previous_password_hash_column, :password_hash
|
|
10
10
|
auth_value_method :previous_password_hash_table, :account_previous_password_hashes
|
|
@@ -51,11 +51,13 @@ module Rodauth
|
|
|
51
51
|
return true if salts.empty?
|
|
52
52
|
|
|
53
53
|
salts.any? do |hash_id, salt|
|
|
54
|
-
|
|
54
|
+
database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt)
|
|
55
55
|
end
|
|
56
56
|
else
|
|
57
57
|
# :nocov:
|
|
58
|
-
previous_password_ds.select_map(previous_password_hash_column).any?
|
|
58
|
+
previous_password_ds.select_map(previous_password_hash_column).any? do |hash|
|
|
59
|
+
password_hash_match?(hash, password)
|
|
60
|
+
end
|
|
59
61
|
# :nocov:
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -4,8 +4,6 @@ module Rodauth
|
|
|
4
4
|
Feature.define(:email_auth, :EmailAuth) do
|
|
5
5
|
depends :login, :email_base
|
|
6
6
|
|
|
7
|
-
def_deprecated_alias :no_matching_email_auth_key_error_flash, :no_matching_email_auth_key_message
|
|
8
|
-
|
|
9
7
|
notice_flash "An email has been sent to you with a link to login to your account", 'email_auth_email_sent'
|
|
10
8
|
error_flash "There was an error logging you in"
|
|
11
9
|
error_flash "There was an error requesting an email link to authenticate", 'email_auth_request'
|
|
@@ -23,17 +21,16 @@ module Rodauth
|
|
|
23
21
|
redirect(:email_auth_email_recently_sent){default_post_email_redirect}
|
|
24
22
|
|
|
25
23
|
auth_value_method :email_auth_deadline_column, :deadline
|
|
26
|
-
auth_value_method :email_auth_deadline_interval, {:days=>1}
|
|
27
|
-
|
|
24
|
+
auth_value_method :email_auth_deadline_interval, {:days=>1}.freeze
|
|
25
|
+
translatable_method :email_auth_email_subject, 'Login Link'
|
|
28
26
|
auth_value_method :email_auth_id_column, :id
|
|
29
27
|
auth_value_method :email_auth_key_column, :key
|
|
30
28
|
auth_value_method :email_auth_key_param, 'key'
|
|
31
29
|
auth_value_method :email_auth_email_last_sent_column, :email_last_sent
|
|
32
30
|
auth_value_method :email_auth_skip_resend_email_within, 300
|
|
33
31
|
auth_value_method :email_auth_table, :account_email_auth_keys
|
|
32
|
+
auth_value_method :force_email_auth?, false
|
|
34
33
|
session_key :email_auth_session_key, :email_auth_key
|
|
35
|
-
|
|
36
|
-
auth_value_methods :force_email_auth?
|
|
37
34
|
|
|
38
35
|
auth_methods(
|
|
39
36
|
:create_email_auth_email,
|
|
@@ -74,7 +71,7 @@ module Rodauth
|
|
|
74
71
|
|
|
75
72
|
r.get do
|
|
76
73
|
if key = param_or_nil(email_auth_key_param)
|
|
77
|
-
|
|
74
|
+
set_session_value(email_auth_session_key, key)
|
|
78
75
|
redirect(r.path)
|
|
79
76
|
end
|
|
80
77
|
|
|
@@ -82,7 +79,7 @@ module Rodauth
|
|
|
82
79
|
if account_from_email_auth_key(key)
|
|
83
80
|
email_auth_view
|
|
84
81
|
else
|
|
85
|
-
|
|
82
|
+
remove_session_value(email_auth_session_key)
|
|
86
83
|
set_redirect_error_flash no_matching_email_auth_key_error_flash
|
|
87
84
|
redirect require_login_redirect
|
|
88
85
|
end
|
|
@@ -97,7 +94,7 @@ module Rodauth
|
|
|
97
94
|
redirect email_auth_email_sent_redirect
|
|
98
95
|
end
|
|
99
96
|
|
|
100
|
-
|
|
97
|
+
login('email_auth')
|
|
101
98
|
end
|
|
102
99
|
end
|
|
103
100
|
|
|
@@ -148,43 +145,44 @@ module Rodauth
|
|
|
148
145
|
ds.get(email_auth_key_column)
|
|
149
146
|
end
|
|
150
147
|
|
|
151
|
-
def login_form_footer
|
|
152
|
-
footer = super
|
|
153
|
-
footer += email_auth_request_form if valid_login_entered?
|
|
154
|
-
footer
|
|
155
|
-
end
|
|
156
|
-
|
|
157
148
|
def email_auth_request_form
|
|
158
149
|
render('email-auth-request-form')
|
|
159
150
|
end
|
|
160
151
|
|
|
161
152
|
def after_login_entered_during_multi_phase_login
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
redirect email_auth_email_sent_redirect
|
|
167
|
-
else
|
|
168
|
-
# If the account has a password hash, allow password login, but
|
|
169
|
-
# we will show form below to also login via email link.
|
|
170
|
-
super
|
|
171
|
-
end
|
|
153
|
+
# If forcing email auth, just send the email link.
|
|
154
|
+
_email_auth_request_and_redirect if force_email_auth?
|
|
155
|
+
|
|
156
|
+
super
|
|
172
157
|
end
|
|
173
158
|
|
|
174
159
|
def use_multi_phase_login?
|
|
175
160
|
true
|
|
176
161
|
end
|
|
177
162
|
|
|
178
|
-
def
|
|
179
|
-
|
|
163
|
+
def possible_authentication_methods
|
|
164
|
+
methods = super
|
|
165
|
+
methods << 'email_auth' if !methods.include?('password') && allow_email_auth?
|
|
166
|
+
methods
|
|
180
167
|
end
|
|
181
168
|
|
|
182
169
|
private
|
|
183
170
|
|
|
171
|
+
def _multi_phase_login_forms
|
|
172
|
+
forms = super
|
|
173
|
+
forms << [30, email_auth_request_form, :_email_auth_request_and_redirect] if valid_login_entered? && allow_email_auth?
|
|
174
|
+
forms
|
|
175
|
+
end
|
|
176
|
+
|
|
184
177
|
def email_auth_email_recently_sent?
|
|
185
178
|
(email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within)
|
|
186
179
|
end
|
|
187
180
|
|
|
181
|
+
def _email_auth_request_and_redirect
|
|
182
|
+
_email_auth_request
|
|
183
|
+
redirect email_auth_email_sent_redirect
|
|
184
|
+
end
|
|
185
|
+
|
|
188
186
|
def _email_auth_request
|
|
189
187
|
if email_auth_email_recently_sent?
|
|
190
188
|
set_redirect_error_flash email_auth_email_recently_sent_error_flash
|
|
@@ -204,6 +202,10 @@ module Rodauth
|
|
|
204
202
|
|
|
205
203
|
attr_reader :email_auth_key_value
|
|
206
204
|
|
|
205
|
+
def allow_email_auth?
|
|
206
|
+
defined?(super) ? super : true
|
|
207
|
+
end
|
|
208
|
+
|
|
207
209
|
def after_login
|
|
208
210
|
# Remove the email auth key after any login, even if
|
|
209
211
|
# it is a password login. This is done to invalidate
|
|
@@ -213,7 +215,7 @@ module Rodauth
|
|
|
213
215
|
# that allows login access to the account becomes a
|
|
214
216
|
# security liability, and it is best to remove it.
|
|
215
217
|
remove_email_auth_key
|
|
216
|
-
super
|
|
218
|
+
super
|
|
217
219
|
end
|
|
218
220
|
|
|
219
221
|
def after_close_account
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:email_base, :EmailBase) do
|
|
5
|
-
|
|
5
|
+
translatable_method :email_subject_prefix, ''
|
|
6
6
|
auth_value_method :require_mail?, true
|
|
7
7
|
auth_value_method :allow_raw_email_token?, false
|
|
8
8
|
|
|
@@ -43,7 +43,7 @@ module Rodauth
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def email_from
|
|
46
|
-
"webmaster@#{
|
|
46
|
+
"webmaster@#{domain}"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def email_to
|
|
@@ -51,7 +51,7 @@ module Rodauth
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def token_link(route, param, key)
|
|
54
|
-
|
|
54
|
+
route_url(route, param => "#{account_id}#{token_separator}#{convert_email_token_key(key)}")
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def convert_email_token_key(key)
|
|
@@ -3,54 +3,74 @@
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
Feature.define(:http_basic_auth, :HttpBasicAuth) do
|
|
5
5
|
auth_value_method :http_basic_auth_realm, "protected"
|
|
6
|
-
auth_value_method :require_http_basic_auth
|
|
6
|
+
auth_value_method :require_http_basic_auth?, false
|
|
7
7
|
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
-
sess = super
|
|
11
|
-
return sess if sess[session_key]
|
|
12
|
-
return sess unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1])
|
|
13
|
-
username, password = token.unpack("m*").first.split(/:/, 2)
|
|
8
|
+
def logged_in?
|
|
9
|
+
ret = super
|
|
14
10
|
|
|
15
|
-
if
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
throw_basic_auth_error(login_param, no_matching_login_message)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
before_login_attempt
|
|
22
|
-
|
|
23
|
-
unless open_account?
|
|
24
|
-
throw_basic_auth_error(login_param, no_matching_login_message)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
unless password_match?(password)
|
|
28
|
-
after_login_failure
|
|
29
|
-
throw_basic_auth_error(password_param, invalid_password_message)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
transaction do
|
|
33
|
-
before_login
|
|
34
|
-
sess[session_key] = account_session_value
|
|
35
|
-
after_login
|
|
36
|
-
end
|
|
37
|
-
end
|
|
11
|
+
if !ret && !defined?(@checked_http_basic_auth)
|
|
12
|
+
http_basic_auth
|
|
13
|
+
ret = super
|
|
38
14
|
end
|
|
39
15
|
|
|
40
|
-
|
|
16
|
+
ret
|
|
41
17
|
end
|
|
42
18
|
|
|
43
|
-
private
|
|
44
|
-
|
|
45
19
|
def require_login
|
|
46
|
-
if
|
|
20
|
+
if require_http_basic_auth?
|
|
21
|
+
require_http_basic_auth
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def require_http_basic_auth
|
|
28
|
+
unless http_basic_auth
|
|
47
29
|
set_http_basic_auth_error_response
|
|
48
30
|
request.halt
|
|
49
31
|
end
|
|
32
|
+
end
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
def http_basic_auth
|
|
35
|
+
return @checked_http_basic_auth if defined?(@checked_http_basic_auth)
|
|
36
|
+
|
|
37
|
+
@checked_http_basic_auth = nil
|
|
38
|
+
return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1])
|
|
39
|
+
|
|
40
|
+
username, password = token.unpack("m*").first.split(/:/, 2)
|
|
41
|
+
return unless username && password
|
|
42
|
+
|
|
43
|
+
catch_error do
|
|
44
|
+
unless account_from_login(username)
|
|
45
|
+
throw_basic_auth_error(login_param, no_matching_login_message)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
before_login_attempt
|
|
49
|
+
|
|
50
|
+
unless open_account?
|
|
51
|
+
throw_basic_auth_error(login_param, no_matching_login_message)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
unless password_match?(password)
|
|
55
|
+
after_login_failure
|
|
56
|
+
throw_basic_auth_error(password_param, invalid_password_message)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
transaction do
|
|
60
|
+
before_login
|
|
61
|
+
login_session('password')
|
|
62
|
+
after_login
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
@checked_http_basic_auth = true
|
|
66
|
+
return true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
nil
|
|
52
70
|
end
|
|
53
71
|
|
|
72
|
+
private
|
|
73
|
+
|
|
54
74
|
def set_http_basic_auth_error_response
|
|
55
75
|
response.status = 401
|
|
56
76
|
response.headers["WWW-Authenticate"] = "Basic realm=\"#{http_basic_auth_realm}\""
|