quo_vadis 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +31 -26
  4. data/app/controllers/quo_vadis/password_resets_controller.rb +1 -1
  5. data/app/models/quo_vadis/account.rb +13 -0
  6. data/app/models/quo_vadis/log.rb +3 -1
  7. data/app/models/quo_vadis/password.rb +16 -0
  8. data/{test/dummy/app → app}/views/quo_vadis/confirmations/edit.html.erb +0 -0
  9. data/{test/dummy/app → app}/views/quo_vadis/confirmations/edit_email.html.erb +0 -0
  10. data/{test/dummy/app → app}/views/quo_vadis/confirmations/index.html.erb +0 -0
  11. data/{test/dummy/app → app}/views/quo_vadis/confirmations/new.html.erb +0 -0
  12. data/{test/dummy/app → app}/views/quo_vadis/logs/index.html.erb +0 -0
  13. data/{test/dummy/app → app}/views/quo_vadis/mailer/account_confirmation.text.erb +0 -0
  14. data/{test/dummy/app → app}/views/quo_vadis/mailer/email_change_notification.text.erb +0 -0
  15. data/{test/dummy/app → app}/views/quo_vadis/mailer/identifier_change_notification.text.erb +0 -0
  16. data/{test/dummy/app → app}/views/quo_vadis/mailer/password_change_notification.text.erb +0 -0
  17. data/{test/dummy/app → app}/views/quo_vadis/mailer/password_reset_notification.text.erb +0 -0
  18. data/{test/dummy/app → app}/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb +0 -0
  19. data/{test/dummy/app → app}/views/quo_vadis/mailer/reset_password.text.erb +0 -0
  20. data/{test/dummy/app → app}/views/quo_vadis/mailer/totp_reuse_notification.text.erb +0 -0
  21. data/{test/dummy/app → app}/views/quo_vadis/mailer/totp_setup_notification.text.erb +0 -0
  22. data/{test/dummy/app → app}/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb +0 -0
  23. data/{test/dummy/app → app}/views/quo_vadis/password_resets/edit.html.erb +0 -0
  24. data/{test/dummy/app → app}/views/quo_vadis/password_resets/index.html.erb +0 -0
  25. data/{test/dummy/app → app}/views/quo_vadis/password_resets/new.html.erb +0 -0
  26. data/{test/dummy/app → app}/views/quo_vadis/passwords/edit.html.erb +0 -0
  27. data/{test/dummy/app → app}/views/quo_vadis/recovery_codes/challenge.html.erb +0 -0
  28. data/{test/dummy/app → app}/views/quo_vadis/recovery_codes/index.html.erb +0 -0
  29. data/{test/dummy/app → app}/views/quo_vadis/sessions/index.html.erb +0 -0
  30. data/{test/dummy/app → app}/views/quo_vadis/sessions/new.html.erb +0 -0
  31. data/{test/dummy/app → app}/views/quo_vadis/totps/challenge.html.erb +0 -0
  32. data/{test/dummy/app → app}/views/quo_vadis/totps/new.html.erb +0 -0
  33. data/{test/dummy/app → app}/views/quo_vadis/twofas/show.html.erb +0 -0
  34. data/config/locales/quo_vadis.en.yml +1 -0
  35. data/config/routes.rb +4 -4
  36. data/lib/generators/quo_vadis/install_generator.rb +1 -1
  37. data/lib/quo_vadis/controller.rb +1 -1
  38. data/lib/quo_vadis/model.rb +4 -0
  39. data/lib/quo_vadis/version.rb +1 -1
  40. data/test/integration/logging_test.rb +9 -0
  41. data/test/integration/password_reset_test.rb +1 -1
  42. data/test/models/account_test.rb +16 -0
  43. data/test/models/password_test.rb +13 -0
  44. data/test/models/token_test.rb +1 -1
  45. metadata +29 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f8acc907c43d19fcc6f9be59854be5e848add025d427f78ec608b2e24677e73
4
- data.tar.gz: efab0ab6fc83535466f6bb6e31bb056016c5220ef227155629601e5c17aff5b6
3
+ metadata.gz: 455075dcaacc7b730c0834edec721467fd36aed73ab25d55980f2db6baa5226c
4
+ data.tar.gz: 463eafcb85b1da5a09ebf990d38729f9e4a52e60856e83350df1f57fccbdd439
5
5
  SHA512:
6
- metadata.gz: 8d34c8922431d4234650c50718a29b71a328b070582bb5b7fac74d4b1e2b6847bc2b130a197d6dcc05d93d2c0dab882a90fecf9faa3e1620f36acb48b9be3778
7
- data.tar.gz: 4f471b97ff2a2dc0461ddac1f68c1e9f263ac5c188ab89daf2974f3c8f2e85c5e2b586da72b8c798d5f696acf3a2a7079aef57fb689f8f75f8113a38d56b2c2d
6
+ metadata.gz: 3113487a82c77378bec401e4c721923d2de127a0ad790cb96e754e83794dc236e2a8042f33305114f1b85b021ad12d08f9724e1338c3f0f4c1e536853482e93b
7
+ data.tar.gz: 0c03c57428c0642fbd29d0b9ad150e01d72a3bd4ef5eab35243584b4621a6d35aaba218af90b13cb7ef8a66f0ded8c0f02a842899c2ef3561d7b0279f5864a23
data/CHANGELOG.md CHANGED
@@ -1,8 +1,20 @@
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
+
4
15
  ## 2.1.0 (25 June 2021)
5
16
 
17
+ * Do not require password on create.
6
18
  * Fix incorrect assignment of built association.
7
19
  * Add i18n translations for log actions.
8
20
  * Use model instance in change-password form.
data/README.md CHANGED
@@ -246,19 +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/test/dummy/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.
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/test/dummy/app/views/quo_vadis/confirmations/index.html.erb)). It must be in `app/views/quo_vadis/confirmations/index.html.:format`.
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/test/dummy/app/views/quo_vadis/confirmations/edit.html.erb)). It must be in `app/views/quo_vadis/confirmations/edit.html.:format`.
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
- 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/test/dummy/app/views/quo_vadis/confirmations/edit_email.html.erb)). It must be in `app/views/quo_vadis/confirmations/edit_email.html.:format`.
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
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/test/dummy/app/views/quo_vadis/confirmations/new.html.erb)). It must be in `app/views/quo_vadis/confirmations/new.html.:format`.
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`.
262
262
 
263
263
  After the user has confirmed their account, they will be logged in and redirected to the first of these that exists:
264
264
 
@@ -272,7 +272,7 @@ So add whichever works best for you.
272
272
 
273
273
  Use `before_action :require_password_authentication` or `before_action :require_authentication` in your controllers.
274
274
 
275
- Write the login view ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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).
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).
276
276
 
277
277
  If you include a `remember` checkbox in your login form:
278
278
 
@@ -312,22 +312,22 @@ In your views, have a link where users can manage their 2FA:
312
312
  link_to '2FA', quo_vadis.twofa_path
313
313
  ```
314
314
 
315
- Write the 2FA overview page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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.
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.
316
316
 
317
- Next, write the TOTP setup page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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.
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.
318
318
 
319
- Next, write the recovery codes page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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.
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.
320
320
 
321
- Next, write the TOTP challenge page where a user inputs their 6-digit TOTP ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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.
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.
322
322
 
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/test/dummy/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.
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.
324
324
 
325
325
 
326
326
  ### Change password
327
327
 
328
328
  To change their password, the user must provide their current one as well as the new one.
329
329
 
330
- Write the change-password form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/passwords/edit.html.erb)). It must be in `app/views/quo_vadis/passwords/edit.html.:format`.
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`.
331
331
 
332
332
  After the password has been changed, the user is redirected to the first of:
333
333
 
@@ -347,17 +347,17 @@ The user can reset their password if they lose it. The flow is:
347
347
  4. [Password-reset confirmation page] The user enters their new password and clicks a button.
348
348
  5. QuoVadis sets the user's password and logs them in.
349
349
 
350
- First, write the page where the user requests a password-reset ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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).
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).
351
351
 
352
352
  See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
353
353
 
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/test/dummy/app/views/quo_vadis/password_resets/index.html.erb)). It must be in `app/views/quo_vadis/password_resets/index.html.:format`.
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`.
355
355
 
356
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.
357
357
 
358
- Now write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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.
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.
359
359
 
360
- Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/password_resets/edit.html.erb)). It must be in `app/views/quo_vadis/password_resets/edit.html.:format`.
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`.
361
361
 
362
362
  After the user has reset their password, they will be logged in and redirected to the first of these that exists:
363
363
 
@@ -371,14 +371,14 @@ A logged-in session lasts for either the browser session or `QuoVadis.session_li
371
371
 
372
372
  A user can view their active sessions and log out of any of them.
373
373
 
374
- Write the view showing the sessions ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/sessions/index.html.erb)). It must be in `app/views/quo_vadis/sessions/index.html.:format`.
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`.
375
375
 
376
376
 
377
377
  ### Audit trail
378
378
 
379
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.
380
380
 
381
- Write the view showing the events ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/logs/index.html.erb)). It must be in `app/views/quo_vadis/logs/index.html.:format`.
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`.
382
382
 
383
383
 
384
384
  ### Notifications
@@ -387,18 +387,23 @@ QuoVadis notifies users by email whenever their authentication details are chang
387
387
 
388
388
  Write the corresponding mailer views:
389
389
 
390
- - change of email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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/test/dummy/app/views/quo_vadis/mailer/identifier_change_notification.text.erb))
392
- - change of password ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/password_change_notification.text.erb))
393
- - reset of password ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/password_reset_notification.text.erb))
394
- - TOTP setup ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/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/test/dummy/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb))
396
- - 2FA deactivated ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb))
397
- - recovery codes generated ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb))
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))
398
398
 
399
399
  They must be in `app/views/quo_vadis/mailer/NAME.{text,html}.erb`.
400
400
 
401
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
+
402
407
  ## Configuration
403
408
 
404
409
  This is QuoVadis' [default configuration](https://github.com/airblade/quo_vadis/blob/master/lib/quo_vadis/defaults.rb):
@@ -17,7 +17,7 @@ module QuoVadis
17
17
 
18
18
  if account
19
19
  token = QuoVadis::PasswordResetToken.generate account
20
- QuoVadis.deliver :reset_password, email: account.model.email, url: quo_vadis.edit_password_reset_url(token)
20
+ QuoVadis.deliver :reset_password, email: account.model.email, url: quo_vadis.password_reset_url(token)
21
21
  end
22
22
 
23
23
  redirect_to password_resets_path, notice: QuoVadis.translate('flash.password_reset.create')
@@ -35,6 +35,19 @@ module QuoVadis
35
35
  Array.new(MAX_NUMBER_OF_RECOVERY_CODES) { recovery_codes.create }.map &:code
36
36
  end
37
37
 
38
+ def revoke
39
+ password&.destroy
40
+ totp&.destroy
41
+ recovery_codes.destroy_all
42
+ sessions.destroy_all
43
+
44
+ Log.create(
45
+ account: self,
46
+ action: Log::REVOKE,
47
+ ip: (CurrentRequestDetails.ip || '')
48
+ )
49
+ end
50
+
38
51
  private
39
52
 
40
53
  def log_identifier_change
@@ -22,6 +22,7 @@ module QuoVadis
22
22
  ACCOUNT_CONFIRMATION = 'account.confirmation'
23
23
  LOGOUT_OTHER = 'logout.other'
24
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
@@ -74,6 +74,7 @@ en:
74
74
  logout:
75
75
  self: Logged out
76
76
  other: Logged out session remotely
77
+ revoke: Revoked access
77
78
  activerecord:
78
79
  errors:
79
80
  models:
data/config/routes.rb CHANGED
@@ -12,8 +12,8 @@ QuoVadis::Engine.routes.draw do
12
12
  resource :password, only: [:edit, :update]
13
13
 
14
14
  resources :password_resets, only: [:new, :create, :index]
15
- get '/pwd-reset/:token', to: 'password_resets#edit', as: 'edit_password_reset'
16
- put '/pwd-reset/:token', to: 'password_resets#update', as: 'password_reset'
15
+ get '/pwd-reset/:token', to: 'password_resets#edit', as: 'password_reset'
16
+ put '/pwd-reset/:token', to: 'password_resets#update'
17
17
 
18
18
  resources :confirmations, only: [:new, :create, :index] do
19
19
  collection do
@@ -22,8 +22,8 @@ QuoVadis::Engine.routes.draw do
22
22
  post :resend
23
23
  end
24
24
  end
25
- get '/confirm/:token', to: 'confirmations#edit', as: 'edit_confirmation'
26
- put '/confirm/:token', to: 'confirmations#update', as: 'confirmation'
25
+ get '/confirm/:token', to: 'confirmations#edit', as: 'confirmation'
26
+ put '/confirm/:token', to: 'confirmations#update'
27
27
 
28
28
  resources :totps, only: [:new, :create] do
29
29
  collection do
@@ -1,6 +1,6 @@
1
1
  module QuoVadis
2
2
  class InstallGenerator < Rails::Generators::Base
3
- source_root Pathname.new(__dir__) / '..' / '..' / '..' / 'test' / 'dummy' / 'app' / 'views' / 'quo_vadis'
3
+ source_root Pathname.new(__dir__) / '..' / '..' / '..' / 'app' / 'views' / 'quo_vadis'
4
4
 
5
5
  desc "Copy QuoVadis' views into your app."
6
6
  def copy_views
@@ -87,7 +87,7 @@ module QuoVadis
87
87
 
88
88
  def request_confirmation(model)
89
89
  token = QuoVadis::AccountConfirmationToken.generate model.qv_account
90
- QuoVadis.deliver :account_confirmation, email: model.email, url: quo_vadis.edit_confirmation_url(token)
90
+ QuoVadis.deliver :account_confirmation, email: model.email, url: quo_vadis.confirmation_url(token)
91
91
  session[:account_pending_confirmation] = model.qv_account.id
92
92
 
93
93
  flash[:notice] = QuoVadis.translate 'flash.confirmation.create'
@@ -53,6 +53,10 @@ module QuoVadis
53
53
  (qv_account.password || qv_account.build_password).password_confirmation = val
54
54
  end
55
55
 
56
+ def revoke_authentication_credentials
57
+ qv_account.revoke
58
+ end
59
+
56
60
  private
57
61
 
58
62
  def qv_copy_password_errors
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuoVadis
4
- VERSION = '2.1.0'
4
+ VERSION = '2.1.1'
5
5
  end
@@ -201,6 +201,15 @@ class LoggingTest < IntegrationTest
201
201
  end
202
202
 
203
203
 
204
+ test 'revoke' do
205
+ login_new_session
206
+ assert_log QuoVadis::Log::REVOKE do
207
+ QuoVadis::CurrentRequestDetails.ip = '127.0.0.1' # fake out the IP assertion
208
+ @account.revoke
209
+ end
210
+ end
211
+
212
+
204
213
  private
205
214
 
206
215
  def assert_log(action, metadata = {}, account = @account, &block)
@@ -56,7 +56,7 @@ class PasswordResetTest < IntegrationTest
56
56
  put quo_vadis.password_reset_path(extract_token_from_email, password: {password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx'})
57
57
  assert controller.logged_in?
58
58
 
59
- get quo_vadis.edit_password_reset_url(extract_token_from_email)
59
+ get quo_vadis.password_reset_url(extract_token_from_email)
60
60
  assert_redirected_to quo_vadis.new_password_reset_path
61
61
  assert_equal 'Either the link has expired or you have already reset your password.', flash[:alert]
62
62
  end
@@ -31,4 +31,20 @@ class AccountTest < ActiveSupport::TestCase
31
31
  end
32
32
  end
33
33
 
34
+
35
+ test 'revoke' do
36
+ u = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
37
+ account = u.qv_account
38
+ account.create_totp last_used_at: 1.minute.ago
39
+ account.generate_recovery_codes
40
+
41
+ u.revoke_authentication_credentials
42
+ account.reload
43
+
44
+ assert_nil account.password
45
+ assert_nil account.totp
46
+ assert_empty account.recovery_codes
47
+ assert_empty account.sessions
48
+ end
49
+
34
50
  end
@@ -120,6 +120,19 @@ class PasswordTest < ActiveSupport::TestCase
120
120
  end
121
121
 
122
122
 
123
+ test 'passwords can only be updated via #change and #reset' do
124
+ user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
125
+ pw = user.qv_account.password
126
+
127
+ refute pw.update password: 'secretsecret'
128
+ assert_equal ["must be updated via #change or #reset"], pw.errors[:password]
129
+
130
+ pw.password = VALID_PASSWORD
131
+ refute pw.save
132
+ assert_equal ["must be updated via #change or #reset"], pw.errors[:password]
133
+ end
134
+
135
+
123
136
  test 'cascade destroy' do
124
137
  user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
125
138
  assert user.qv_account.persisted?
@@ -52,7 +52,7 @@ class TokenTest < ActiveSupport::TestCase
52
52
 
53
53
  test 'password reset already done' do
54
54
  token = QuoVadis::PasswordResetToken.generate @account
55
- @account.password.update password: 'secretsecret'
55
+ @account.password.reset 'secretsecret', 'secretsecret'
56
56
  assert_nil QuoVadis::PasswordResetToken.find_account(token)
57
57
  end
58
58
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quo_vadis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Stewart
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-25 00:00:00.000000000 Z
11
+ date: 2021-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -97,6 +97,32 @@ files:
97
97
  - app/models/quo_vadis/session.rb
98
98
  - app/models/quo_vadis/token.rb
99
99
  - app/models/quo_vadis/totp.rb
100
+ - app/views/quo_vadis/confirmations/edit.html.erb
101
+ - app/views/quo_vadis/confirmations/edit_email.html.erb
102
+ - app/views/quo_vadis/confirmations/index.html.erb
103
+ - app/views/quo_vadis/confirmations/new.html.erb
104
+ - app/views/quo_vadis/logs/index.html.erb
105
+ - app/views/quo_vadis/mailer/account_confirmation.text.erb
106
+ - app/views/quo_vadis/mailer/email_change_notification.text.erb
107
+ - app/views/quo_vadis/mailer/identifier_change_notification.text.erb
108
+ - app/views/quo_vadis/mailer/password_change_notification.text.erb
109
+ - app/views/quo_vadis/mailer/password_reset_notification.text.erb
110
+ - app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb
111
+ - app/views/quo_vadis/mailer/reset_password.text.erb
112
+ - app/views/quo_vadis/mailer/totp_reuse_notification.text.erb
113
+ - app/views/quo_vadis/mailer/totp_setup_notification.text.erb
114
+ - app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb
115
+ - app/views/quo_vadis/password_resets/edit.html.erb
116
+ - app/views/quo_vadis/password_resets/index.html.erb
117
+ - app/views/quo_vadis/password_resets/new.html.erb
118
+ - app/views/quo_vadis/passwords/edit.html.erb
119
+ - app/views/quo_vadis/recovery_codes/challenge.html.erb
120
+ - app/views/quo_vadis/recovery_codes/index.html.erb
121
+ - app/views/quo_vadis/sessions/index.html.erb
122
+ - app/views/quo_vadis/sessions/new.html.erb
123
+ - app/views/quo_vadis/totps/challenge.html.erb
124
+ - app/views/quo_vadis/totps/new.html.erb
125
+ - app/views/quo_vadis/twofas/show.html.erb
100
126
  - bin/console
101
127
  - bin/rails
102
128
  - bin/setup
@@ -131,32 +157,6 @@ files:
131
157
  - test/dummy/app/views/articles/secret.html.erb
132
158
  - test/dummy/app/views/articles/very_secret.html.erb
133
159
  - test/dummy/app/views/layouts/application.html.erb
134
- - test/dummy/app/views/quo_vadis/confirmations/edit.html.erb
135
- - test/dummy/app/views/quo_vadis/confirmations/edit_email.html.erb
136
- - test/dummy/app/views/quo_vadis/confirmations/index.html.erb
137
- - test/dummy/app/views/quo_vadis/confirmations/new.html.erb
138
- - test/dummy/app/views/quo_vadis/logs/index.html.erb
139
- - test/dummy/app/views/quo_vadis/mailer/account_confirmation.text.erb
140
- - test/dummy/app/views/quo_vadis/mailer/email_change_notification.text.erb
141
- - test/dummy/app/views/quo_vadis/mailer/identifier_change_notification.text.erb
142
- - test/dummy/app/views/quo_vadis/mailer/password_change_notification.text.erb
143
- - test/dummy/app/views/quo_vadis/mailer/password_reset_notification.text.erb
144
- - test/dummy/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb
145
- - test/dummy/app/views/quo_vadis/mailer/reset_password.text.erb
146
- - test/dummy/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb
147
- - test/dummy/app/views/quo_vadis/mailer/totp_setup_notification.text.erb
148
- - test/dummy/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb
149
- - test/dummy/app/views/quo_vadis/password_resets/edit.html.erb
150
- - test/dummy/app/views/quo_vadis/password_resets/index.html.erb
151
- - test/dummy/app/views/quo_vadis/password_resets/new.html.erb
152
- - test/dummy/app/views/quo_vadis/passwords/edit.html.erb
153
- - test/dummy/app/views/quo_vadis/recovery_codes/challenge.html.erb
154
- - test/dummy/app/views/quo_vadis/recovery_codes/index.html.erb
155
- - test/dummy/app/views/quo_vadis/sessions/index.html.erb
156
- - test/dummy/app/views/quo_vadis/sessions/new.html.erb
157
- - test/dummy/app/views/quo_vadis/totps/challenge.html.erb
158
- - test/dummy/app/views/quo_vadis/totps/new.html.erb
159
- - test/dummy/app/views/quo_vadis/twofas/show.html.erb
160
160
  - test/dummy/app/views/sign_ups/new.html.erb
161
161
  - test/dummy/app/views/sign_ups/show.html.erb
162
162
  - test/dummy/app/views/users/new.html.erb
@@ -224,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
224
  - !ruby/object:Gem::Version
225
225
  version: '0'
226
226
  requirements: []
227
- rubygems_version: 3.1.4
227
+ rubygems_version: 3.1.2
228
228
  signing_key:
229
229
  specification_version: 4
230
230
  summary: Multifactor authentication for Rails 6.