rodauth 2.20.0 → 2.23.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 +38 -442
- data/README.rdoc +15 -3
- data/doc/base.rdoc +1 -0
- data/doc/guides/internals.rdoc +11 -0
- data/doc/guides/paths.rdoc +3 -0
- data/doc/login_password_requirements_base.rdoc +1 -1
- data/doc/release_notes/2.21.0.txt +28 -0
- data/doc/release_notes/2.22.0.txt +43 -0
- data/doc/release_notes/2.23.0.txt +15 -0
- data/doc/reset_password.rdoc +16 -16
- data/doc/reset_password_notify.rdoc +17 -0
- data/lib/rodauth/features/active_sessions.rb +4 -2
- data/lib/rodauth/features/base.rb +25 -7
- data/lib/rodauth/features/change_password_notify.rb +2 -22
- data/lib/rodauth/features/email_auth.rb +1 -16
- data/lib/rodauth/features/http_basic_auth.rb +1 -1
- data/lib/rodauth/features/internal_request.rb +1 -1
- data/lib/rodauth/features/json.rb +2 -4
- data/lib/rodauth/features/jwt_cors.rb +1 -1
- data/lib/rodauth/features/lockout.rb +2 -18
- data/lib/rodauth/features/otp.rb +1 -1
- data/lib/rodauth/features/remember.rb +1 -1
- data/lib/rodauth/features/reset_password.rb +1 -16
- data/lib/rodauth/features/reset_password_notify.rb +16 -0
- data/lib/rodauth/features/sms_codes.rb +1 -1
- data/lib/rodauth/features/verify_account.rb +3 -20
- data/lib/rodauth/features/verify_account_grace_period.rb +13 -1
- data/lib/rodauth/version.rb +1 -1
- data/lib/rodauth.rb +27 -0
- data/templates/reset-password-notify-email.str +2 -0
- data/templates/webauthn-remove.str +1 -0
- metadata +13 -3
@@ -0,0 +1,15 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* The otp feature now uses the :use_path option when rendering QR
|
4
|
+
codes, resulting in significantly smaller svg images.
|
5
|
+
|
6
|
+
* Removing all multifactor authentication methods now removes the fact
|
7
|
+
that the session was authenticated via SMS, if the user used SMS as
|
8
|
+
an authentication method for the current session.
|
9
|
+
|
10
|
+
* The invalid domain check in the internal_request feature now works
|
11
|
+
correctly when using the rack master branch.
|
12
|
+
|
13
|
+
* The :httponly cookie option is no longer set automatically in the
|
14
|
+
remember feature if the :http_only cookie option was provided by the
|
15
|
+
user (rack recognizes both options).
|
data/doc/reset_password.rdoc
CHANGED
@@ -14,12 +14,12 @@ reset_password_autologin? :: Whether to autologin the user after successfully re
|
|
14
14
|
reset_password_button :: The text to use for the reset password button.
|
15
15
|
reset_password_deadline_column :: The column name in the +reset_password_table+ storing the deadline after which the token will be ignored.
|
16
16
|
reset_password_deadline_interval :: The amount of time for which to allow users to reset their passwords, 1 day by default. Only used if +set_deadline_values?+ is true.
|
17
|
-
reset_password_email_last_sent_column :: The email last sent column in the +reset_password_table+. Set to nil to always send a reset password email when requested.
|
18
|
-
reset_password_email_recently_sent_error_flash :: The flash error to show if not sending reset password email because one has been sent recently.
|
19
|
-
reset_password_email_recently_sent_redirect :: Where to redirect if not sending reset password email because one has been sent recently.
|
20
|
-
reset_password_email_sent_notice_flash :: The flash notice to show after a reset password email has been sent.
|
21
|
-
reset_password_email_sent_redirect :: Where to redirect after sending a reset password email.
|
22
|
-
reset_password_email_subject :: The subject to use for reset password
|
17
|
+
reset_password_email_last_sent_column :: The email last sent column in the +reset_password_table+. Set to nil to always send a reset password request email when requested.
|
18
|
+
reset_password_email_recently_sent_error_flash :: The flash error to show if not sending reset password request email because one has been sent recently.
|
19
|
+
reset_password_email_recently_sent_redirect :: Where to redirect if not sending reset password request email because one has been sent recently.
|
20
|
+
reset_password_email_sent_notice_flash :: The flash notice to show after a reset password request email has been sent.
|
21
|
+
reset_password_email_sent_redirect :: Where to redirect after sending a reset password request email.
|
22
|
+
reset_password_email_subject :: The subject to use for the reset password request email.
|
23
23
|
reset_password_error_flash :: The flash error to show after resetting a password.
|
24
24
|
reset_password_explanatory_text :: The text to display above the button to request a password reset.
|
25
25
|
reset_password_id_column :: The id column in the +reset_password_table+, should be a foreign key referencing the accounts table.
|
@@ -30,35 +30,35 @@ reset_password_page_title :: The page title to use on the reset password form.
|
|
30
30
|
reset_password_redirect :: Where to redirect after resetting a password.
|
31
31
|
reset_password_request_additional_form_tags :: HTML fragment containing additional form tags to use on the reset password request form.
|
32
32
|
reset_password_request_button :: The text to use for the reset password request button.
|
33
|
-
reset_password_request_error_flash :: The flash error to show if not able to send a reset password email.
|
33
|
+
reset_password_request_error_flash :: The flash error to show if not able to send a reset password request email.
|
34
34
|
reset_password_request_link_text :: The text to use for a link to the page to request a password reset.
|
35
35
|
reset_password_request_page_title :: The page title to use on the reset password request form.
|
36
36
|
reset_password_request_route :: The route to the reset password request action. Defaults to +reset-password-request+.
|
37
37
|
reset_password_route :: The route to the reset password action. Defaults to +reset-password+.
|
38
38
|
reset_password_session_key :: The key in the session to hold the reset password key temporarily.
|
39
|
-
reset_password_skip_resend_email_within :: The number of seconds before sending another reset password email, if +reset_password_email_last_sent_column+ is set.
|
39
|
+
reset_password_skip_resend_email_within :: The number of seconds before sending another reset password request email, if +reset_password_email_last_sent_column+ is set.
|
40
40
|
reset_password_table :: The name of the reset password keys table.
|
41
41
|
|
42
42
|
== Auth Methods
|
43
43
|
|
44
44
|
account_from_reset_password_key(key) :: Retrieve the account using the given reset password key, or return nil if no account matches.
|
45
45
|
after_reset_password :: Run arbitrary code after successfully resetting a password.
|
46
|
-
after_reset_password_request :: Run arbitrary code after sending the reset password email.
|
46
|
+
after_reset_password_request :: Run arbitrary code after sending the reset password request email.
|
47
47
|
before_reset_password :: Run arbitrary code before resetting a password.
|
48
|
-
before_reset_password_request :: Run arbitrary code before sending the reset password email.
|
48
|
+
before_reset_password_request :: Run arbitrary code before sending the reset password request email.
|
49
49
|
before_reset_password_request_route :: Run arbitrary code before handling a reset password request route.
|
50
50
|
before_reset_password_route :: Run arbitrary code before handling a reset password route.
|
51
|
-
create_reset_password_email :: A Mail::Message for the reset password email.
|
51
|
+
create_reset_password_email :: A Mail::Message for the reset password request email.
|
52
52
|
create_reset_password_key :: Add the reset password key data to the database.
|
53
|
-
get_reset_password_email_last_sent :: Get the last time a reset password email is sent, or nil if there is no last sent time.
|
53
|
+
get_reset_password_email_last_sent :: Get the last time a reset password request email is sent, or nil if there is no last sent time.
|
54
54
|
get_reset_password_key(id) :: Get the password reset key for the given account id from the database.
|
55
55
|
login_failed_reset_password_request_form :: The HTML to use for a form to request a password reset, shown on the login page after the user tries to login with an invalid password.
|
56
56
|
remove_reset_password_key :: Remove the reset password key for the current account, run after successful password reset.
|
57
|
-
reset_password_email_body :: The body to use for the reset password email.
|
58
|
-
reset_password_email_link :: The link to the reset password form in the reset password email.
|
57
|
+
reset_password_email_body :: The body to use for the reset password request email.
|
58
|
+
reset_password_email_link :: The link to the reset password form in the reset password request email.
|
59
59
|
reset_password_key_insert_hash :: The hash to insert into the +reset_password_table+.
|
60
60
|
reset_password_key_value :: The reset password key for the current account.
|
61
61
|
reset_password_request_view :: The HTML to use for the reset password request form.
|
62
62
|
reset_password_view :: The HTML to use for the reset password form.
|
63
|
-
send_reset_password_email :: Send the reset password email.
|
64
|
-
set_reset_password_email_last_sent :: Set the last time a reset password email is sent.
|
63
|
+
send_reset_password_email :: Send the reset password request email.
|
64
|
+
set_reset_password_email_last_sent :: Set the last time a reset password request email is sent.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
= Documentation for Reset Password Notify Feature
|
2
|
+
|
3
|
+
The reset password notify feature emails the user after the user has
|
4
|
+
reset their password. The user has already been sent a reset password
|
5
|
+
email by this point, so they know a password reset was requested, but
|
6
|
+
this feature allows for confirming that the password reset process
|
7
|
+
was completed. Depends on the reset_password feature.
|
8
|
+
|
9
|
+
== Auth Value Methods
|
10
|
+
|
11
|
+
reset_password_notify_email_subject :: The subject to use for the reset password notify email.
|
12
|
+
reset_password_notify_email_body :: The body to use for the reset password notify email.
|
13
|
+
|
14
|
+
== Auth Methods
|
15
|
+
|
16
|
+
create_reset_password_notify_email :: A Mail::Message for the reset password notify email.
|
17
|
+
send_reset_password_notify_email :: Send the reset password notify email.
|
@@ -13,7 +13,7 @@ module Rodauth
|
|
13
13
|
auth_value_method :active_sessions_last_use_column, :last_use
|
14
14
|
auth_value_method :active_sessions_session_id_column, :session_id
|
15
15
|
auth_value_method :active_sessions_table, :account_active_session_keys
|
16
|
-
translatable_method :global_logout_label, 'Logout all Logged In
|
16
|
+
translatable_method :global_logout_label, 'Logout all Logged In Sessions?'
|
17
17
|
auth_value_method :global_logout_param, 'global_logout'
|
18
18
|
auth_value_method :inactive_session_error_status, 401
|
19
19
|
auth_value_method :session_inactivity_deadline, 86400
|
@@ -81,7 +81,9 @@ module Rodauth
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def remove_current_session
|
84
|
-
|
84
|
+
if session_id = session[session_id_session_key]
|
85
|
+
active_sessions_ds.where(active_sessions_session_id_column=>compute_hmac(session_id)).delete
|
86
|
+
end
|
85
87
|
end
|
86
88
|
|
87
89
|
def remove_all_active_sessions
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require 'rack/request'
|
4
|
+
require 'rack/utils'
|
5
|
+
|
3
6
|
module Rodauth
|
4
7
|
Feature.define(:base, :Base) do
|
5
8
|
after 'login'
|
@@ -91,6 +94,7 @@ module Rodauth
|
|
91
94
|
:inputmode_for_field?,
|
92
95
|
:logged_in?,
|
93
96
|
:login_required,
|
97
|
+
:null_byte_parameter_value,
|
94
98
|
:open_account?,
|
95
99
|
:password_match?,
|
96
100
|
:random_key,
|
@@ -338,6 +342,11 @@ module Rodauth
|
|
338
342
|
require_login
|
339
343
|
end
|
340
344
|
|
345
|
+
def require_account
|
346
|
+
require_authentication
|
347
|
+
require_account_session
|
348
|
+
end
|
349
|
+
|
341
350
|
def account_initial_status_value
|
342
351
|
account_open_status_value
|
343
352
|
end
|
@@ -441,7 +450,16 @@ module Rodauth
|
|
441
450
|
# parameter with that name.
|
442
451
|
def param_or_nil(key)
|
443
452
|
value = raw_param(key)
|
444
|
-
|
453
|
+
unless value.nil?
|
454
|
+
value = value.to_s
|
455
|
+
value = null_byte_parameter_value(key, value) if value.include?("\0")
|
456
|
+
end
|
457
|
+
value
|
458
|
+
end
|
459
|
+
|
460
|
+
# Return nil by default for values with null bytes
|
461
|
+
def null_byte_parameter_value(key, value)
|
462
|
+
nil
|
445
463
|
end
|
446
464
|
|
447
465
|
def raw_param(key)
|
@@ -496,6 +514,11 @@ module Rodauth
|
|
496
514
|
request.redirect(path)
|
497
515
|
end
|
498
516
|
|
517
|
+
def return_response(body=nil)
|
518
|
+
response.write(body) if body
|
519
|
+
request.halt
|
520
|
+
end
|
521
|
+
|
499
522
|
def route_path(route, opts={})
|
500
523
|
path = "#{prefix}/#{route}"
|
501
524
|
path += "?#{Rack::Utils.build_nested_query(opts)}" unless opts.empty?
|
@@ -524,11 +547,6 @@ module Rodauth
|
|
524
547
|
Rack::Utils.secure_compare(provided.ljust(actual.length), actual) && provided.length == actual.length
|
525
548
|
end
|
526
549
|
|
527
|
-
def require_account
|
528
|
-
require_authentication
|
529
|
-
require_account_session
|
530
|
-
end
|
531
|
-
|
532
550
|
def require_account_session
|
533
551
|
unless account_from_session
|
534
552
|
clear_session
|
@@ -756,7 +774,7 @@ module Rodauth
|
|
756
774
|
num = ds.update(values)
|
757
775
|
if num == 1
|
758
776
|
values.each do |k, v|
|
759
|
-
|
777
|
+
hash[k] = Sequel::CURRENT_TIMESTAMP == v ? Time.now : v
|
760
778
|
end
|
761
779
|
end
|
762
780
|
num
|
@@ -3,31 +3,11 @@
|
|
3
3
|
module Rodauth
|
4
4
|
Feature.define(:change_password_notify, :ChangePasswordNotify) do
|
5
5
|
depends :change_password, :email_base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
auth_value_methods(
|
10
|
-
:password_changed_email_body
|
11
|
-
)
|
12
|
-
auth_methods(
|
13
|
-
:create_password_changed_email,
|
14
|
-
:send_password_changed_email
|
15
|
-
)
|
6
|
+
loaded_templates %w'password-changed-email'
|
7
|
+
email :password_changed, 'Password Changed', :translatable=>true
|
16
8
|
|
17
9
|
private
|
18
10
|
|
19
|
-
def send_password_changed_email
|
20
|
-
send_email(create_password_changed_email)
|
21
|
-
end
|
22
|
-
|
23
|
-
def create_password_changed_email
|
24
|
-
create_email(password_changed_email_subject, password_changed_email_body)
|
25
|
-
end
|
26
|
-
|
27
|
-
def password_changed_email_body
|
28
|
-
render('password-changed-email')
|
29
|
-
end
|
30
|
-
|
31
11
|
def after_change_password
|
32
12
|
super
|
33
13
|
send_password_changed_email
|
@@ -19,10 +19,10 @@ module Rodauth
|
|
19
19
|
button 'Send Login Link Via Email', 'email_auth_request'
|
20
20
|
redirect(:email_auth_email_sent){default_post_email_redirect}
|
21
21
|
redirect(:email_auth_email_recently_sent){default_post_email_redirect}
|
22
|
+
email :email_auth, 'Login Link'
|
22
23
|
|
23
24
|
auth_value_method :email_auth_deadline_column, :deadline
|
24
25
|
auth_value_method :email_auth_deadline_interval, {:days=>1}.freeze
|
25
|
-
translatable_method :email_auth_email_subject, 'Login Link'
|
26
26
|
auth_value_method :email_auth_id_column, :id
|
27
27
|
auth_value_method :email_auth_key_column, :key
|
28
28
|
auth_value_method :email_auth_key_param, 'key'
|
@@ -33,9 +33,7 @@ module Rodauth
|
|
33
33
|
session_key :email_auth_session_key, :email_auth_key
|
34
34
|
|
35
35
|
auth_methods(
|
36
|
-
:create_email_auth_email,
|
37
36
|
:create_email_auth_key,
|
38
|
-
:email_auth_email_body,
|
39
37
|
:email_auth_email_link,
|
40
38
|
:email_auth_key_insert_hash,
|
41
39
|
:email_auth_key_value,
|
@@ -43,7 +41,6 @@ module Rodauth
|
|
43
41
|
:get_email_auth_key,
|
44
42
|
:get_email_auth_email_last_sent,
|
45
43
|
:remove_email_auth_key,
|
46
|
-
:send_email_auth_email,
|
47
44
|
:set_email_auth_email_last_sent
|
48
45
|
)
|
49
46
|
|
@@ -137,10 +134,6 @@ module Rodauth
|
|
137
134
|
@account = _account_from_email_auth_key(key)
|
138
135
|
end
|
139
136
|
|
140
|
-
def send_email_auth_email
|
141
|
-
send_email(create_email_auth_email)
|
142
|
-
end
|
143
|
-
|
144
137
|
def email_auth_email_link
|
145
138
|
token_link(email_auth_route, email_auth_key_param, email_auth_key_value)
|
146
139
|
end
|
@@ -233,14 +226,6 @@ module Rodauth
|
|
233
226
|
@email_auth_key_value = random_key
|
234
227
|
end
|
235
228
|
|
236
|
-
def create_email_auth_email
|
237
|
-
create_email(email_auth_email_subject, email_auth_email_body)
|
238
|
-
end
|
239
|
-
|
240
|
-
def email_auth_email_body
|
241
|
-
render('email-auth-email')
|
242
|
-
end
|
243
|
-
|
244
229
|
def use_date_arithmetic?
|
245
230
|
super || db.database_type == :mysql
|
246
231
|
end
|
@@ -156,8 +156,7 @@ module Rodauth
|
|
156
156
|
end
|
157
157
|
elsif only_json?
|
158
158
|
response.status = json_response_error_status
|
159
|
-
|
160
|
-
request.halt
|
159
|
+
return_response non_json_request_error_message
|
161
160
|
end
|
162
161
|
|
163
162
|
super
|
@@ -175,8 +174,7 @@ module Rodauth
|
|
175
174
|
def _return_json_response
|
176
175
|
response.status ||= json_response_error_status if json_response[json_response_error_key]
|
177
176
|
response['Content-Type'] ||= json_response_content_type
|
178
|
-
|
179
|
-
request.halt
|
177
|
+
return_response _json_response_body(json_response)
|
180
178
|
end
|
181
179
|
|
182
180
|
def include_success_messages?
|
@@ -41,7 +41,7 @@ module Rodauth
|
|
41
41
|
response['Access-Control-Allow-Headers'] = jwt_cors_allow_headers
|
42
42
|
response['Access-Control-Max-Age'] = jwt_cors_max_age.to_s
|
43
43
|
response.status = 204
|
44
|
-
|
44
|
+
return_response
|
45
45
|
end
|
46
46
|
|
47
47
|
response['Access-Control-Expose-Headers'] = jwt_cors_expose_headers
|
@@ -25,6 +25,7 @@ module Rodauth
|
|
25
25
|
redirect :unlock_account
|
26
26
|
redirect(:unlock_account_request){default_post_email_redirect}
|
27
27
|
redirect(:unlock_account_email_recently_sent){default_post_email_redirect}
|
28
|
+
email :unlock_account, 'Unlock Account'
|
28
29
|
|
29
30
|
auth_value_method :unlock_account_autologin?, true
|
30
31
|
auth_value_method :max_invalid_logins, 100
|
@@ -37,7 +38,6 @@ module Rodauth
|
|
37
38
|
auth_value_method :account_lockouts_email_last_sent_column, :email_last_sent
|
38
39
|
auth_value_method :account_lockouts_deadline_column, :deadline
|
39
40
|
auth_value_method :account_lockouts_deadline_interval, {:days=>1}.freeze
|
40
|
-
translatable_method :unlock_account_email_subject, 'Unlock Account'
|
41
41
|
translatable_method :unlock_account_explanatory_text, '<p>This account is currently locked out. You can unlock the account:</p>'
|
42
42
|
translatable_method :unlock_account_request_explanatory_text, '<p>This account is currently locked out. You can request that the account be unlocked:</p>'
|
43
43
|
auth_value_method :unlock_account_key_param, 'key'
|
@@ -47,15 +47,12 @@ module Rodauth
|
|
47
47
|
|
48
48
|
auth_methods(
|
49
49
|
:clear_invalid_login_attempts,
|
50
|
-
:create_unlock_account_email,
|
51
50
|
:generate_unlock_account_key,
|
52
51
|
:get_unlock_account_key,
|
53
52
|
:get_unlock_account_email_last_sent,
|
54
53
|
:invalid_login_attempted,
|
55
54
|
:locked_out?,
|
56
|
-
:send_unlock_account_email,
|
57
55
|
:set_unlock_account_email_last_sent,
|
58
|
-
:unlock_account_email_body,
|
59
56
|
:unlock_account_email_link,
|
60
57
|
:unlock_account,
|
61
58
|
:unlock_account_key
|
@@ -226,10 +223,6 @@ module Rodauth
|
|
226
223
|
@account = _account_from_unlock_key(key)
|
227
224
|
end
|
228
225
|
|
229
|
-
def send_unlock_account_email
|
230
|
-
send_email(create_unlock_account_email)
|
231
|
-
end
|
232
|
-
|
233
226
|
def unlock_account_email_link
|
234
227
|
token_link(unlock_account_route, unlock_account_key_param, unlock_account_key_value)
|
235
228
|
end
|
@@ -284,16 +277,7 @@ module Rodauth
|
|
284
277
|
def show_lockout_page
|
285
278
|
set_response_error_reason_status(:account_locked_out, lockout_error_status)
|
286
279
|
set_error_flash login_lockout_error_flash
|
287
|
-
|
288
|
-
request.halt
|
289
|
-
end
|
290
|
-
|
291
|
-
def create_unlock_account_email
|
292
|
-
create_email(unlock_account_email_subject, unlock_account_email_body)
|
293
|
-
end
|
294
|
-
|
295
|
-
def unlock_account_email_body
|
296
|
-
render('unlock-account-email')
|
280
|
+
return_response unlock_account_request_view
|
297
281
|
end
|
298
282
|
|
299
283
|
def unlock_account_email_recently_sent?
|
data/lib/rodauth/features/otp.rb
CHANGED
@@ -303,7 +303,7 @@ module Rodauth
|
|
303
303
|
end
|
304
304
|
|
305
305
|
def otp_qr_code
|
306
|
-
RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true)
|
306
|
+
RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true)
|
307
307
|
end
|
308
308
|
|
309
309
|
def otp_user_key
|
@@ -144,7 +144,7 @@ module Rodauth
|
|
144
144
|
opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}"
|
145
145
|
opts[:expires] = convert_timestamp(active_remember_key_ds.get(remember_deadline_column))
|
146
146
|
opts[:path] = "/" unless opts.key?(:path)
|
147
|
-
opts[:httponly] = true unless opts.key?(:httponly)
|
147
|
+
opts[:httponly] = true unless opts.key?(:httponly) || opts.key?(:http_only)
|
148
148
|
opts[:secure] = true unless opts.key?(:secure) || !request.ssl?
|
149
149
|
::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts)
|
150
150
|
end
|
@@ -24,10 +24,10 @@ module Rodauth
|
|
24
24
|
redirect
|
25
25
|
redirect(:reset_password_email_sent){default_post_email_redirect}
|
26
26
|
redirect(:reset_password_email_recently_sent){default_post_email_redirect}
|
27
|
+
email :reset_password, 'Reset Password'
|
27
28
|
|
28
29
|
auth_value_method :reset_password_deadline_column, :deadline
|
29
30
|
auth_value_method :reset_password_deadline_interval, {:days=>1}.freeze
|
30
|
-
translatable_method :reset_password_email_subject, 'Reset Password'
|
31
31
|
auth_value_method :reset_password_key_param, 'key'
|
32
32
|
auth_value_method :reset_password_autologin?, false
|
33
33
|
auth_value_method :reset_password_table, :account_password_reset_keys
|
@@ -41,16 +41,13 @@ module Rodauth
|
|
41
41
|
|
42
42
|
auth_methods(
|
43
43
|
:create_reset_password_key,
|
44
|
-
:create_reset_password_email,
|
45
44
|
:get_reset_password_key,
|
46
45
|
:get_reset_password_email_last_sent,
|
47
46
|
:login_failed_reset_password_request_form,
|
48
47
|
:remove_reset_password_key,
|
49
|
-
:reset_password_email_body,
|
50
48
|
:reset_password_email_link,
|
51
49
|
:reset_password_key_insert_hash,
|
52
50
|
:reset_password_key_value,
|
53
|
-
:send_reset_password_email,
|
54
51
|
:set_reset_password_email_last_sent
|
55
52
|
)
|
56
53
|
auth_private_methods(
|
@@ -187,10 +184,6 @@ module Rodauth
|
|
187
184
|
@account = _account_from_reset_password_key(key)
|
188
185
|
end
|
189
186
|
|
190
|
-
def send_reset_password_email
|
191
|
-
send_email(create_reset_password_email)
|
192
|
-
end
|
193
|
-
|
194
187
|
def reset_password_email_link
|
195
188
|
token_link(reset_password_route, reset_password_key_param, reset_password_key_value)
|
196
189
|
end
|
@@ -241,18 +234,10 @@ module Rodauth
|
|
241
234
|
@reset_password_key_value = random_key
|
242
235
|
end
|
243
236
|
|
244
|
-
def create_reset_password_email
|
245
|
-
create_email(reset_password_email_subject, reset_password_email_body)
|
246
|
-
end
|
247
|
-
|
248
237
|
def login_failed_reset_password_request_form
|
249
238
|
render("reset-password-request")
|
250
239
|
end
|
251
240
|
|
252
|
-
def reset_password_email_body
|
253
|
-
render('reset-password-email')
|
254
|
-
end
|
255
|
-
|
256
241
|
def use_date_arithmetic?
|
257
242
|
super || db.database_type == :mysql
|
258
243
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Rodauth
|
4
|
+
Feature.define(:reset_password_notify, :ResetPasswordNotify) do
|
5
|
+
depends :reset_password
|
6
|
+
loaded_templates %w'reset-password-notify-email'
|
7
|
+
email :reset_password_notify, 'Password Reset Completed', :translatable=>true
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def after_reset_password
|
12
|
+
super
|
13
|
+
send_reset_password_notify_email
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -26,8 +26,8 @@ module Rodauth
|
|
26
26
|
redirect
|
27
27
|
redirect(:verify_account_email_sent){default_post_email_redirect}
|
28
28
|
redirect(:verify_account_email_recently_sent){default_post_email_redirect}
|
29
|
+
email :verify_account, 'Verify Account'
|
29
30
|
|
30
|
-
translatable_method :verify_account_email_subject, 'Verify Account'
|
31
31
|
auth_value_method :verify_account_key_param, 'key'
|
32
32
|
auth_value_method :verify_account_autologin?, true
|
33
33
|
auth_value_method :verify_account_table, :account_verification_keys
|
@@ -43,14 +43,11 @@ module Rodauth
|
|
43
43
|
auth_methods(
|
44
44
|
:allow_resending_verify_account_email?,
|
45
45
|
:create_verify_account_key,
|
46
|
-
:create_verify_account_email,
|
47
46
|
:get_verify_account_key,
|
48
47
|
:get_verify_account_email_last_sent,
|
49
48
|
:remove_verify_account_key,
|
50
|
-
:send_verify_account_email,
|
51
49
|
:set_verify_account_email_last_sent,
|
52
50
|
:verify_account,
|
53
|
-
:verify_account_email_body,
|
54
51
|
:verify_account_email_link,
|
55
52
|
:verify_account_key_insert_hash,
|
56
53
|
:verify_account_key_value
|
@@ -198,8 +195,7 @@ module Rodauth
|
|
198
195
|
if account_from_login(login) && allow_resending_verify_account_email?
|
199
196
|
set_response_error_reason_status(:already_an_unverified_account_with_this_login, unopen_account_error_status)
|
200
197
|
set_error_flash attempt_to_create_unverified_account_error_flash
|
201
|
-
|
202
|
-
request.halt
|
198
|
+
return_response resend_verify_account_view
|
203
199
|
end
|
204
200
|
super
|
205
201
|
end
|
@@ -212,10 +208,6 @@ module Rodauth
|
|
212
208
|
account_unverified_status_value
|
213
209
|
end
|
214
210
|
|
215
|
-
def send_verify_account_email
|
216
|
-
send_email(create_verify_account_email)
|
217
|
-
end
|
218
|
-
|
219
211
|
def verify_account_email_link
|
220
212
|
token_link(verify_account_route, verify_account_key_param, verify_account_key_value)
|
221
213
|
end
|
@@ -275,8 +267,7 @@ module Rodauth
|
|
275
267
|
unless open_account?
|
276
268
|
set_response_error_reason_status(:unverified_account, unopen_account_error_status)
|
277
269
|
set_error_flash attempt_to_login_to_unverified_account_error_flash
|
278
|
-
|
279
|
-
request.halt
|
270
|
+
return_response resend_verify_account_view
|
280
271
|
end
|
281
272
|
super
|
282
273
|
end
|
@@ -311,14 +302,6 @@ module Rodauth
|
|
311
302
|
{verify_account_id_column=>account_id, verify_account_key_column=>verify_account_key_value}
|
312
303
|
end
|
313
304
|
|
314
|
-
def create_verify_account_email
|
315
|
-
create_email(verify_account_email_subject, verify_account_email_body)
|
316
|
-
end
|
317
|
-
|
318
|
-
def verify_account_email_body
|
319
|
-
render('verify-account-email')
|
320
|
-
end
|
321
|
-
|
322
305
|
def verify_account_ds(id=account_id)
|
323
306
|
db[verify_account_table].where(verify_account_id_column=>id)
|
324
307
|
end
|
@@ -30,10 +30,17 @@ module Rodauth
|
|
30
30
|
false
|
31
31
|
end
|
32
32
|
|
33
|
+
def require_login
|
34
|
+
if unverified_grace_period_expired?
|
35
|
+
clear_session
|
36
|
+
end
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
33
40
|
def update_session
|
34
41
|
super
|
35
42
|
if account_in_unverified_grace_period?
|
36
|
-
set_session_value(unverified_account_session_key,
|
43
|
+
set_session_value(unverified_account_session_key, Time.now.to_i + verify_account_grace_period)
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
@@ -78,6 +85,11 @@ module Rodauth
|
|
78
85
|
!verify_account_ds.where(Sequel.date_add(verification_requested_at_column, :seconds=>verify_account_grace_period) > Sequel::CURRENT_TIMESTAMP).empty?
|
79
86
|
end
|
80
87
|
|
88
|
+
def unverified_grace_period_expired?
|
89
|
+
return false unless expires_at = session[unverified_account_session_key]
|
90
|
+
expires_at.is_a?(Integer) && Time.now.to_i > expires_at
|
91
|
+
end
|
92
|
+
|
81
93
|
def use_date_arithmetic?
|
82
94
|
true
|
83
95
|
end
|
data/lib/rodauth/version.rb
CHANGED
data/lib/rodauth.rb
CHANGED
@@ -233,6 +233,33 @@ module Rodauth
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
|
236
|
+
def email(type, subject, opts = {})
|
237
|
+
subject_method = :"#{type}_email_subject"
|
238
|
+
body_method = :"#{type}_email_body"
|
239
|
+
create_method = :"create_#{type}_email"
|
240
|
+
send_method = :"send_#{type}_email"
|
241
|
+
|
242
|
+
translatable_method subject_method, subject
|
243
|
+
auth_methods create_method, send_method
|
244
|
+
|
245
|
+
body_template = "#{type.to_s.tr('_', '-')}-email"
|
246
|
+
if opts[:translatable]
|
247
|
+
auth_value_methods body_method
|
248
|
+
define_method(body_method){translate(body_method, render(body_template))}
|
249
|
+
else
|
250
|
+
auth_methods body_method
|
251
|
+
define_method(body_method){render(body_template)}
|
252
|
+
end
|
253
|
+
|
254
|
+
define_method(create_method) do
|
255
|
+
create_email(send(subject_method), send(body_method))
|
256
|
+
end
|
257
|
+
|
258
|
+
define_method(send_method) do
|
259
|
+
send_email(send(create_method))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
236
263
|
def additional_form_tags(name=feature_name)
|
237
264
|
auth_value_method(:"#{name}_additional_form_tags", nil)
|
238
265
|
end
|
@@ -4,6 +4,7 @@
|
|
4
4
|
#{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
|
5
5
|
<fieldset class="form-group mb-3">
|
6
6
|
#{(usage = rodauth.account_webauthn_usage; last_id = usage.keys.last; usage;).map do |id, last_use|
|
7
|
+
last_use = last_use.strftime("%F %T") if last_use.is_a?(Time)
|
7
8
|
input = rodauth.input_field_string(rodauth.webauthn_remove_param, "webauthn-remove-#{h id}", :type=>'radio', :class=>"form-check-input", :skip_error_message=>true, :value=>id, :required=>false)
|
8
9
|
label = "<label class=\"rodauth-webauthn-id form-check-label\" for=\"webauthn-remove-#{h id}\">Last Use: #{last_use}</label>"
|
9
10
|
error = rodauth.formatted_field_error(rodauth.webauthn_remove_param) if id == last_id
|