quo_vadis 2.0.2 → 2.1.3
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 +37 -30
- 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 +2 -2
- data/app/controllers/quo_vadis/totps_controller.rb +1 -1
- data/app/mailers/quo_vadis/mailer.rb +16 -16
- 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/{test/dummy/app → app}/views/quo_vadis/confirmations/edit_email.html.erb +0 -0
- data/{test/dummy/app → app}/views/quo_vadis/confirmations/index.html.erb +0 -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 +4 -4
- data/lib/generators/quo_vadis/install_generator.rb +1 -1
- data/lib/quo_vadis/controller.rb +2 -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 +6 -4
- data/test/dummy/config/initializers/quo_vadis.rb +0 -4
- data/test/integration/logging_test.rb +13 -4
- 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/mailers/mailer_test.rb +49 -32
- data/test/models/account_test.rb +24 -2
- data/test/models/model_test.rb +4 -1
- 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 +28 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 707a011642d93c975b94f1c99c157decdec10c8138edf4e3b4dcf884438abac8
|
4
|
+
data.tar.gz: 8b5e4a2396909660b86ff6de1cee25dd574fe6ca1d10ccf1831c51381e61a2e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1aa7cb7ee6c63886f3a8c322f98bb14ea54a4c997d74103e2f4db9f787c65fd3853eb8d771cc7b85c6ea99a1c1a92ee5a7c7db10b29f8d1d1d88f4682f3e8b7
|
7
|
+
data.tar.gz: 88f33eb05eafc2746bc8295f9606f441d99aa24edd422f9a800909008dca1eaded9800d86ec9dbe9b292449b7ec24b8458ec478257885d6f0ea0853ea6e2744c
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,37 @@
|
|
4
4
|
## HEAD
|
5
5
|
|
6
6
|
|
7
|
+
## 2.1.3 (30 September 2021)
|
8
|
+
|
9
|
+
* Pass IP and timestamp as paramenters to mailer.
|
10
|
+
|
11
|
+
|
12
|
+
## 2.1.2 (30 September 2021)
|
13
|
+
|
14
|
+
* Delete existing recovery codes when generating new ones.
|
15
|
+
|
16
|
+
|
17
|
+
## 2.1.1 (8 July 2021)
|
18
|
+
|
19
|
+
* Remove unnecessary route names.
|
20
|
+
* Add user revocation.
|
21
|
+
* Ensure password is only updated via #change or #reset.
|
22
|
+
* Move views into gem's app/views/ directory.
|
23
|
+
|
24
|
+
|
25
|
+
## 2.1.0 (25 June 2021)
|
26
|
+
|
27
|
+
* Do not require password on create.
|
28
|
+
* Fix incorrect assignment of built association.
|
29
|
+
* Add i18n translations for log actions.
|
30
|
+
* Use model instance in change-password form.
|
31
|
+
* Ensure password-reset flash notice not displayed when emailed link is clicked.
|
32
|
+
* Use model instance in password-reset form.
|
33
|
+
* Give no indication of unknown account on request of password reset email.
|
34
|
+
* Use 422 status code for form submission error responses.
|
35
|
+
* Make default cookie name depend on Rails environment.
|
36
|
+
|
37
|
+
|
7
38
|
## 2.0.2 (24 May 2021)
|
8
39
|
|
9
40
|
* Account confirmation: enable updating of email address.
|
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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -23,14 +23,14 @@ module QuoVadis
|
|
23
23
|
unless account
|
24
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
|
|
@@ -14,52 +14,52 @@ module QuoVadis
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def email_change_notification
|
17
|
-
@timestamp =
|
18
|
-
@ip =
|
17
|
+
@timestamp = params[:timestamp]
|
18
|
+
@ip = params[:ip]
|
19
19
|
_mail params[:email], QuoVadis.translate('mailer.notification.email_change')
|
20
20
|
end
|
21
21
|
|
22
22
|
def identifier_change_notification
|
23
|
-
@timestamp =
|
23
|
+
@timestamp = params[:timestamp]
|
24
24
|
@identifier = params[:identifier]
|
25
|
-
@ip =
|
25
|
+
@ip = params[:ip]
|
26
26
|
_mail params[:email], QuoVadis.translate('mailer.notification.identifier_change',
|
27
27
|
identifier: params[:identifier])
|
28
28
|
end
|
29
29
|
|
30
30
|
def password_change_notification
|
31
|
-
@timestamp =
|
32
|
-
@ip =
|
31
|
+
@timestamp = params[:timestamp]
|
32
|
+
@ip = params[:ip]
|
33
33
|
_mail params[:email], QuoVadis.translate('mailer.notification.password_change')
|
34
34
|
end
|
35
35
|
|
36
36
|
def password_reset_notification
|
37
|
-
@timestamp =
|
38
|
-
@ip =
|
37
|
+
@timestamp = params[:timestamp]
|
38
|
+
@ip = params[:ip]
|
39
39
|
_mail params[:email], QuoVadis.translate('mailer.notification.password_reset')
|
40
40
|
end
|
41
41
|
|
42
42
|
def totp_setup_notification
|
43
|
-
@timestamp =
|
44
|
-
@ip =
|
43
|
+
@timestamp = params[:timestamp]
|
44
|
+
@ip = params[:ip]
|
45
45
|
_mail params[:email], QuoVadis.translate('mailer.notification.totp_setup')
|
46
46
|
end
|
47
47
|
|
48
48
|
def totp_reuse_notification
|
49
|
-
@timestamp =
|
50
|
-
@ip =
|
49
|
+
@timestamp = params[:timestamp]
|
50
|
+
@ip = params[:ip]
|
51
51
|
_mail params[:email], QuoVadis.translate('mailer.notification.totp_reuse')
|
52
52
|
end
|
53
53
|
|
54
54
|
def twofa_deactivated_notification
|
55
|
-
@timestamp =
|
56
|
-
@ip =
|
55
|
+
@timestamp = params[:timestamp]
|
56
|
+
@ip = params[:ip]
|
57
57
|
_mail params[:email], QuoVadis.translate('mailer.notification.twofa_deactivated')
|
58
58
|
end
|
59
59
|
|
60
60
|
def recovery_codes_generation_notification
|
61
|
-
@timestamp =
|
62
|
-
@ip =
|
61
|
+
@timestamp = params[:timestamp]
|
62
|
+
@ip = params[:ip]
|
63
63
|
_mail params[:email], QuoVadis.translate('mailer.notification.recovery_codes_generation')
|
64
64
|
end
|
65
65
|
|
@@ -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
|
File without changes
|
File without changes
|
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
|
@@ -1,14 +1,6 @@
|
|
1
1
|
<h1>Change 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_path, method: :put do |f| %>
|
3
|
+
<%= form_with model: @password, url: password_path, method: :put do |f| %>
|
12
4
|
<p>
|
13
5
|
<%= f.label :password %>
|
14
6
|
<%= f.password_field :password, autocomplete: 'current-password' %>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|