quo_vadis 2.1.11 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +50 -99
- data/app/controllers/quo_vadis/confirmations_controller.rb +26 -61
- data/app/controllers/quo_vadis/password_resets_controller.rb +64 -32
- data/app/controllers/quo_vadis/sessions_controller.rb +0 -5
- data/app/mailers/quo_vadis/mailer.rb +2 -2
- data/app/models/quo_vadis/account.rb +32 -0
- data/app/models/quo_vadis/password.rb +6 -1
- data/app/views/quo_vadis/confirmations/new.html.erb +19 -7
- data/app/views/quo_vadis/mailer/account_confirmation.text.erb +2 -3
- data/app/views/quo_vadis/mailer/reset_password.text.erb +2 -2
- data/app/views/quo_vadis/password_resets/edit.html.erb +13 -1
- data/app/views/quo_vadis/password_resets/new.html.erb +1 -1
- data/config/locales/quo_vadis.en.yml +9 -7
- data/config/routes.rb +4 -12
- data/lib/quo_vadis/controller.rb +20 -10
- data/lib/quo_vadis/defaults.rb +2 -2
- data/lib/quo_vadis/version.rb +1 -1
- data/lib/quo_vadis.rb +2 -2
- data/test/dummy/app/controllers/sign_ups_controller.rb +2 -11
- data/test/fixtures/quo_vadis/mailer/account_confirmation.text +2 -3
- data/test/fixtures/quo_vadis/mailer/reset_password.text +2 -2
- data/test/integration/account_confirmation_test.rb +42 -86
- data/test/integration/controller_test.rb +8 -8
- data/test/integration/logging_test.rb +31 -7
- data/test/integration/password_login_test.rb +1 -1
- data/test/integration/password_reset_test.rb +89 -54
- data/test/mailers/mailer_test.rb +2 -2
- data/test/models/account_test.rb +48 -0
- data/test/models/session_test.rb +4 -0
- metadata +2 -10
- data/app/models/quo_vadis/account_confirmation_token.rb +0 -17
- data/app/models/quo_vadis/password_reset_token.rb +0 -17
- data/app/models/quo_vadis/token.rb +0 -42
- data/app/views/quo_vadis/confirmations/edit.html.erb +0 -10
- data/app/views/quo_vadis/confirmations/edit_email.html.erb +0 -14
- data/app/views/quo_vadis/confirmations/index.html.erb +0 -14
- data/app/views/quo_vadis/password_resets/index.html.erb +0 -5
- data/test/models/token_test.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e9808f4e29d96b1c9deac895bb45ebacbeb046928f22851fe618593abb49fa4
|
4
|
+
data.tar.gz: 38980863633e441f4c5d28c2fe03e5d8e6357afc4f4ddd4546f4492205aee48c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb68d8909ca3343ed508dbe5c0510860a358cb39398d2bb5b91f999c6a74935e8bb09d41a27de5bcdc1a3061cb280865ce146fa168fa88690fc37f030cd83a78
|
7
|
+
data.tar.gz: 6b5e022eab6f659dd4620117595c9ff9b9527f532d6963b732d149db4316f833626cca40f5e89cd7b8071ebea7e87ff64462c71c8104628b033447debec7a2a2
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,15 @@
|
|
4
4
|
## HEAD
|
5
5
|
|
6
6
|
|
7
|
+
## 2.2.0 (17 April 2023)
|
8
|
+
|
9
|
+
* Improve the readme with internal links and more section headings.
|
10
|
+
* Rename `password_reset_token_lifetime` to `password_reset_otp_lifetime`.
|
11
|
+
* Use OTP instead of link for password reset.
|
12
|
+
* Rename `account_confirmation_token_lifetime` to `account_confirmation_otp_lifetime`.
|
13
|
+
* Use OTP instead of link for account confirmation.
|
14
|
+
|
15
|
+
|
7
16
|
## 2.1.11 (14 September 2022)
|
8
17
|
|
9
18
|
* Introduce common controller superclass.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Quo Vadis
|
2
2
|
|
3
|
-
Multifactor authentication for your Rails
|
3
|
+
Multifactor authentication for your Rails app (Rails 7 and Rails 6).
|
4
4
|
|
5
5
|
Designed in accordance with the [OWASP Application Security Verification Standard](https://owasp.org/www-project-application-security-verification-standard/) and relevant [OWASP Cheatsheets](https://cheatsheetseries.owasp.org).
|
6
6
|
|
@@ -23,8 +23,8 @@ Simple to integrate into your application. The main task is customising the exa
|
|
23
23
|
- Two-factor authentication (2FA) by TOTP with recovery codes as a backup factor. Can be optional or mandatory.
|
24
24
|
- Change password.
|
25
25
|
- Reset password.
|
26
|
-
- Account confirmation (optional).
|
27
|
-
-
|
26
|
+
- Account confirmation (a.k.a. email verification) (optional).
|
27
|
+
- OTPs (account confirmation, password reset), TOTPs, and recovery codes are all one-time-only.
|
28
28
|
- Sessions expired after lifetime or idle time exceeded.
|
29
29
|
- Session replaced after any privilege change.
|
30
30
|
- View active sessions, log out of any of them.
|
@@ -86,7 +86,7 @@ end
|
|
86
86
|
|
87
87
|
You can create and update your models as before. When you want to set a password for the first time, just include `:password` and, optionally, `:password_confirmation` in the attributes to `#create` or `#update`.
|
88
88
|
|
89
|
-
If you want to change an existing password, use the Change Password feature
|
89
|
+
If you want to change an existing password, use the [Change Password](#change-password) feature. If you update a model (that already has a password) with a `:password` attribute, it will raise a `QuoVadis::PasswordExistsError`.
|
90
90
|
|
91
91
|
The minimum password length is configured by `QuoVadis.password_minimum_length` (12 by default).
|
92
92
|
|
@@ -95,7 +95,7 @@ The minimum password length is configured by `QuoVadis.password_minimum_length`
|
|
95
95
|
|
96
96
|
You can use these methods in your controllers.
|
97
97
|
|
98
|
-
|
98
|
+
#### `require_password_authentication`
|
99
99
|
|
100
100
|
Use this to restrict actions to password-authenticated users. It is aliased to `:require_authentication` for convenience.
|
101
101
|
|
@@ -105,7 +105,7 @@ class FoosController < ApplicationController
|
|
105
105
|
end
|
106
106
|
```
|
107
107
|
|
108
|
-
|
108
|
+
#### `require_two_factor_authentication`
|
109
109
|
|
110
110
|
Use this to restrict actions to users authenticated with both a password and a second factor. (You do not need to use `:require_password_authentication` for these actions.)
|
111
111
|
|
@@ -115,21 +115,17 @@ class BarsController < ApplicationController
|
|
115
115
|
end
|
116
116
|
```
|
117
117
|
|
118
|
-
|
118
|
+
#### `login(model, browser_session = true, metadata: {})`
|
119
119
|
|
120
|
-
|
120
|
+
Use this to log in a user who has authenticated with a password. For the optional `browser_session` argument, pass `true` to log in for the duration of the browser session, or `false` to log in for `QuoVadis.session_lifetime` (which could be the browser session anyway). Any metadata are stored in the log entry for the login.
|
121
121
|
|
122
|
-
|
123
|
-
|
124
|
-
This is used to sent an account confirmation email to the user. See the Account Confirmation feature below for details.
|
125
|
-
|
126
|
-
__`authenticated_model`__
|
122
|
+
#### `authenticated_model`
|
127
123
|
|
128
124
|
Call this to get the authenticated user. Feel free to alias this to `:current_user` or set it into an `ActiveSupport::CurrentAttributes` class.
|
129
125
|
|
130
126
|
Available in controllers and views.
|
131
127
|
|
132
|
-
|
128
|
+
#### `logged_in?`
|
133
129
|
|
134
130
|
Call this to find out whether a user has authenticated with a password.
|
135
131
|
|
@@ -175,7 +171,7 @@ Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob
|
|
175
171
|
- a field for their identifier;
|
176
172
|
- an `:email` field if the identifier is not their email.
|
177
173
|
|
178
|
-
In your controller, use the `#login` method to log in your new user. The optional second argument
|
174
|
+
In your controller, use the [`#login`](#loginmodel-browser_session-%3D-true) method to log in your new user. The optional second argument specifies for how long the user should be logged in, and any metadata you supply is logged in the audit log.
|
179
175
|
|
180
176
|
After logging in the user, redirect them wherever you like. You can use `qv.path_after_signup` which resolves to the first of these routes that exists: `:after_signup`, `:after_login`, the root route.
|
181
177
|
|
@@ -207,60 +203,19 @@ get '/dashboard', as: 'after_login'
|
|
207
203
|
|
208
204
|
### Sign up with account confirmation
|
209
205
|
|
210
|
-
|
206
|
+
Follow the steps above for sign-up.
|
211
207
|
|
212
|
-
|
213
|
-
2. [Your controller] Your code tells QuoVadis to email the user a confirmation link. The link is valid for `QuoVadis.account_confirmation_token_lifetime`.
|
214
|
-
3. [The email] The user clicks the link.
|
215
|
-
4. [Account-confirmation confirmation page] The user clicks a button to confirm their account. (This step is to prevent any link prefetching in the user's mail client from confirming them unintentionally.)
|
216
|
-
5. QuoVadis confirms the user's account and logs them in.
|
208
|
+
After you have logged in the user and redirected them (to any page which requires being logged in), QuoVadis detects that they need to confirm their account. QuoVadis emails them a 6-digit confirmation code and redirects them to the confirmation page where they can enter that code.
|
217
209
|
|
218
|
-
|
210
|
+
The confirmation code is valid for `QuoVadis.account_confirmation_otp_lifetime`.
|
219
211
|
|
220
|
-
|
221
|
-
- optionally a `:password_confirmation` field;
|
222
|
-
- a field for their identifier;
|
223
|
-
- an `:email` field if the identifier is not their email.
|
212
|
+
Once the user has confirmed their account, they will be redirected to `qv.path_after_signup` which resolves to the first of these routes that exists: `:after_signup`, `:after_login`, the root route. Add whichever works best for you.
|
224
213
|
|
225
|
-
|
214
|
+
You need to write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/account_confirmation.text.erb)). It must be in `app/views/quo_vadis/mailer/account_confirmation.{text,html}.erb` and output the `@otp` variable. See the [Configuration](#configuration) section for how to set QuoVadis's emails' from addresses, headers, etc.
|
226
215
|
|
227
|
-
|
228
|
-
class UsersController < ApplicationController
|
229
|
-
def create
|
230
|
-
@user = User.new user_params
|
231
|
-
if @user.save
|
232
|
-
request_confirmation @user
|
233
|
-
redirect_to quo_vadis.confirmations_path # a page where you advise the user to check their email
|
234
|
-
else
|
235
|
-
# ...
|
236
|
-
end
|
237
|
-
end
|
216
|
+
Now write the confirmation page where the user types in the confirmation code from the email ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/new.html.erb)). It must be in `app/views/quo_vadis/confirmations/new.html.:format` and must POST the `otp` field to `confirm_path`. You can provide a button to send a new confirmation code (perhaps the original email didn't arrive, or the user didn't have time to act on it before it expired) – it should POST to `send_confirmation_path`.
|
238
217
|
|
239
|
-
|
240
|
-
|
241
|
-
def user_params
|
242
|
-
params.require(:user).permit(:name, :email, :password, :password_confirmation)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
```
|
246
|
-
|
247
|
-
QuoVadis will send the user an email with a link. Write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/account_confirmation.text.erb)). It must be in `app/views/quo_vadis/mailer/account_confirmation.{text,html}.erb` and output the `@url` variable.
|
248
|
-
|
249
|
-
See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
|
250
|
-
|
251
|
-
Now write the page to where the user is redirected while they wait for the email ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/index.html.erb)). It must be in `app/views/quo_vadis/confirmations/index.html.:format`.
|
252
|
-
|
253
|
-
On that page you can show the user the address the email was sent to, enable them to update their email address if they make a mistake on the sign-up form, and provide a button to resend another email directly. If the sign-up occurred in a different browser session, you can instead link to `new_confirmation_path` where the user can request another email if need be.
|
254
|
-
|
255
|
-
Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/edit.html.erb)). It must be in `app/views/quo_vadis/confirmations/edit.html.:format`.
|
256
|
-
|
257
|
-
Next, write the page where the user can amend their email address if they made a mistake when signing up ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/edit_email.html.erb)). It must be in `app/views/quo_vadis/confirmations/edit_email.html.:format`.
|
258
|
-
|
259
|
-
Finally, write the page where people can put in their identifier (not their email, unless the identifier is email) again to request another confirmation email ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/new.html.erb)). It must be in `app/views/quo_vadis/confirmations/new.html.:format`.
|
260
|
-
|
261
|
-
After the user has confirmed their account, they will be logged in and redirected to `qv.path_after_signup` which resolves to the first of these routes that exists: `:after_signup`, `:after_login`, the root route.
|
262
|
-
|
263
|
-
So add whichever works best for you.
|
218
|
+
If the user closes their browser after signing up but before they have confirmed their account, when they next access a page which requires being logged in they will be sent a new confirmation code and redirected to the confirmation page, as if they had just signed up.
|
264
219
|
|
265
220
|
|
266
221
|
### Login
|
@@ -334,31 +289,27 @@ A successful password change logs out any other sessions the user has (e.g. on o
|
|
334
289
|
|
335
290
|
### Reset password
|
336
291
|
|
337
|
-
The user can reset their password if they lose it. The flow is:
|
292
|
+
The user can reset their password if they lose it and cannot log in. The flow is:
|
338
293
|
|
339
294
|
1. [Request password-reset page] User enters their identifier (not their email unless the identifier is email).
|
340
|
-
2. QuoVadis emails the user a
|
341
|
-
3. [The email] The user
|
342
|
-
4. [Password-reset
|
295
|
+
2. QuoVadis emails the user a 6-digit reset code, which is valid for `QuoVadis.password_reset_otp_lifetime`, and redirects to the password-reset page.
|
296
|
+
3. [The email] The user reads the code.
|
297
|
+
4. [Password-reset page] The user enters the 6-digt code and their new password and clicks the save button.
|
343
298
|
5. QuoVadis sets the user's password and logs them in.
|
344
299
|
|
345
|
-
First, write the page where the user requests a password-reset ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/password_resets/new.html.erb)). It must be in `app/views/quo_vadis/password_resets/new.html.:format`.
|
300
|
+
First, write the page where the user requests a password-reset ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/password_resets/new.html.erb)). It must be in `app/views/quo_vadis/password_resets/new.html.:format`. It must POST the user's identifier (not email, unless the identifier is email) to `password_reset_path`.
|
346
301
|
|
347
|
-
See the Configuration section
|
302
|
+
Now write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/reset_password.text.erb)). It must be in `app/views/quo_vadis/mailer/reset_password.{text,html}.erb` and output the `@otp` variable. See the [Configuration](#configuration) section for how to set QuoVadis's emails' from addresses, headers, etc.
|
348
303
|
|
349
|
-
Now write the page
|
350
|
-
|
351
|
-
It's a good idea for that page to link to `new_password_reset_path` where the user can request another email if need be.
|
352
|
-
|
353
|
-
Now write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/reset_password.text.erb)). It must be in `app/views/quo_vadis/mailer/reset_password.{text,html}.erb` and output the `@url` variable.
|
354
|
-
|
355
|
-
Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/password_resets/edit.html.erb)). It must be in `app/views/quo_vadis/password_resets/edit.html.:format`.
|
304
|
+
Now write the page where the user types in the reset code from the email and their new password ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/password_resets/edit.html.erb)). It must be in `app/views/quo_vadis/password_resets/edit.html.:format` and must PUT the `otp`, `password`, and `password_confirmation` fields to `password_reset_path`.
|
356
305
|
|
357
306
|
After the user has reset their password, they will be logged in and redirected to the first of these that exists:
|
358
307
|
|
359
308
|
- a route named `:after_login`;
|
360
309
|
- your root route.
|
361
310
|
|
311
|
+
When the user resets their password, they are logged out of any other sessions they may have, for example on other devices.
|
312
|
+
|
362
313
|
|
363
314
|
### Sessions
|
364
315
|
|
@@ -411,9 +362,9 @@ QuoVadis.configure do
|
|
411
362
|
session_lifetime :session
|
412
363
|
session_lifetime_extend_to_end_of_day false
|
413
364
|
session_idle_timeout :lifetime
|
414
|
-
|
365
|
+
password_reset_otp_lifetime 10.minutes
|
415
366
|
accounts_require_confirmation false
|
416
|
-
|
367
|
+
account_confirmation_otp_lifetime 10.minutes
|
417
368
|
mail_headers ({ from: 'Example App <support@example.com>' })
|
418
369
|
enqueue_transactional_emails true
|
419
370
|
app_name Rails.app_class.to_s.deconstantize # for the TOTP QR code
|
@@ -426,75 +377,75 @@ You can override any of it with a similarly structured file in `config/initializ
|
|
426
377
|
|
427
378
|
Here are the options in detail:
|
428
379
|
|
429
|
-
|
380
|
+
#### `password_minimum_length` (integer)
|
430
381
|
|
431
382
|
The minimum number of characters for a password.
|
432
383
|
|
433
|
-
|
384
|
+
#### `mask_ips` (boolean)
|
434
385
|
|
435
386
|
Whether to mask the IP address in the sessions list and the audit trail.
|
436
387
|
|
437
388
|
Masking means setting the last octet (IPv4) or the last 80 bits (IPv6) to 0.
|
438
389
|
|
439
|
-
|
390
|
+
#### `cookie_name` (string)
|
440
391
|
|
441
392
|
The name of the cookie QuoVadis uses to store the session identifier. The `__Host-` prefix is [recommended](https://developer.mozilla.org/en-US/docs/Web/API/document/cookie) in an SSL environment (but cannot be used in a non-SSL environment).
|
442
393
|
|
443
|
-
|
394
|
+
#### `session_lifetime` (`:session` | `ActiveSupport::Duration` | integer)
|
444
395
|
|
445
396
|
The lifetime of a logged-in session. Use `:session` for the browser session, or a `Duration` or number of seconds.
|
446
397
|
|
447
|
-
|
398
|
+
#### `session_lifetime_extend_to_end_of_day` (boolean)
|
448
399
|
|
449
400
|
Whether to extend the session's lifetime to the end of the day it will expire on.
|
450
401
|
|
451
402
|
Set `true` to reduce the chance of a user being logged out while actively using your application.
|
452
403
|
|
453
|
-
|
404
|
+
#### `session_idle_timeout` (`:lifetime` | `ActiveSupport::Duration` | integer)
|
454
405
|
|
455
406
|
The logged-in session is expired if the user isn't seen for this `Duration` or number of seconds. Use `:lifetime` to set the idle timeout to the session's lifetime (i.e. to turn off the idle timeout).
|
456
407
|
|
457
|
-
|
408
|
+
#### `password_reset_otp_lifetime` (`ActiveSupport::Duration` | integer)
|
458
409
|
|
459
|
-
The `Duration` or number of seconds for which a password-reset
|
410
|
+
The `Duration` or number of seconds for which a password-reset code is valid.
|
460
411
|
|
461
|
-
|
412
|
+
#### `accounts_require_confirmation` (boolean)
|
462
413
|
|
463
414
|
Whether new users must confirm their account before they can log in.
|
464
415
|
|
465
|
-
|
416
|
+
#### `account_confirmation_otp_lifetime` (`ActiveSupport::Duration` | integer)
|
466
417
|
|
467
|
-
The `Duration` or number of seconds for which an account-confirmation
|
418
|
+
The `Duration` or number of seconds for which an account-confirmation code is valid.
|
468
419
|
|
469
|
-
|
420
|
+
#### `mailer_superclass` (string)
|
470
421
|
|
471
422
|
The class from which QuoVadis's mailer inherits.
|
472
423
|
|
473
|
-
|
424
|
+
#### `mail_headers` (hash)
|
474
425
|
|
475
426
|
Mail headers which QuoVadis' emails should have.
|
476
427
|
|
477
|
-
|
428
|
+
#### `enqueue_transactional_emails` (boolean)
|
478
429
|
|
479
430
|
Set `true` if account-confirmation and password-reset emails should be queued for later delivery (`#deliver_later`) or `false` if they should be sent inline (`#deliver_now`).
|
480
431
|
|
481
|
-
|
432
|
+
#### `app_name` (string)
|
482
433
|
|
483
434
|
Used in the provisioning URI for the TOTP QR code.
|
484
435
|
|
485
|
-
|
436
|
+
#### `two_factor_authentication_mandatory` (boolean)
|
486
437
|
|
487
438
|
Whether users must set up and use a second authentication factor.
|
488
439
|
|
489
|
-
|
440
|
+
#### `mount_point` (string)
|
490
441
|
|
491
442
|
The path prefix for QuoVadis's routes.
|
492
443
|
|
493
444
|
For example, the default login path is at `/login`. If you set `mount_point` to `/auth`, the login path would be `/auth/login`.
|
494
445
|
|
495
|
-
|
446
|
+
### Rails configuration
|
496
447
|
|
497
|
-
|
448
|
+
#### Mailer URLs
|
498
449
|
|
499
450
|
You must also configure the mailer host so URLs are generated correctly in emails:
|
500
451
|
|
@@ -502,7 +453,7 @@ You must also configure the mailer host so URLs are generated correctly in email
|
|
502
453
|
config.action_mailer.default_url_options: { host: 'example.com' }
|
503
454
|
```
|
504
455
|
|
505
|
-
|
456
|
+
#### Layouts
|
506
457
|
|
507
458
|
You can specify QuoVadis's controllers' layouts in a `#to_prepare` block in your application configuration. For example:
|
508
459
|
|
@@ -517,7 +468,7 @@ module YourApp
|
|
517
468
|
end
|
518
469
|
```
|
519
470
|
|
520
|
-
|
471
|
+
#### Routes
|
521
472
|
|
522
473
|
You can set up your post-signup, post-authentication, and post-password-change routes. If you don't, you must have a root route. For example:
|
523
474
|
|
@@ -3,99 +3,64 @@
|
|
3
3
|
module QuoVadis
|
4
4
|
class ConfirmationsController < QuoVadisController
|
5
5
|
|
6
|
-
|
7
|
-
def index
|
6
|
+
def new
|
8
7
|
@account = find_pending_account_from_session
|
9
|
-
end
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
unless @account
|
10
|
+
redirect_to qv.path_after_signup, alert: QuoVadis.translate('flash.confirmation.unknown')
|
11
|
+
end
|
14
12
|
end
|
15
13
|
|
16
14
|
|
17
|
-
# send new confirmation email form submits here
|
18
15
|
def create
|
19
|
-
account =
|
16
|
+
@account = find_pending_account_from_session
|
20
17
|
|
21
|
-
unless account
|
22
|
-
redirect_to
|
18
|
+
unless @account
|
19
|
+
redirect_to qv.path_after_signup, alert: QuoVadis.translate('flash.confirmation.unknown')
|
20
|
+
return
|
23
21
|
end
|
24
22
|
|
25
|
-
|
26
|
-
redirect_to confirmations_path
|
27
|
-
end
|
23
|
+
expiry = session[:account_confirmation_expires_at]
|
28
24
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
account = AccountConfirmationToken.find_account params[:token]
|
33
|
-
|
34
|
-
unless account # expired or already confirmed
|
35
|
-
redirect_to new_confirmation_path, alert: QuoVadis.translate('flash.confirmation.unknown') and return
|
25
|
+
if Time.current.to_i > expiry
|
26
|
+
redirect_to confirm_path, alert: QuoVadis.translate('flash.confirmation.expired')
|
27
|
+
return
|
36
28
|
end
|
37
|
-
end
|
38
29
|
|
30
|
+
confirmed = @account.confirm(params[:otp], expiry)
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
unless account # expired or already confirmed
|
45
|
-
redirect_to new_confirmation_path, alert: QuoVadis.translate('flash.confirmation.unknown') and return
|
32
|
+
if !confirmed
|
33
|
+
redirect_to confirm_path, alert: QuoVadis.translate('flash.confirmation.invalid')
|
34
|
+
return
|
46
35
|
end
|
47
36
|
|
48
|
-
account
|
49
|
-
qv.log account, Log::ACCOUNT_CONFIRMATION
|
37
|
+
qv.log @account, Log::ACCOUNT_CONFIRMATION
|
50
38
|
|
51
39
|
session.delete :account_pending_confirmation
|
40
|
+
session.delete :account_confirmation_expires_at
|
52
41
|
|
53
|
-
login account.model, true
|
54
42
|
redirect_to qv.path_after_signup, notice: QuoVadis.translate('flash.confirmation.confirmed')
|
55
43
|
end
|
56
44
|
|
57
45
|
|
58
|
-
def edit_email
|
59
|
-
account = find_pending_account_from_session
|
60
|
-
|
61
|
-
unless account
|
62
|
-
redirect_to confirmations_path, alert: QuoVadis.translate('flash.confirmation.unknown') and return
|
63
|
-
end
|
64
|
-
|
65
|
-
@email = account.model.email
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
def update_email
|
70
|
-
account = find_pending_account_from_session
|
71
|
-
|
72
|
-
unless account
|
73
|
-
redirect_to confirmations_path, alert: QuoVadis.translate('flash.confirmation.unknown') and return
|
74
|
-
end
|
75
|
-
|
76
|
-
account.model.update email: params[:email]
|
77
|
-
|
78
|
-
request_confirmation account.model
|
79
|
-
redirect_to confirmations_path
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
46
|
def resend
|
84
|
-
account = find_pending_account_from_session
|
47
|
+
@account = find_pending_account_from_session
|
85
48
|
|
86
|
-
unless account
|
87
|
-
redirect_to
|
49
|
+
unless @account
|
50
|
+
redirect_to qv.path_after_signup, alert: QuoVadis.translate('flash.confirmation.unknown')
|
88
51
|
end
|
89
52
|
|
90
|
-
request_confirmation account.model
|
91
|
-
redirect_to
|
53
|
+
qv.request_confirmation @account.model
|
54
|
+
redirect_to confirm_path, notice: QuoVadis.translate('flash.confirmation.sent')
|
92
55
|
end
|
93
56
|
|
94
57
|
|
95
58
|
private
|
96
59
|
|
97
60
|
def find_pending_account_from_session
|
98
|
-
|
61
|
+
if session[:account_pending_confirmation]
|
62
|
+
Account.unconfirmed.find(session[:account_pending_confirmation])
|
63
|
+
end
|
99
64
|
end
|
100
65
|
|
101
66
|
end
|
@@ -3,66 +3,98 @@
|
|
3
3
|
module QuoVadis
|
4
4
|
class PasswordResetsController < QuoVadisController
|
5
5
|
|
6
|
-
#
|
7
|
-
def index
|
8
|
-
end
|
9
|
-
|
10
|
-
|
6
|
+
# form where user enters their identifier
|
11
7
|
def new
|
12
8
|
end
|
13
9
|
|
14
10
|
|
11
|
+
# generate and email an otp
|
15
12
|
def create
|
16
13
|
account = QuoVadis.find_account_by_identifier_in_params params
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
# The recommendation is to show the user the same message whether
|
16
|
+
# or not their account was found. This favours privacy over
|
17
|
+
# helpfulness and is the default.
|
18
|
+
#
|
19
|
+
# If you would prefer helpfulness over privacy -- perhaps the user
|
20
|
+
# simply typo'd their identifier -- set the `unknown` flash message
|
21
|
+
# to something helpful.
|
22
|
+
message_known = QuoVadis.translate('flash.password_reset.create')
|
23
|
+
message_unknown = QuoVadis.translate('flash.password_reset.unknown')
|
24
|
+
|
25
|
+
if message_known == message_unknown
|
26
|
+
flash[:notice] = message_known
|
27
|
+
elsif account
|
28
|
+
flash[:notice] = message_known
|
29
|
+
else
|
30
|
+
flash[:alert] = message_unknown
|
21
31
|
end
|
22
32
|
|
23
|
-
|
24
|
-
|
33
|
+
if account
|
34
|
+
session[:account_resetting_password] = account.id
|
25
35
|
|
36
|
+
expiration = QuoVadis.password_reset_otp_lifetime.from_now.to_i
|
37
|
+
session[:password_reset_expires_at] = expiration
|
26
38
|
|
27
|
-
|
28
|
-
def edit
|
29
|
-
account = PasswordResetToken.find_account params[:token]
|
39
|
+
otp = account.otp_for_password_reset(expiration)
|
30
40
|
|
31
|
-
|
32
|
-
redirect_to new_password_reset_path, alert: QuoVadis.translate('flash.password_reset.unknown') and return
|
41
|
+
QuoVadis.deliver :reset_password, {email: account.model.email, otp: otp}
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
|
44
|
+
redirect_to edit_password_reset_path
|
45
|
+
end
|
46
|
+
|
38
47
|
|
48
|
+
# form for otp and new password
|
49
|
+
def edit
|
39
50
|
@password = QuoVadis::Password.new
|
40
51
|
end
|
41
52
|
|
42
53
|
|
43
|
-
#
|
54
|
+
# update password if otp and password are valid
|
44
55
|
def update
|
45
|
-
account =
|
56
|
+
account = find_account_resetting_password_from_session
|
46
57
|
|
47
|
-
unless account
|
48
|
-
redirect_to new_password_reset_path
|
58
|
+
unless account
|
59
|
+
redirect_to new_password_reset_path
|
60
|
+
return
|
49
61
|
end
|
50
62
|
|
51
|
-
|
52
|
-
if @password.reset params[:password][:password], params[:password][:password_confirmation]
|
53
|
-
# Logout account's sessions because password has changed.
|
54
|
-
# Note model is not logged in here.
|
55
|
-
@password.account.sessions.destroy_all
|
63
|
+
expiry = session[:password_reset_expires_at]
|
56
64
|
|
57
|
-
|
58
|
-
|
65
|
+
if Time.current.to_i > expiry
|
66
|
+
redirect_to new_password_reset_path, alert: QuoVadis.translate('flash.password_reset.expired')
|
67
|
+
return
|
68
|
+
end
|
59
69
|
|
60
|
-
|
61
|
-
redirect_to
|
62
|
-
|
70
|
+
unless account.verify_password_reset(params[:password][:otp], expiry)
|
71
|
+
redirect_to new_password_reset_path, alert: QuoVadis.translate('flash.password_reset.invalid')
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
@password = account.password
|
76
|
+
unless @password.reset(params[:password][:password], params[:password][:password_confirmation])
|
63
77
|
render :edit, status: :unprocessable_entity
|
78
|
+
return
|
64
79
|
end
|
80
|
+
|
81
|
+
session.delete :account_resetting_password
|
82
|
+
session.delete :password_reset_expires_at
|
83
|
+
|
84
|
+
qv.log account, Log::PASSWORD_RESET
|
85
|
+
QuoVadis.notify :password_reset_notification, email: account.model.email
|
86
|
+
|
87
|
+
login account.model, true
|
88
|
+
|
89
|
+
redirect_to qv.path_after_authentication, notice: QuoVadis.translate('flash.password_reset.reset')
|
65
90
|
end
|
66
91
|
|
92
|
+
private
|
93
|
+
|
94
|
+
def find_account_resetting_password_from_session
|
95
|
+
if session[:account_resetting_password]
|
96
|
+
Account.find(session[:account_resetting_password])
|
97
|
+
end
|
98
|
+
end
|
67
99
|
end
|
68
100
|
end
|
@@ -34,11 +34,6 @@ module QuoVadis
|
|
34
34
|
return
|
35
35
|
end
|
36
36
|
|
37
|
-
if QuoVadis.accounts_require_confirmation && !account.confirmed?
|
38
|
-
redirect_to new_confirmation_path, notice: QuoVadis.translate('flash.confirmation.required')
|
39
|
-
return
|
40
|
-
end
|
41
|
-
|
42
37
|
# no params[:remember] => use QuoVadis.session_lifetime
|
43
38
|
# params[:remember] == 0 => use :session
|
44
39
|
# params[:remember] == 1 => use QuoVadis.session_lifetime
|
@@ -4,12 +4,12 @@ module QuoVadis
|
|
4
4
|
class Mailer < QuoVadis.mailer_superclass.constantize
|
5
5
|
|
6
6
|
def reset_password
|
7
|
-
@
|
7
|
+
@otp = params[:otp]
|
8
8
|
_mail params[:email], QuoVadis.translate('mailer.password_reset.subject')
|
9
9
|
end
|
10
10
|
|
11
11
|
def account_confirmation
|
12
|
-
@
|
12
|
+
@otp = params[:otp]
|
13
13
|
_mail params[:email], QuoVadis.translate('mailer.confirmation.subject')
|
14
14
|
end
|
15
15
|
|