quo_vadis 2.0.1 → 2.1.2
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.md +31 -0
- data/README.md +59 -33
- data/app/controllers/quo_vadis/confirmations_controller.rb +47 -1
- data/app/controllers/quo_vadis/password_resets_controller.rb +11 -8
- data/app/controllers/quo_vadis/passwords_controller.rb +4 -2
- data/app/controllers/quo_vadis/recovery_codes_controller.rb +1 -1
- data/app/controllers/quo_vadis/sessions_controller.rb +3 -3
- data/app/controllers/quo_vadis/totps_controller.rb +1 -1
- data/app/models/quo_vadis/account.rb +14 -0
- data/app/models/quo_vadis/log.rb +4 -2
- data/app/models/quo_vadis/password.rb +16 -0
- data/{test/dummy/app → app}/views/quo_vadis/confirmations/edit.html.erb +0 -0
- data/app/views/quo_vadis/confirmations/edit_email.html.erb +14 -0
- data/app/views/quo_vadis/confirmations/index.html.erb +14 -0
- data/{test/dummy/app → app}/views/quo_vadis/confirmations/new.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/logs/index.html.erb +3 -1
- data/{test/dummy/app → app}/views/quo_vadis/mailer/account_confirmation.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/email_change_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/identifier_change_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/password_change_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/password_reset_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/reset_password.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/totp_reuse_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/totp_setup_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/password_resets/edit.html.erb +1 -9
- data/{test/dummy/app → app}/views/quo_vadis/password_resets/index.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/password_resets/new.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/passwords/edit.html.erb +1 -9
- data/{test/dummy/app → app}/views/quo_vadis/recovery_codes/challenge.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/recovery_codes/index.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/sessions/index.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/sessions/new.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/totps/challenge.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/totps/new.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/twofas/show.html.erb +0 -0
- data/config/locales/quo_vadis.en.yml +30 -1
- data/config/routes.rb +11 -5
- data/lib/generators/quo_vadis/install_generator.rb +1 -1
- data/lib/quo_vadis/controller.rb +3 -3
- data/lib/quo_vadis/defaults.rb +1 -1
- data/lib/quo_vadis/model.rb +8 -11
- data/lib/quo_vadis/version.rb +1 -1
- data/lib/quo_vadis.rb +5 -1
- data/test/dummy/app/controllers/sign_ups_controller.rb +1 -1
- data/test/dummy/config/initializers/quo_vadis.rb +0 -4
- data/test/integration/account_confirmation_test.rb +35 -2
- data/test/integration/logging_test.rb +14 -5
- data/test/integration/password_change_test.rb +16 -10
- data/test/integration/password_login_test.rb +14 -2
- data/test/integration/password_reset_test.rb +6 -6
- data/test/integration/totps_test.rb +1 -1
- data/test/models/account_test.rb +16 -0
- data/test/models/password_test.rb +16 -0
- data/test/models/recovery_code_test.rb +7 -1
- data/test/models/token_test.rb +1 -1
- metadata +29 -28
- data/test/dummy/app/views/quo_vadis/confirmations/index.html.erb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b81cb83f9cd0b6bb6e9af4015dba59b5cef0f5c3fccd5a82ee6d7e1bbc5768a
|
4
|
+
data.tar.gz: 8a9f5f0c013ec89de6849e9ddb3393fee46b84a2d08b20e606126cf97bba8032
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7b5e766f22e55e5bc269d00e6fb9132cc54ba2523a72996d0d7da478dbf0662764d29265c591f5847d5f2cfaeaca1e78fb4d48fb8fecb24af0bee9e9c2047e2
|
7
|
+
data.tar.gz: 47bf8bba82c90f3b7527be6a316ec8f9a4b229077a6846454abecebd6d2ae29910f7e416635d9cc1d54e3b99fe87d4c7d0ca871741907f1987d1ee42bf79994b
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,37 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## HEAD
|
5
|
+
|
6
|
+
|
7
|
+
## 2.1.1 (8 July 2021)
|
8
|
+
|
9
|
+
* Remove unnecessary route names.
|
10
|
+
* Add user revocation.
|
11
|
+
* Ensure password is only updated via #change or #reset.
|
12
|
+
* Move views into gem's app/views/ directory.
|
13
|
+
|
14
|
+
|
15
|
+
## 2.1.0 (25 June 2021)
|
16
|
+
|
17
|
+
* Do not require password on create.
|
18
|
+
* Fix incorrect assignment of built association.
|
19
|
+
* Add i18n translations for log actions.
|
20
|
+
* Use model instance in change-password form.
|
21
|
+
* Ensure password-reset flash notice not displayed when emailed link is clicked.
|
22
|
+
* Use model instance in password-reset form.
|
23
|
+
* Give no indication of unknown account on request of password reset email.
|
24
|
+
* Use 422 status code for form submission error responses.
|
25
|
+
* Make default cookie name depend on Rails environment.
|
26
|
+
|
27
|
+
|
28
|
+
## 2.0.2 (24 May 2021)
|
29
|
+
|
30
|
+
* Account confirmation: enable updating of email address.
|
31
|
+
* Account confirmation: enable direct resending of email.
|
32
|
+
* Log unknown identifier in metadata.
|
33
|
+
|
34
|
+
|
4
35
|
## 2.0.1 (18 May 2021)
|
5
36
|
|
6
37
|
* Remove Gemfile.lock from repo.
|
data/README.md
CHANGED
@@ -86,9 +86,9 @@ class Person < ApplicationRecord
|
|
86
86
|
end
|
87
87
|
```
|
88
88
|
|
89
|
-
When
|
89
|
+
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`.
|
90
90
|
|
91
|
-
|
91
|
+
If you want to change an existing password, use the Change Password feature (see below). If you update a model (that already has a password) with a `:password` attribute, it will raise a `QuoVadis::PasswordExistsError`.
|
92
92
|
|
93
93
|
The minimum password length is configured by `QuoVadis.password_minimum_length` (12 by default).
|
94
94
|
|
@@ -232,7 +232,7 @@ class UsersController < ApplicationController
|
|
232
232
|
@user = User.new user_params
|
233
233
|
if @user.save
|
234
234
|
request_confirmation @user
|
235
|
-
redirect_to
|
235
|
+
redirect_to quo_vadis.confirmations_path # a page where you advise the user to check their email
|
236
236
|
else
|
237
237
|
# ...
|
238
238
|
end
|
@@ -246,17 +246,19 @@ class UsersController < ApplicationController
|
|
246
246
|
end
|
247
247
|
```
|
248
248
|
|
249
|
-
QuoVadis will send the user an email with a link. Write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/
|
249
|
+
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.
|
250
250
|
|
251
251
|
See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
|
252
252
|
|
253
|
-
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/
|
253
|
+
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`.
|
254
254
|
|
255
|
-
|
255
|
+
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.
|
256
256
|
|
257
|
-
Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/
|
257
|
+
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`.
|
258
258
|
|
259
|
-
|
259
|
+
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`.
|
260
|
+
|
261
|
+
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
262
|
|
261
263
|
After the user has confirmed their account, they will be logged in and redirected to the first of these that exists:
|
262
264
|
|
@@ -270,7 +272,7 @@ So add whichever works best for you.
|
|
270
272
|
|
271
273
|
Use `before_action :require_password_authentication` or `before_action :require_authentication` in your controllers.
|
272
274
|
|
273
|
-
Write the login view ([example](https://github.com/airblade/quo_vadis/blob/master/
|
275
|
+
Write the login view ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/sessions/new.html.erb)). Your login form must be in `app/views/quo_vadis/sessions/new.html.:format`. Note it must capture the user's identifier (not email, unless the identifier is email).
|
274
276
|
|
275
277
|
If you include a `remember` checkbox in your login form:
|
276
278
|
|
@@ -290,7 +292,7 @@ After authenticating the user will be redirected to the first of these that exis
|
|
290
292
|
|
291
293
|
If you do not want 2FA at all, set `QuoVadis.two_factor_authentication_mandatory false` in your configuration and skip the rest of this section.
|
292
294
|
|
293
|
-
If you do want 2FA, you can choose whether it is
|
295
|
+
If you do want 2FA, you can choose whether it is mandatory or optional for your users by setting `QuoVadis.two_factor_authentication_mandatory <true|false>` in your configuration.
|
294
296
|
|
295
297
|
Use `before_action :require_two_factor_authentication` in your controllers (which supersedes `:require_password_authentication`). This will require the user, after authenticating with their password, to authenticate with 2FA – when 2FA is mandatory, or when it is optional and the user has set up 2FA.
|
296
298
|
|
@@ -310,22 +312,22 @@ In your views, have a link where users can manage their 2FA:
|
|
310
312
|
link_to '2FA', quo_vadis.twofa_path
|
311
313
|
```
|
312
314
|
|
313
|
-
Write the 2FA overview page ([example](https://github.com/airblade/quo_vadis/blob/master/
|
315
|
+
Write the 2FA overview page ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/twofas/show.html.erb)). It must be in `app/views/quo_vadis/twofas/show.html.:format`. This page allows the user to set up 2FA, deactivate or reset it, and generate new recovery codes.
|
314
316
|
|
315
|
-
Next, write the TOTP setup page ([example](https://github.com/airblade/quo_vadis/blob/master/
|
317
|
+
Next, write the TOTP setup page ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/totps/new.html.erb)). It must be in `app/views/quo_vadis/totps/new.html.:format`. This page shows the user a QR code (and the key as text) which they scan with their authenticator.
|
316
318
|
|
317
|
-
Next, write the recovery codes page ([example](https://github.com/airblade/quo_vadis/blob/master/
|
319
|
+
Next, write the recovery codes page ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/recovery_codes/index.html.erb)). It must be in `app/views/quo_vadis/recovery_codes/index.html.:format`. This shows the recovery codes immediately after TOTP is setup, and immediately after generating fresh recovery codes, but not otherwise.
|
318
320
|
|
319
|
-
Next, write the TOTP challenge page where a user inputs their 6-digit TOTP ([example](https://github.com/airblade/quo_vadis/blob/master/
|
321
|
+
Next, write the TOTP challenge page where a user inputs their 6-digit TOTP ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/totps/challenge.html.erb)). It must be in `app/views/quo_vadis/totps/challenge.html.:format`. It's a good idea to link to the recovery code page (`challenge_recovery_codes_path`) for any user who has lost their authenticator.
|
320
322
|
|
321
|
-
Finally, write the recovery code challenge page where a user inputs one of their recovery codes ([example](https://github.com/airblade/quo_vadis/blob/master/
|
323
|
+
Finally, write the recovery code challenge page where a user inputs one of their recovery codes ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/recovery_codes/challenge.html.erb)). It must be in `app/views/quo_vadis/recovery_codes/challenge.html.:format`. A recovery code can only be used once, and using one deactivates TOTP – so the user will have to set it up again next time.
|
322
324
|
|
323
325
|
|
324
326
|
### Change password
|
325
327
|
|
326
328
|
To change their password, the user must provide their current one as well as the new one.
|
327
329
|
|
328
|
-
Write the change-password form ([example](https://github.com/airblade/quo_vadis/blob/master/
|
330
|
+
Write the change-password form ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/passwords/edit.html.erb)). It must be in `app/views/quo_vadis/passwords/edit.html.:format`.
|
329
331
|
|
330
332
|
After the password has been changed, the user is redirected to the first of:
|
331
333
|
|
@@ -345,17 +347,17 @@ The user can reset their password if they lose it. The flow is:
|
|
345
347
|
4. [Password-reset confirmation page] The user enters their new password and clicks a button.
|
346
348
|
5. QuoVadis sets the user's password and logs them in.
|
347
349
|
|
348
|
-
First, write the page where the user requests a password-reset ([example](https://github.com/airblade/quo_vadis/blob/master/
|
350
|
+
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`. Note it must capture the user's identifier (not email, unless the identifier is email).
|
349
351
|
|
350
352
|
See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
|
351
353
|
|
352
|
-
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/
|
354
|
+
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/password_resets/index.html.erb)). It must be in `app/views/quo_vadis/password_resets/index.html.:format`.
|
353
355
|
|
354
356
|
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.
|
355
357
|
|
356
|
-
|
358
|
+
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.
|
357
359
|
|
358
|
-
|
360
|
+
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`.
|
359
361
|
|
360
362
|
After the user has reset their password, they will be logged in and redirected to the first of these that exists:
|
361
363
|
|
@@ -369,14 +371,14 @@ A logged-in session lasts for either the browser session or `QuoVadis.session_li
|
|
369
371
|
|
370
372
|
A user can view their active sessions and log out of any of them.
|
371
373
|
|
372
|
-
Write the view showing the sessions ([example](https://github.com/airblade/quo_vadis/blob/master/
|
374
|
+
Write the view showing the sessions ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/sessions/index.html.erb)). It must be in `app/views/quo_vadis/sessions/index.html.:format`.
|
373
375
|
|
374
376
|
|
375
377
|
### Audit trail
|
376
378
|
|
377
379
|
An audit trail is kept of authentication events. You can see the full list in the [`Log`](https://github.com/airblade/quo_vadis/blob/master/app/models/quo_vadis/log.rb) class.
|
378
380
|
|
379
|
-
Write the view showing the events ([example](https://github.com/airblade/quo_vadis/blob/master/
|
381
|
+
Write the view showing the events ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/logs/index.html.erb)). It must be in `app/views/quo_vadis/logs/index.html.:format`.
|
380
382
|
|
381
383
|
|
382
384
|
### Notifications
|
@@ -385,18 +387,23 @@ QuoVadis notifies users by email whenever their authentication details are chang
|
|
385
387
|
|
386
388
|
Write the corresponding mailer views:
|
387
389
|
|
388
|
-
- change of email ([example](https://github.com/airblade/quo_vadis/blob/master/
|
389
|
-
- change of identifier (unless the identifier is email) ([example](https://github.com/airblade/quo_vadis/blob/master/
|
390
|
-
- change of password ([example](https://github.com/airblade/quo_vadis/blob/master/
|
391
|
-
- reset of password ([example](https://github.com/airblade/quo_vadis/blob/master/
|
392
|
-
- TOTP setup ([example](https://github.com/airblade/quo_vadis/blob/master/
|
393
|
-
- TOTP code used a second time ([example](https://github.com/airblade/quo_vadis/blob/master/
|
394
|
-
- 2FA deactivated ([example](https://github.com/airblade/quo_vadis/blob/master/
|
395
|
-
- recovery codes generated ([example](https://github.com/airblade/quo_vadis/blob/master/
|
390
|
+
- change of email ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/email_change_notification.text.erb))
|
391
|
+
- change of identifier (unless the identifier is email) ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/identifier_change_notification.text.erb))
|
392
|
+
- change of password ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/password_change_notification.text.erb))
|
393
|
+
- reset of password ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/password_reset_notification.text.erb))
|
394
|
+
- TOTP setup ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/totp_setup_notification.text.erb))
|
395
|
+
- TOTP code used a second time ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb))
|
396
|
+
- 2FA deactivated ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb))
|
397
|
+
- recovery codes generated ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb))
|
396
398
|
|
397
399
|
They must be in `app/views/quo_vadis/mailer/NAME.{text,html}.erb`.
|
398
400
|
|
399
401
|
|
402
|
+
### Revocation
|
403
|
+
|
404
|
+
You can revoke a user's access by calling `#revoke_authentication_credentials` on the model instance. This deletes the user's password, TOTP credential, recovery codes, and active sessions. Their authentication logs, or audit trail, are preserved.
|
405
|
+
|
406
|
+
|
400
407
|
## Configuration
|
401
408
|
|
402
409
|
This is QuoVadis' [default configuration](https://github.com/airblade/quo_vadis/blob/master/lib/quo_vadis/defaults.rb):
|
@@ -405,7 +412,7 @@ This is QuoVadis' [default configuration](https://github.com/airblade/quo_vadis/
|
|
405
412
|
QuoVadis.configure do
|
406
413
|
password_minimum_length 12
|
407
414
|
mask_ips false
|
408
|
-
cookie_name '__Host-qv'
|
415
|
+
cookie_name (Rails.env.production? ? '__Host-qv' : 'qv')
|
409
416
|
session_lifetime :session
|
410
417
|
session_lifetime_extend_to_end_of_day false
|
411
418
|
session_idle_timeout :lifetime
|
@@ -436,7 +443,7 @@ Masking means setting the last octet (IPv4) or the last 80 bits (IPv6) to 0.
|
|
436
443
|
|
437
444
|
__`cookie_name`__ (string)
|
438
445
|
|
439
|
-
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).
|
446
|
+
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).
|
440
447
|
|
441
448
|
__`session_lifetime`__ (`:session` | `ActiveSupport::Duration` | integer)
|
442
449
|
|
@@ -488,13 +495,32 @@ For example, the default login path is at `/login`. If you set `mount_point` to
|
|
488
495
|
|
489
496
|
#### Rails configuration
|
490
497
|
|
498
|
+
__Mailer URLs__
|
499
|
+
|
491
500
|
You must also configure the mailer host so URLs are generated correctly in emails:
|
492
501
|
|
493
502
|
```ruby
|
494
503
|
config.action_mailer.default_url_options: { host: 'example.com' }
|
495
504
|
```
|
496
505
|
|
497
|
-
|
506
|
+
__Layouts__
|
507
|
+
|
508
|
+
You can specify QuoVadis's controllers' layouts in a `#to_prepare` block in your application configuration. For example:
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
# config/application.rb
|
512
|
+
module YourApp
|
513
|
+
class Application < Rails::Application
|
514
|
+
config.to_prepare do
|
515
|
+
QuoVadis::ConfirmationsController.layout 'your_layout'
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
```
|
520
|
+
|
521
|
+
__Routes__
|
522
|
+
|
523
|
+
You can set up your post-authentication and post-password-change routes. If you don't, you must have a root route. For example:
|
498
524
|
|
499
525
|
```ruby
|
500
526
|
# config/routes.rb
|
@@ -5,6 +5,7 @@ module QuoVadis
|
|
5
5
|
|
6
6
|
# holding page
|
7
7
|
def index
|
8
|
+
@account = find_pending_account_from_session
|
8
9
|
end
|
9
10
|
|
10
11
|
|
@@ -45,12 +46,57 @@ module QuoVadis
|
|
45
46
|
end
|
46
47
|
|
47
48
|
account.confirmed!
|
48
|
-
|
49
49
|
qv.log account, Log::ACCOUNT_CONFIRMATION
|
50
50
|
|
51
|
+
session.delete :account_pending_confirmation
|
52
|
+
|
51
53
|
login account.model, true
|
52
54
|
redirect_to qv.path_after_authentication, notice: QuoVadis.translate('flash.confirmation.confirmed')
|
53
55
|
end
|
54
56
|
|
57
|
+
|
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
|
+
def resend
|
84
|
+
account = find_pending_account_from_session
|
85
|
+
|
86
|
+
unless account
|
87
|
+
redirect_to confirmations_path, alert: QuoVadis.translate('flash.confirmation.unknown') and return
|
88
|
+
end
|
89
|
+
|
90
|
+
request_confirmation account.model
|
91
|
+
redirect_to confirmations_path
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def find_pending_account_from_session
|
98
|
+
Account.find(session[:account_pending_confirmation]) if session[:account_pending_confirmation]
|
99
|
+
end
|
100
|
+
|
55
101
|
end
|
56
102
|
end
|
@@ -13,15 +13,14 @@ module QuoVadis
|
|
13
13
|
|
14
14
|
|
15
15
|
def create
|
16
|
-
flash[:notice] = QuoVadis.translate 'flash.password_reset.create'
|
17
|
-
|
18
16
|
account = QuoVadis.find_account_by_identifier_in_params params
|
19
|
-
return unless account
|
20
17
|
|
21
|
-
|
22
|
-
|
18
|
+
if account
|
19
|
+
token = QuoVadis::PasswordResetToken.generate account
|
20
|
+
QuoVadis.deliver :reset_password, email: account.model.email, url: quo_vadis.password_reset_url(token)
|
21
|
+
end
|
23
22
|
|
24
|
-
redirect_to password_resets_path
|
23
|
+
redirect_to password_resets_path, notice: QuoVadis.translate('flash.password_reset.create')
|
25
24
|
end
|
26
25
|
|
27
26
|
|
@@ -33,6 +32,10 @@ module QuoVadis
|
|
33
32
|
redirect_to new_password_reset_path, alert: QuoVadis.translate('flash.password_reset.unknown') and return
|
34
33
|
end
|
35
34
|
|
35
|
+
# Ensure the flash notice set in the create action does not appear when the user clicks the
|
36
|
+
# link in the email they were sent.
|
37
|
+
flash.delete :notice
|
38
|
+
|
36
39
|
@password = QuoVadis::Password.new
|
37
40
|
end
|
38
41
|
|
@@ -46,7 +49,7 @@ module QuoVadis
|
|
46
49
|
end
|
47
50
|
|
48
51
|
@password = account.password
|
49
|
-
if @password.reset params[:password], params[:password_confirmation]
|
52
|
+
if @password.reset params[:password][:password], params[:password][:password_confirmation]
|
50
53
|
# Logout account's sessions because password has changed.
|
51
54
|
# Note model is not logged in here.
|
52
55
|
@password.account.sessions.destroy_all
|
@@ -57,7 +60,7 @@ module QuoVadis
|
|
57
60
|
login @password.account.model, true
|
58
61
|
redirect_to qv.path_after_authentication, notice: QuoVadis.translate('flash.password_reset.reset')
|
59
62
|
else
|
60
|
-
render :edit
|
63
|
+
render :edit, status: :unprocessable_entity
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
@@ -11,14 +11,16 @@ module QuoVadis
|
|
11
11
|
|
12
12
|
def update
|
13
13
|
@password = authenticated_model.qv_account.password
|
14
|
-
if @password.change
|
14
|
+
if @password.change(params[:password][:password],
|
15
|
+
params[:password][:new_password],
|
16
|
+
params[:password][:new_password_confirmation])
|
15
17
|
qv.log authenticated_model.qv_account, Log::PASSWORD_CHANGE
|
16
18
|
QuoVadis.notify :password_change_notification, email: authenticated_model.email
|
17
19
|
qv.logout_other_sessions
|
18
20
|
qv.replace_session
|
19
21
|
redirect_to qv.path_after_password_change, notice: QuoVadis.translate('flash.password.update')
|
20
22
|
else
|
21
|
-
render :edit
|
23
|
+
render :edit, status: :unprocessable_entity
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -21,16 +21,16 @@ module QuoVadis
|
|
21
21
|
account = QuoVadis.find_account_by_identifier_in_params params
|
22
22
|
|
23
23
|
unless account
|
24
|
-
qv.log nil, Log::LOGIN_UNKNOWN
|
24
|
+
qv.log nil, Log::LOGIN_UNKNOWN, identifier: QuoVadis.identifier_value_in_params(params)
|
25
25
|
flash.now[:alert] = QuoVadis.translate 'flash.login.failed'
|
26
|
-
render :new
|
26
|
+
render :new, status: :unprocessable_entity
|
27
27
|
return
|
28
28
|
end
|
29
29
|
|
30
30
|
unless account.password.authenticate params[:password]
|
31
31
|
qv.log account, Log::LOGIN_FAILURE
|
32
32
|
flash.now[:alert] = QuoVadis.translate 'flash.login.failed'
|
33
|
-
render :new
|
33
|
+
render :new, status: :unprocessable_entity
|
34
34
|
return
|
35
35
|
end
|
36
36
|
|
@@ -32,9 +32,23 @@ module QuoVadis
|
|
32
32
|
|
33
33
|
# Returns an array of the recovery codes' codes.
|
34
34
|
def generate_recovery_codes
|
35
|
+
recovery_codes.delete_all
|
35
36
|
Array.new(MAX_NUMBER_OF_RECOVERY_CODES) { recovery_codes.create }.map &:code
|
36
37
|
end
|
37
38
|
|
39
|
+
def revoke
|
40
|
+
password&.destroy
|
41
|
+
totp&.destroy
|
42
|
+
recovery_codes.destroy_all
|
43
|
+
sessions.destroy_all
|
44
|
+
|
45
|
+
Log.create(
|
46
|
+
account: self,
|
47
|
+
action: Log::REVOKE,
|
48
|
+
ip: (CurrentRequestDetails.ip || '')
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
38
52
|
private
|
39
53
|
|
40
54
|
def log_identifier_change
|
data/app/models/quo_vadis/log.rb
CHANGED
@@ -21,7 +21,8 @@ module QuoVadis
|
|
21
21
|
PASSWORD_RESET = 'password.reset'
|
22
22
|
ACCOUNT_CONFIRMATION = 'account.confirmation'
|
23
23
|
LOGOUT_OTHER = 'logout.other'
|
24
|
-
LOGOUT = 'logout'
|
24
|
+
LOGOUT = 'logout.self'
|
25
|
+
REVOKE = 'revoke'
|
25
26
|
|
26
27
|
ACTIONS = [
|
27
28
|
LOGIN_SUCCESS,
|
@@ -41,7 +42,8 @@ module QuoVadis
|
|
41
42
|
PASSWORD_RESET,
|
42
43
|
ACCOUNT_CONFIRMATION,
|
43
44
|
LOGOUT_OTHER,
|
44
|
-
LOGOUT
|
45
|
+
LOGOUT,
|
46
|
+
REVOKE
|
45
47
|
]
|
46
48
|
|
47
49
|
belongs_to :account, optional: true # optional only for LOGIN_UNKNOWN
|
@@ -7,6 +7,7 @@ module QuoVadis
|
|
7
7
|
has_secure_password
|
8
8
|
|
9
9
|
validates_length_of :password, minimum: QuoVadis.password_minimum_length, allow_blank: true
|
10
|
+
validate :password_updated_legitimately, on: :update
|
10
11
|
|
11
12
|
attr_accessor :new_password
|
12
13
|
|
@@ -48,5 +49,20 @@ module QuoVadis
|
|
48
49
|
save
|
49
50
|
end
|
50
51
|
|
52
|
+
private
|
53
|
+
|
54
|
+
def password_updated_legitimately
|
55
|
+
return unless password_digest_changed?
|
56
|
+
|
57
|
+
unless change_or_reset_called?
|
58
|
+
errors.add :password, 'must be updated via #change or #reset'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def change_or_reset_called?
|
63
|
+
caller_locations.any? { |loc|
|
64
|
+
['change', 'reset'].include?(loc.label) && Pathname.new(loc.path).basename.to_s == 'password.rb'
|
65
|
+
}
|
66
|
+
end
|
51
67
|
end
|
52
68
|
end
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h1>Account confirmation: change email</h1>
|
2
|
+
|
3
|
+
<p>Please update your email address.</p>
|
4
|
+
|
5
|
+
<%= form_with url: update_email_confirmations_path, method: :put do |f| %>
|
6
|
+
<p>
|
7
|
+
<%= f.label :email %>
|
8
|
+
<%= f.text_field :email, value: @email, inputmode: 'email', autocomplete: 'email' %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<%= f.submit 'Update my email address and send me a new confirmation email' %>
|
13
|
+
</p>
|
14
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h1>Account confirmation</h1>
|
2
|
+
|
3
|
+
<% if @account %>
|
4
|
+
<p>We have sent an email to <%= @account.model.email %>.</p>
|
5
|
+
|
6
|
+
<p>Wrong address? <%= link_to 'Change it', edit_email_confirmations_path %>.</p>
|
7
|
+
|
8
|
+
<p>Didn't receive it? <%= button_to 'Get another one', resend_confirmations_path %></p>
|
9
|
+
|
10
|
+
<% else %>
|
11
|
+
<p>We have sent an email to you.</p>
|
12
|
+
|
13
|
+
<p><%= link_to 'Request a new email', new_confirmation_path %></p>
|
14
|
+
<% end %>
|
File without changes
|
@@ -3,6 +3,7 @@
|
|
3
3
|
<table>
|
4
4
|
<thead>
|
5
5
|
<tr>
|
6
|
+
<th>Timestamp</th>
|
6
7
|
<th>Action</th>
|
7
8
|
<th>IP</th>
|
8
9
|
<th>Metadata</th>
|
@@ -11,7 +12,8 @@
|
|
11
12
|
<tbody>
|
12
13
|
<% @logs.each do |log| %>
|
13
14
|
<tr>
|
14
|
-
<td><%= log.
|
15
|
+
<td><%= log.created_at %></td>
|
16
|
+
<td><%= QuoVadis.translate "log.action.#{log.action}" %></td>
|
15
17
|
<td><%= log.ip %></td>
|
16
18
|
<td><%= log.metadata.empty? ? '' : log.metadata.map {|k,v| "#{k}: #{v}"}.join(', ') %></td>
|
17
19
|
</tr>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/{test/dummy/app → app}/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,14 +1,6 @@
|
|
1
1
|
<h1>Reset password</h1>
|
2
2
|
|
3
|
-
|
4
|
-
<ul>
|
5
|
-
<% @password.errors.full_messages.each do |msg| %>
|
6
|
-
<li><%= msg %></li>
|
7
|
-
<% end %>
|
8
|
-
</ul>
|
9
|
-
<% end %>
|
10
|
-
|
11
|
-
<%= form_with url: password_reset_path(params[:token]), method: :put do |f| %>
|
3
|
+
<%= form_with model: @password, url: password_reset_path(params[:token]), method: :put do |f| %>
|
12
4
|
<p>
|
13
5
|
<%= f.label :password %>
|
14
6
|
<%= f.password_field :password, autocomplete: 'new-password' %>
|
File without changes
|
File without changes
|