rodauth 0.10.0 → 1.0.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 +146 -0
- data/README.rdoc +644 -220
- data/Rakefile +99 -11
- data/doc/account_expiration.rdoc +55 -0
- data/doc/base.rdoc +104 -0
- data/doc/change_login.rdoc +29 -0
- data/doc/change_password.rdoc +26 -0
- data/doc/close_account.rdoc +31 -0
- data/doc/confirm_password.rdoc +22 -0
- data/doc/create_account.rdoc +34 -0
- data/doc/disallow_password_reuse.rdoc +37 -0
- data/doc/email_base.rdoc +19 -0
- data/doc/jwt.rdoc +35 -0
- data/doc/lockout.rdoc +83 -0
- data/doc/login.rdoc +27 -0
- data/doc/login_password_requirements_base.rdoc +50 -0
- data/doc/logout.rdoc +21 -0
- data/doc/otp.rdoc +100 -0
- data/doc/password_complexity.rdoc +50 -0
- data/doc/password_expiration.rdoc +52 -0
- data/doc/password_grace_period.rdoc +10 -0
- data/doc/recovery_codes.rdoc +60 -0
- data/doc/release_notes/1.0.0.txt +443 -0
- data/doc/remember.rdoc +82 -0
- data/doc/reset_password.rdoc +70 -0
- data/doc/session_expiration.rdoc +27 -0
- data/doc/single_session.rdoc +43 -0
- data/doc/sms_codes.rdoc +119 -0
- data/doc/two_factor_base.rdoc +27 -0
- data/doc/verify_account.rdoc +70 -0
- data/doc/verify_account_grace_period.rdoc +15 -0
- data/doc/verify_change_login.rdoc +9 -0
- data/lib/roda/plugins/rodauth.rb +3 -262
- data/lib/rodauth.rb +260 -0
- data/lib/rodauth/features/account_expiration.rb +108 -0
- data/lib/rodauth/features/base.rb +479 -0
- data/lib/rodauth/features/change_login.rb +77 -0
- data/lib/rodauth/features/change_password.rb +66 -0
- data/lib/rodauth/features/close_account.rb +82 -0
- data/lib/rodauth/features/confirm_password.rb +51 -0
- data/lib/rodauth/features/create_account.rb +128 -0
- data/lib/rodauth/features/disallow_password_reuse.rb +82 -0
- data/lib/rodauth/features/email_base.rb +63 -0
- data/lib/rodauth/features/jwt.rb +151 -0
- data/lib/rodauth/features/lockout.rb +262 -0
- data/lib/rodauth/features/login.rb +61 -0
- data/lib/rodauth/features/login_password_requirements_base.rb +123 -0
- data/lib/rodauth/features/logout.rb +37 -0
- data/lib/rodauth/features/otp.rb +338 -0
- data/lib/rodauth/features/password_complexity.rb +89 -0
- data/lib/rodauth/features/password_expiration.rb +111 -0
- data/lib/rodauth/features/password_grace_period.rb +46 -0
- data/lib/rodauth/features/recovery_codes.rb +240 -0
- data/lib/rodauth/features/remember.rb +200 -0
- data/lib/rodauth/features/reset_password.rb +207 -0
- data/lib/rodauth/features/session_expiration.rb +55 -0
- data/lib/rodauth/features/single_session.rb +87 -0
- data/lib/rodauth/features/sms_codes.rb +498 -0
- data/lib/rodauth/features/two_factor_base.rb +135 -0
- data/lib/rodauth/features/verify_account.rb +232 -0
- data/lib/rodauth/features/verify_account_grace_period.rb +76 -0
- data/lib/rodauth/features/verify_change_login.rb +20 -0
- data/lib/rodauth/migrations.rb +130 -0
- data/lib/rodauth/version.rb +9 -0
- data/spec/account_expiration_spec.rb +90 -0
- data/spec/all.rb +1 -0
- data/spec/change_login_spec.rb +149 -0
- data/spec/change_password_spec.rb +177 -0
- data/spec/close_account_spec.rb +162 -0
- data/spec/confirm_password_spec.rb +70 -0
- data/spec/create_account_spec.rb +127 -0
- data/spec/disallow_password_reuse_spec.rb +84 -0
- data/spec/lockout_spec.rb +228 -0
- data/spec/login_spec.rb +188 -0
- data/spec/migrate/001_tables.rb +103 -16
- data/spec/migrate/002_account_password_hash_column.rb +11 -0
- data/spec/migrate_password/001_tables.rb +60 -42
- data/spec/migrate_travis/001_tables.rb +116 -0
- data/spec/password_complexity_spec.rb +108 -0
- data/spec/password_expiration_spec.rb +243 -0
- data/spec/password_grace_period_spec.rb +93 -0
- data/spec/remember_spec.rb +424 -0
- data/spec/reset_password_spec.rb +185 -0
- data/spec/rodauth_spec.rb +57 -980
- data/spec/session_expiration_spec.rb +58 -0
- data/spec/single_session_spec.rb +107 -0
- data/spec/spec_helper.rb +202 -0
- data/spec/two_factor_spec.rb +1310 -0
- data/spec/verify_account_grace_period_spec.rb +135 -0
- data/spec/verify_account_spec.rb +142 -0
- data/spec/verify_change_login_spec.rb +46 -0
- data/spec/views/login.str +2 -2
- data/templates/add-recovery-codes.str +2 -0
- data/templates/button.str +5 -0
- data/templates/change-login.str +5 -18
- data/templates/change-password.str +6 -14
- data/templates/close-account.str +3 -6
- data/templates/confirm-password.str +4 -14
- data/templates/create-account.str +6 -30
- data/templates/login-confirm-field.str +6 -0
- data/templates/login-field.str +6 -0
- data/templates/login.str +5 -19
- data/templates/logout.str +2 -6
- data/templates/otp-auth-code-field.str +6 -0
- data/templates/otp-auth.str +8 -0
- data/templates/otp-disable.str +6 -0
- data/templates/otp-setup.str +21 -0
- data/templates/password-confirm-field.str +6 -0
- data/templates/password-field.str +6 -0
- data/templates/recovery-auth.str +12 -0
- data/templates/recovery-codes.str +6 -0
- data/templates/remember.str +8 -12
- data/templates/reset-password-request.str +2 -2
- data/templates/reset-password.str +4 -18
- data/templates/sms-auth.str +6 -0
- data/templates/sms-code-field.str +6 -0
- data/templates/sms-confirm.str +7 -0
- data/templates/sms-disable.str +7 -0
- data/templates/sms-request.str +5 -0
- data/templates/sms-setup.str +12 -0
- data/templates/unlock-account-request.str +3 -7
- data/templates/unlock-account.str +4 -7
- data/templates/verify-account-resend.str +2 -2
- data/templates/verify-account.str +2 -6
- metadata +191 -29
- data/lib/roda/plugins/rodauth/base.rb +0 -428
- data/lib/roda/plugins/rodauth/change_login.rb +0 -48
- data/lib/roda/plugins/rodauth/change_password.rb +0 -42
- data/lib/roda/plugins/rodauth/close_account.rb +0 -42
- data/lib/roda/plugins/rodauth/create_account.rb +0 -92
- data/lib/roda/plugins/rodauth/lockout.rb +0 -292
- data/lib/roda/plugins/rodauth/login.rb +0 -81
- data/lib/roda/plugins/rodauth/logout.rb +0 -36
- data/lib/roda/plugins/rodauth/remember.rb +0 -226
- data/lib/roda/plugins/rodauth/reset_password.rb +0 -205
- data/lib/roda/plugins/rodauth/verify_account.rb +0 -228
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
Remember = Feature.define(:remember) do
|
|
5
|
+
depends :confirm_password
|
|
6
|
+
|
|
7
|
+
notice_flash "Your remember setting has been updated"
|
|
8
|
+
error_flash "There was an error updating your remember setting"
|
|
9
|
+
view 'remember', 'Change Remember Setting'
|
|
10
|
+
additional_form_tags
|
|
11
|
+
button 'Change Remember Setting'
|
|
12
|
+
before
|
|
13
|
+
before 'load_memory'
|
|
14
|
+
after
|
|
15
|
+
after 'load_memory'
|
|
16
|
+
redirect
|
|
17
|
+
|
|
18
|
+
auth_value_method :remember_cookie_options, {}
|
|
19
|
+
auth_value_method :extend_remember_deadline?, false
|
|
20
|
+
auth_value_method :remember_period, {:days=>14}
|
|
21
|
+
auth_value_method :remembered_session_key, :remembered
|
|
22
|
+
auth_value_method :remember_deadline_interval, {:days=>14}
|
|
23
|
+
auth_value_method :remember_id_column, :id
|
|
24
|
+
auth_value_method :remember_key_column, :key
|
|
25
|
+
auth_value_method :remember_deadline_column, :deadline
|
|
26
|
+
auth_value_method :remember_table, :account_remember_keys
|
|
27
|
+
auth_value_method :remember_cookie_key, '_remember'
|
|
28
|
+
auth_value_method :remember_param, 'remember'
|
|
29
|
+
auth_value_method :remember_remember_param_value, 'remember'
|
|
30
|
+
auth_value_method :remember_forget_param_value, 'forget'
|
|
31
|
+
auth_value_method :remember_disable_param_value, 'disable'
|
|
32
|
+
auth_value_method :remember_remember_label, 'Remember Me'
|
|
33
|
+
auth_value_method :remember_forget_label, 'Forget Me'
|
|
34
|
+
auth_value_method :remember_disable_label, 'Disable Remember Me'
|
|
35
|
+
|
|
36
|
+
auth_methods(
|
|
37
|
+
:add_remember_key,
|
|
38
|
+
:clear_remembered_session_key,
|
|
39
|
+
:disable_remember_login,
|
|
40
|
+
:forget_login,
|
|
41
|
+
:generate_remember_key_value,
|
|
42
|
+
:get_remember_key,
|
|
43
|
+
:load_memory,
|
|
44
|
+
:logged_in_via_remember_key?,
|
|
45
|
+
:remember_key_value,
|
|
46
|
+
:remember_login,
|
|
47
|
+
:remove_remember_key
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
route do |r|
|
|
51
|
+
require_account
|
|
52
|
+
before_remember_route
|
|
53
|
+
|
|
54
|
+
r.get do
|
|
55
|
+
remember_view
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
r.post do
|
|
59
|
+
remember = param(remember_param)
|
|
60
|
+
if [remember_remember_param_value, remember_forget_param_value, remember_disable_param_value].include?(remember)
|
|
61
|
+
transaction do
|
|
62
|
+
before_remember
|
|
63
|
+
case remember
|
|
64
|
+
when remember_remember_param_value
|
|
65
|
+
remember_login
|
|
66
|
+
when remember_forget_param_value
|
|
67
|
+
forget_login
|
|
68
|
+
when remember_disable_param_value
|
|
69
|
+
disable_remember_login
|
|
70
|
+
end
|
|
71
|
+
after_remember
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
set_notice_flash remember_notice_flash
|
|
75
|
+
redirect remember_redirect
|
|
76
|
+
else
|
|
77
|
+
set_error_flash remember_error_flash
|
|
78
|
+
remember_view
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def load_memory
|
|
84
|
+
return if session[session_key]
|
|
85
|
+
return unless cookie = request.cookies[remember_cookie_key]
|
|
86
|
+
id, key = cookie.split('_', 2)
|
|
87
|
+
return unless id && key
|
|
88
|
+
|
|
89
|
+
unless (actual = active_remember_key_ds(id).get(remember_key_column)) && timing_safe_eql?(key, actual)
|
|
90
|
+
forget_login
|
|
91
|
+
return
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
session[session_key] = id
|
|
95
|
+
account = account_from_session
|
|
96
|
+
session.delete(session_key)
|
|
97
|
+
|
|
98
|
+
unless account
|
|
99
|
+
remove_remember_key(id)
|
|
100
|
+
forget_login
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
before_load_memory
|
|
105
|
+
update_session
|
|
106
|
+
|
|
107
|
+
set_session_value(remembered_session_key, true)
|
|
108
|
+
if extend_remember_deadline?
|
|
109
|
+
active_remember_key_ds(id).update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period))
|
|
110
|
+
remember_login
|
|
111
|
+
end
|
|
112
|
+
after_load_memory
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def remember_login
|
|
116
|
+
get_remember_key
|
|
117
|
+
opts = Hash[remember_cookie_options]
|
|
118
|
+
opts[:value] = "#{account_id}_#{remember_key_value}"
|
|
119
|
+
opts[:expires] = convert_timestamp(active_remember_key_ds.get(remember_deadline_column))
|
|
120
|
+
::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def forget_login
|
|
124
|
+
::Rack::Utils.delete_cookie_header!(response.headers, remember_cookie_key, remember_cookie_options)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def get_remember_key
|
|
128
|
+
unless @remember_key_value = active_remember_key_ds.get(remember_key_column)
|
|
129
|
+
generate_remember_key_value
|
|
130
|
+
transaction do
|
|
131
|
+
remove_remember_key
|
|
132
|
+
add_remember_key
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def disable_remember_login
|
|
139
|
+
remove_remember_key
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def add_remember_key
|
|
143
|
+
hash = {remember_id_column=>account_id, remember_key_column=>remember_key_value}
|
|
144
|
+
set_deadline_value(hash, remember_deadline_column, remember_deadline_interval)
|
|
145
|
+
|
|
146
|
+
if e = raised_uniqueness_violation{remember_key_ds.insert(hash)}
|
|
147
|
+
# If inserting into the remember key table causes a violation, we can pull the
|
|
148
|
+
# existing row from the table. If there is no invalid row, we can then reraise.
|
|
149
|
+
raise e unless @remember_key_value = active_remember_key_ds.get(remember_key_column)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def remove_remember_key(id=account_id)
|
|
154
|
+
remember_key_ds(id).delete
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def clear_remembered_session_key
|
|
158
|
+
session.delete(remembered_session_key)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def logged_in_via_remember_key?
|
|
162
|
+
!!session[remembered_session_key]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def after_logout
|
|
168
|
+
forget_login
|
|
169
|
+
super if defined?(super)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def after_close_account
|
|
173
|
+
remove_remember_key
|
|
174
|
+
super if defined?(super)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def after_confirm_password
|
|
178
|
+
super
|
|
179
|
+
clear_remembered_session_key
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
attr_reader :remember_key_value
|
|
183
|
+
|
|
184
|
+
def generate_remember_key_value
|
|
185
|
+
@remember_key_value = random_key
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def use_date_arithmetic?
|
|
189
|
+
extend_remember_deadline? || db.database_type == :mysql
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def remember_key_ds(id=account_id)
|
|
193
|
+
db[remember_table].where(remember_id_column=>id)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def active_remember_key_ds(id=account_id)
|
|
197
|
+
remember_key_ds(id).where(Sequel.expr(remember_deadline_column) > Sequel::CURRENT_TIMESTAMP)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
ResetPassword = Feature.define(:reset_password) do
|
|
5
|
+
depends :login, :email_base, :login_password_requirements_base
|
|
6
|
+
|
|
7
|
+
notice_flash "Your password has been reset"
|
|
8
|
+
notice_flash "An email has been sent to you with a link to reset the password for your account", 'reset_password_email_sent'
|
|
9
|
+
error_flash "There was an error resetting your password"
|
|
10
|
+
error_flash "There was an error requesting a password reset", 'reset_password_request'
|
|
11
|
+
view 'reset-password', 'Reset Password'
|
|
12
|
+
additional_form_tags
|
|
13
|
+
additional_form_tags 'reset_password_request'
|
|
14
|
+
before
|
|
15
|
+
before 'reset_password_request'
|
|
16
|
+
after
|
|
17
|
+
after 'reset_password_request'
|
|
18
|
+
button 'Reset Password'
|
|
19
|
+
button 'Request Password Reset', 'reset_password_request'
|
|
20
|
+
redirect
|
|
21
|
+
redirect :reset_password_email_sent
|
|
22
|
+
|
|
23
|
+
auth_value_method :reset_password_deadline_column, :deadline
|
|
24
|
+
auth_value_method :reset_password_deadline_interval, {:days=>1}
|
|
25
|
+
auth_value_method :no_matching_reset_password_key_message, "invalid password reset key"
|
|
26
|
+
auth_value_method :reset_password_email_subject, 'Reset Password'
|
|
27
|
+
auth_value_method :reset_password_key_param, 'key'
|
|
28
|
+
auth_value_method :reset_password_autologin?, false
|
|
29
|
+
auth_value_method :reset_password_table, :account_password_reset_keys
|
|
30
|
+
auth_value_method :reset_password_id_column, :id
|
|
31
|
+
auth_value_method :reset_password_key_column, :key
|
|
32
|
+
|
|
33
|
+
auth_value_methods :reset_password_email_sent_redirect
|
|
34
|
+
|
|
35
|
+
auth_methods(
|
|
36
|
+
:create_reset_password_key,
|
|
37
|
+
:create_reset_password_email,
|
|
38
|
+
:get_reset_password_key,
|
|
39
|
+
:remove_reset_password_key,
|
|
40
|
+
:reset_password_email_body,
|
|
41
|
+
:reset_password_email_link,
|
|
42
|
+
:reset_password_key_insert_hash,
|
|
43
|
+
:reset_password_key_value,
|
|
44
|
+
:send_reset_password_email
|
|
45
|
+
)
|
|
46
|
+
auth_private_methods(
|
|
47
|
+
:account_from_reset_password_key
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
route(:reset_password_request) do |r|
|
|
51
|
+
check_already_logged_in
|
|
52
|
+
before_reset_password_request_route
|
|
53
|
+
|
|
54
|
+
r.post do
|
|
55
|
+
if account_from_login(param(login_param)) && open_account?
|
|
56
|
+
generate_reset_password_key_value
|
|
57
|
+
transaction do
|
|
58
|
+
before_reset_password_request
|
|
59
|
+
create_reset_password_key
|
|
60
|
+
send_reset_password_email
|
|
61
|
+
after_reset_password_request
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
set_notice_flash reset_password_email_sent_notice_flash
|
|
65
|
+
else
|
|
66
|
+
set_redirect_error_flash reset_password_request_error_flash
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
redirect reset_password_email_sent_redirect
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
route do |r|
|
|
74
|
+
check_already_logged_in
|
|
75
|
+
before_reset_password_route
|
|
76
|
+
|
|
77
|
+
r.get do
|
|
78
|
+
if key = param_or_nil(reset_password_key_param)
|
|
79
|
+
if account_from_reset_password_key(key)
|
|
80
|
+
reset_password_view
|
|
81
|
+
else
|
|
82
|
+
set_redirect_error_flash no_matching_reset_password_key_message
|
|
83
|
+
redirect require_login_redirect
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
r.post do
|
|
89
|
+
key = param(reset_password_key_param)
|
|
90
|
+
unless account_from_reset_password_key(key)
|
|
91
|
+
set_redirect_error_flash reset_password_error_flash
|
|
92
|
+
redirect reset_password_email_sent_redirect
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
password = param(password_param)
|
|
96
|
+
catch_error do
|
|
97
|
+
if password_match?(password)
|
|
98
|
+
throw_error(password_param, same_as_existing_password_message)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if require_password_confirmation? && password != param(password_confirm_param)
|
|
102
|
+
throw_error(password_param, passwords_do_not_match_message)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
unless password_meets_requirements?(password)
|
|
106
|
+
throw_error(password_param, password_does_not_meet_requirements_message)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
transaction do
|
|
110
|
+
before_reset_password
|
|
111
|
+
set_password(password)
|
|
112
|
+
remove_reset_password_key
|
|
113
|
+
after_reset_password
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if reset_password_autologin?
|
|
117
|
+
update_session
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
set_notice_flash reset_password_notice_flash
|
|
121
|
+
redirect reset_password_redirect
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
set_error_flash reset_password_error_flash
|
|
125
|
+
reset_password_view
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def create_reset_password_key
|
|
130
|
+
ds = password_reset_ds
|
|
131
|
+
transaction do
|
|
132
|
+
ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete
|
|
133
|
+
if ds.empty?
|
|
134
|
+
if e = raised_uniqueness_violation{ds.insert(reset_password_key_insert_hash)}
|
|
135
|
+
# If inserting into the reset password table causes a violation, we can pull the
|
|
136
|
+
# existing reset password key from the table, or reraise.
|
|
137
|
+
raise e unless @reset_password_key_value = get_password_reset_key(account_id)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def remove_reset_password_key
|
|
144
|
+
password_reset_ds.delete
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def account_from_reset_password_key(key)
|
|
148
|
+
@account = _account_from_reset_password_key(key)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def send_reset_password_email
|
|
152
|
+
create_reset_password_email.deliver!
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def reset_password_email_link
|
|
156
|
+
token_link(reset_password_route, reset_password_key_param, reset_password_key_value)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def get_password_reset_key(id)
|
|
160
|
+
password_reset_ds(id).get(reset_password_key_column)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
attr_reader :reset_password_key_value
|
|
166
|
+
|
|
167
|
+
def after_login_failure
|
|
168
|
+
@login_form_header = render("reset-password-request")
|
|
169
|
+
super
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def after_close_account
|
|
173
|
+
remove_reset_password_key
|
|
174
|
+
super if defined?(super)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def generate_reset_password_key_value
|
|
178
|
+
@reset_password_key_value = random_key
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def create_reset_password_email
|
|
182
|
+
create_email(reset_password_email_subject, reset_password_email_body)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def reset_password_email_body
|
|
186
|
+
render('reset-password-email')
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def use_date_arithmetic?
|
|
190
|
+
db.database_type == :mysql
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def reset_password_key_insert_hash
|
|
194
|
+
hash = {reset_password_id_column=>account_id, reset_password_key_column=>reset_password_key_value}
|
|
195
|
+
set_deadline_value(hash, reset_password_deadline_column, reset_password_deadline_interval)
|
|
196
|
+
hash
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def password_reset_ds(id=account_id)
|
|
200
|
+
db[reset_password_table].where(reset_password_id_column=>id)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def _account_from_reset_password_key(token)
|
|
204
|
+
account_from_key(token, account_open_status_value){|id| get_password_reset_key(id)}
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
SessionExpiration = Feature.define(:session_expiration) do
|
|
5
|
+
error_flash "This session has expired, please login again."
|
|
6
|
+
|
|
7
|
+
auth_value_method :max_session_lifetime, 86400
|
|
8
|
+
auth_value_method :session_created_session_key, :session_created_at
|
|
9
|
+
auth_value_method :session_expiration_default, true
|
|
10
|
+
auth_value_method :session_inactivity_timeout, 1800
|
|
11
|
+
auth_value_method :session_last_activity_session_key, :last_session_activity_at
|
|
12
|
+
|
|
13
|
+
auth_value_methods :session_expiration_redirect
|
|
14
|
+
|
|
15
|
+
def check_session_expiration
|
|
16
|
+
return unless logged_in?
|
|
17
|
+
|
|
18
|
+
unless session.has_key?(session_last_activity_session_key) && session.has_key?(session_created_session_key)
|
|
19
|
+
if session_expiration_default
|
|
20
|
+
expire_session
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
time = Time.now.to_i
|
|
27
|
+
|
|
28
|
+
if session[session_last_activity_session_key] + session_inactivity_timeout < time
|
|
29
|
+
expire_session
|
|
30
|
+
end
|
|
31
|
+
set_session_value(session_last_activity_session_key, time)
|
|
32
|
+
|
|
33
|
+
if session[session_created_session_key] + max_session_lifetime < time
|
|
34
|
+
expire_session
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def expire_session
|
|
39
|
+
clear_session
|
|
40
|
+
set_redirect_error_flash session_expiration_error_flash
|
|
41
|
+
redirect session_expiration_redirect
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def session_expiration_redirect
|
|
45
|
+
require_login_redirect
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def update_session
|
|
51
|
+
super
|
|
52
|
+
session[session_last_activity_session_key] = session[session_created_session_key] = Time.now.to_i
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|