quo_vadis 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aca902d10c618abd2c9c0461aac204236b4ef7565911aafb5bb12dfe1e3b25e1
4
- data.tar.gz: 3417365d9ca59d5e17a1b28118d3c9ec439b77fb228da9968512786dd3797a12
3
+ metadata.gz: 4f8acc907c43d19fcc6f9be59854be5e848add025d427f78ec608b2e24677e73
4
+ data.tar.gz: efab0ab6fc83535466f6bb6e31bb056016c5220ef227155629601e5c17aff5b6
5
5
  SHA512:
6
- metadata.gz: 5ffdeced9bd86b0a64fe21f3491f3ad03cb2098f0687b663ab794c37e278383ad235df856d8a18bf8e73108d602661990992dd033cbc1358849b8824ac25aff6
7
- data.tar.gz: 59c52478f7a5bc8ee0befe682ee520feec894c6fefe875cc84395e827ae62f9cbf5eb588862cf8eb3888b5b0ba102700b61f5d401156dc9eff893e05307d1787
6
+ metadata.gz: 8d34c8922431d4234650c50718a29b71a328b070582bb5b7fac74d4b1e2b6847bc2b130a197d6dcc05d93d2c0dab882a90fecf9faa3e1620f36acb48b9be3778
7
+ data.tar.gz: 4f471b97ff2a2dc0461ddac1f68c1e9f263ac5c188ab89daf2974f3c8f2e85c5e2b586da72b8c798d5f696acf3a2a7079aef57fb689f8f75f8113a38d56b2c2d
data/CHANGELOG.md CHANGED
@@ -1,7 +1,16 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
- ## HEAD
4
+ ## 2.1.0 (25 June 2021)
5
+
6
+ * Fix incorrect assignment of built association.
7
+ * Add i18n translations for log actions.
8
+ * Use model instance in change-password form.
9
+ * Ensure password-reset flash notice not displayed when emailed link is clicked.
10
+ * Use model instance in password-reset form.
11
+ * Give no indication of unknown account on request of password reset email.
12
+ * Use 422 status code for form submission error responses.
13
+ * Make default cookie name depend on Rails environment.
5
14
 
6
15
 
7
16
  ## 2.0.2 (24 May 2021)
data/README.md CHANGED
@@ -86,9 +86,9 @@ class Person < ApplicationRecord
86
86
  end
87
87
  ```
88
88
 
89
- When __creating__ a model instance, include a `:password` attribute and, optionally, `:password_confirmation` attribute.
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
- When __updating__ a model instance, do not include a `:password` attribute. To change someone's password, use the Change Password feature (see below).
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
 
@@ -256,6 +256,8 @@ On that page you can show the user the address the email was sent to, enable the
256
256
 
257
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`.
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`.
260
+
259
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`.
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:
@@ -353,9 +355,9 @@ Now write the page to where the user is redirected while they wait for the email
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
- 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`.
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.
357
359
 
358
- Finally, write the page where people can put in their identifier (not their email, unless the identifier is email) again to request another password-reset email ([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`.
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`.
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
 
@@ -405,7 +407,7 @@ This is QuoVadis' [default configuration](https://github.com/airblade/quo_vadis/
405
407
  QuoVadis.configure do
406
408
  password_minimum_length 12
407
409
  mask_ips false
408
- cookie_name '__Host-qv'
410
+ cookie_name (Rails.env.production? ? '__Host-qv' : 'qv')
409
411
  session_lifetime :session
410
412
  session_lifetime_extend_to_end_of_day false
411
413
  session_idle_timeout :lifetime
@@ -436,7 +438,7 @@ Masking means setting the last octet (IPv4) or the last 80 bits (IPv6) to 0.
436
438
 
437
439
  __`cookie_name`__ (string)
438
440
 
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).
441
+ 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
442
 
441
443
  __`session_lifetime`__ (`:session` | `ActiveSupport::Duration` | integer)
442
444
 
@@ -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
- token = QuoVadis::PasswordResetToken.generate account
22
- QuoVadis.deliver :reset_password, email: account.model.email, url: quo_vadis.edit_password_reset_url(token)
18
+ if account
19
+ token = QuoVadis::PasswordResetToken.generate account
20
+ QuoVadis.deliver :reset_password, email: account.model.email, url: quo_vadis.edit_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 params[:password], params[:new_password], params[:new_password_confirmation]
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
 
@@ -27,7 +27,7 @@ module QuoVadis
27
27
  else
28
28
  qv.log account, Log::RECOVERY_CODE_FAILURE
29
29
  flash.now[:alert] = QuoVadis.translate('flash.recovery_code.unverified')
30
- render :challenge
30
+ render :challenge, status: :unprocessable_entity
31
31
  end
32
32
  end
33
33
 
@@ -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
 
@@ -53,7 +53,7 @@ module QuoVadis
53
53
  qv.log authenticated_model.qv_account, Log::TOTP_FAILURE
54
54
  end
55
55
  flash.now[:alert] = QuoVadis.translate('flash.totp.unverified')
56
- render :challenge
56
+ render :challenge, status: :unprocessable_entity
57
57
  end
58
58
  end
59
59
 
@@ -21,7 +21,7 @@ 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
25
 
26
26
  ACTIONS = [
27
27
  LOGIN_SUCCESS,
@@ -31,7 +31,6 @@ en:
31
31
  other: You have %{count} recovery codes left.
32
32
  2fa:
33
33
  invalidated: You have invalidated your 2FA credentials and recovery codes.
34
-
35
34
  mailer:
36
35
  password_reset:
37
36
  subject: Change your password
@@ -46,6 +45,35 @@ en:
46
45
  totp_reuse: Your two-factor authentication code was reused just now
47
46
  twofa_deactivated: Two-factor authentication was deactivated just now
48
47
  recovery_codes_generation: Recovery codes have been generated for your account
48
+ log:
49
+ action:
50
+ login:
51
+ success: Logged in
52
+ failure: Failed login attempt (incorrect password)
53
+ unknown: Failed login attempt (unknown identifier)
54
+ totp:
55
+ setup: TOTP set up for 2FA
56
+ success: Authenticated via TOTP
57
+ failure: Failed authentication attempt via TOTP
58
+ reuse: Failed attempt to reuse TOTP code
59
+ recovery_code:
60
+ success: Authenticated via 2FA recovery code
61
+ failure: Failed authentication attempt via 2FA recovery code
62
+ generate: Generated new 2FA recovery codes
63
+ 2fa:
64
+ deactivated: Deactivated 2FA
65
+ identifier:
66
+ change: Changed identifier
67
+ email:
68
+ change: Changed email address
69
+ password:
70
+ change: Changed password
71
+ reset: Reset password
72
+ account:
73
+ confirmation: Confirmed account
74
+ logout:
75
+ self: Logged out
76
+ other: Logged out session remotely
49
77
  activerecord:
50
78
  errors:
51
79
  models:
@@ -17,9 +17,8 @@ module QuoVadis
17
17
 
18
18
  def require_password_authentication
19
19
  return if logged_in?
20
- flash[:notice] = QuoVadis.translate 'flash.require_authentication'
21
20
  session[:qv_bookmark] = request.original_fullpath
22
- redirect_to quo_vadis.login_path
21
+ redirect_to quo_vadis.login_path, notice: QuoVadis.translate('flash.require_authentication')
23
22
  end
24
23
  alias_method :require_authentication, :require_password_authentication
25
24
 
@@ -3,7 +3,7 @@ require 'active_support/core_ext'
3
3
  QuoVadis.configure do
4
4
  password_minimum_length 12
5
5
  mask_ips false
6
- cookie_name '__Host-qv'
6
+ cookie_name (Rails.env.production? ? '__Host-qv' : 'qv')
7
7
  session_lifetime :session
8
8
  session_lifetime_extend_to_end_of_day false
9
9
  session_idle_timeout :lifetime
@@ -14,11 +14,9 @@ module QuoVadis
14
14
 
15
15
  has_one :qv_account, as: :model, class_name: 'QuoVadis::Account', dependent: :destroy, autosave: true
16
16
 
17
- before_validation :qv_build_account_and_password, on: :create
18
- before_validation :qv_copy_identifier_to_account
17
+ before_validation :qv_copy_identifier_to_account, if: Proc.new { |m| m.qv_account }
19
18
 
20
- # Enable a new-user form to set a password.
21
- validate :qv_copy_password_errors, on: :create
19
+ validate :qv_copy_password_errors, if: Proc.new { |m| m.qv_account&.password }
22
20
 
23
21
  unless validators_on(identifier).any? { |v| ActiveRecord::Validations::UniquenessValidator === v }
24
22
  raise NotImplementedError, <<~END
@@ -44,24 +42,19 @@ module QuoVadis
44
42
 
45
43
  def password=(val)
46
44
  @password = val
47
- self.qv_account ||= build_qv_account
45
+ build_qv_account unless qv_account
48
46
  raise PasswordExistsError if qv_account.password&.persisted?
49
47
  (qv_account.password || qv_account.build_password).password = val
50
48
  end
51
49
 
52
50
  def password_confirmation=(val)
53
51
  @password_confirmation = val
54
- self.qv_account ||= build_qv_account
52
+ build_qv_account unless qv_account
55
53
  (qv_account.password || qv_account.build_password).password_confirmation = val
56
54
  end
57
55
 
58
56
  private
59
57
 
60
- def qv_build_account_and_password
61
- self.qv_account ||= build_qv_account
62
- qv_account.password || qv_account.build_password
63
- end
64
-
65
58
  def qv_copy_password_errors
66
59
  qv_account.password.valid? # force qv_account.password to validate
67
60
  qv_account.password.errors[:password ].each { |message| errors.add :password, message }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuoVadis
4
- VERSION = '2.0.2'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -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.action %></td>
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>
@@ -1,14 +1,6 @@
1
1
  <h1>Reset password</h1>
2
2
 
3
- <% if @password.errors.any? %>
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' %>
@@ -1,14 +1,6 @@
1
1
  <h1>Change password</h1>
2
2
 
3
- <% if @password.errors.any? %>
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' %>
@@ -1,7 +1,3 @@
1
1
  QuoVadis.configure do
2
- # Cannot use the __Host- prefix in a non-SSL environment.
3
- # https://tools.ietf.org/html/draft-west-cookie-prefixes-05#section-3.2
4
- cookie_name 'qv'
5
-
6
2
  session_lifetime 1.week
7
3
  end
@@ -15,13 +15,13 @@ class LoggingTest < IntegrationTest
15
15
  assert_response :success
16
16
 
17
17
  assert_select 'tbody tr' do
18
- assert_select 'td', 'password.change'
18
+ assert_select 'td', 'Changed password'
19
19
  assert_select 'td', '1.2.3.4'
20
20
  assert_select 'td', 'foo: bar, baz: qux'
21
21
  end
22
22
 
23
23
  assert_select 'tbody tr' do
24
- assert_select 'td', 'login.success'
24
+ assert_select 'td', 'Logged in'
25
25
  assert_select 'td', '127.0.0.1'
26
26
  assert_select 'td', ''
27
27
  end
@@ -157,7 +157,7 @@ class LoggingTest < IntegrationTest
157
157
  test 'password.change' do
158
158
  login
159
159
  assert_log QuoVadis::Log::PASSWORD_CHANGE do
160
- put quo_vadis.password_path(password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx')
160
+ put quo_vadis.password_path(password: {password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx'})
161
161
  end
162
162
  end
163
163
 
@@ -165,7 +165,7 @@ class LoggingTest < IntegrationTest
165
165
  test 'password.reset' do
166
166
  assert_difference 'QuoVadis::Log.count', 2 do
167
167
  token = QuoVadis::PasswordResetToken.generate @account
168
- put quo_vadis.password_reset_path(token, password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx')
168
+ put quo_vadis.password_reset_path(token, password: {password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx'})
169
169
  end
170
170
  assert_equal QuoVadis::Log::PASSWORD_RESET, QuoVadis::Log.first.action
171
171
  assert_equal QuoVadis::Log::LOGIN_SUCCESS, log.action
@@ -18,29 +18,31 @@ class PasswordChangeTest < IntegrationTest
18
18
 
19
19
 
20
20
  test 'incorrect password' do
21
- put quo_vadis.password_path(password: 'x')
22
- assert_response :success
21
+ put quo_vadis.password_path(password: {password: 'x'})
22
+ assert_response :unprocessable_entity
23
23
  assert_equal ['is incorrect'], password_instance.errors[:password]
24
24
  end
25
25
 
26
26
 
27
27
  test 'new password empty' do
28
- put quo_vadis.password_path(password: '123456789abc', new_password: '')
29
- assert_response :success
28
+ put quo_vadis.password_path(password: {password: '123456789abc', new_password: ''})
29
+ assert_response :unprocessable_entity
30
30
  assert_equal ["can't be blank"], password_instance.errors[:new_password]
31
31
  end
32
32
 
33
33
 
34
34
  test 'new password too short' do
35
- put quo_vadis.password_path(password: '123456789abc', new_password: 'x')
36
- assert_response :success
35
+ put quo_vadis.password_path(password: {password: '123456789abc', new_password: 'x'})
36
+ assert_response :unprocessable_entity
37
37
  assert_equal ["is too short (minimum is #{QuoVadis.password_minimum_length} characters)"], password_instance.errors[:new_password]
38
38
  end
39
39
 
40
40
 
41
41
  test 'new password confirmation does not match' do
42
- put quo_vadis.password_path(password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'y')
43
- assert_response :success
42
+ put quo_vadis.password_path(password: {
43
+ password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'y'
44
+ })
45
+ assert_response :unprocessable_entity
44
46
  assert_equal ["doesn't match Password"], password_instance.errors[:new_password_confirmation]
45
47
  end
46
48
 
@@ -48,7 +50,9 @@ class PasswordChangeTest < IntegrationTest
48
50
  test 'success' do
49
51
  assert_emails 1 do
50
52
  assert_session_replaced do
51
- put quo_vadis.password_path(password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx')
53
+ put quo_vadis.password_path(password: {
54
+ password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx'
55
+ })
52
56
  assert_response :redirect
53
57
  assert_equal 'Your password has been changed.', flash[:notice]
54
58
  end
@@ -60,7 +64,9 @@ class PasswordChangeTest < IntegrationTest
60
64
  desktop = session_login
61
65
  phone = session_login
62
66
 
63
- desktop.put quo_vadis.password_path(password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx')
67
+ desktop.put quo_vadis.password_path(password: {
68
+ password: '123456789abc', new_password: 'xxxxxxxxxxxx', new_password_confirmation: 'xxxxxxxxxxxx'
69
+ })
64
70
  desktop.follow_redirect!
65
71
  assert desktop.controller.logged_in?
66
72
 
@@ -21,6 +21,7 @@ class PasswordLoginTest < IntegrationTest
21
21
 
22
22
  test 'successful login redirects to original path' do
23
23
  get also_secret_articles_path
24
+ refute_nil session[:qv_bookmark]
24
25
 
25
26
  User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
26
27
  post quo_vadis.login_path(email: 'bob@example.com', password: '123456789abc')
@@ -30,11 +31,22 @@ class PasswordLoginTest < IntegrationTest
30
31
  end
31
32
 
32
33
 
34
+ test 'successful login does not redirect to login path' do
35
+ get quo_vadis.login_path
36
+ assert_nil session[:qv_bookmark]
37
+
38
+ User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
39
+ post quo_vadis.login_path(email: 'bob@example.com', password: '123456789abc')
40
+
41
+ assert_redirected_to after_login_path
42
+ end
43
+
44
+
33
45
  test 'failed login' do
34
46
  User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
35
47
  post quo_vadis.login_path(email: 'bob@example.com', password: 'wrong')
36
48
 
37
- assert_response :success
49
+ assert_response :unprocessable_entity
38
50
  assert_equal quo_vadis.login_path, path
39
51
  end
40
52
 
@@ -42,7 +54,7 @@ class PasswordLoginTest < IntegrationTest
42
54
  test 'unknown login' do
43
55
  post quo_vadis.login_path(email: 'bob@example.com', password: 'wrong')
44
56
 
45
- assert_response :success
57
+ assert_response :unprocessable_entity
46
58
  assert_equal quo_vadis.login_path, path
47
59
  end
48
60
 
@@ -15,7 +15,7 @@ class PasswordResetTest < IntegrationTest
15
15
 
16
16
  test 'unknown identifier' do
17
17
  post quo_vadis.password_resets_path(email: 'foo@example.com')
18
- assert_response :success
18
+ assert_redirected_to quo_vadis.password_resets_path
19
19
  assert_equal 'A link to change your password has been emailed to you.', flash[:notice]
20
20
  end
21
21
 
@@ -53,7 +53,7 @@ class PasswordResetTest < IntegrationTest
53
53
  assert_emails 1 do
54
54
  post quo_vadis.password_resets_path(email: 'bob@example.com')
55
55
  end
56
- put quo_vadis.password_reset_path(extract_token_from_email, password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx')
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
59
  get quo_vadis.edit_password_reset_url(extract_token_from_email)
@@ -70,11 +70,11 @@ class PasswordResetTest < IntegrationTest
70
70
  end
71
71
 
72
72
  assert_no_difference 'QuoVadis::Session.count' do
73
- put quo_vadis.password_reset_path(extract_token_from_email, password: '', password_confirmation: '')
73
+ put quo_vadis.password_reset_path(extract_token_from_email, password: {password: '', password_confirmation: ''})
74
74
  end
75
75
 
76
76
  assert_equal digest, @user.qv_account.password.reload.password_digest
77
- assert_response :success
77
+ assert_response :unprocessable_entity
78
78
  assert_equal quo_vadis.password_reset_path(extract_token_from_email), path
79
79
  end
80
80
 
@@ -95,7 +95,7 @@ class PasswordResetTest < IntegrationTest
95
95
  end
96
96
 
97
97
  assert_difference 'QuoVadis::Session.count', (- 2 + 1) do
98
- put quo_vadis.password_reset_path(extract_token_from_email, password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx')
98
+ put quo_vadis.password_reset_path(extract_token_from_email, password: {password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx'})
99
99
  end
100
100
 
101
101
  assert controller.logged_in?
@@ -74,7 +74,7 @@ class TotpsTest < IntegrationTest
74
74
 
75
75
  post quo_vadis.authenticate_totps_path(totp: '123456')
76
76
  refute QuoVadis::Session.last.second_factor_authenticated?
77
- assert_response :success
77
+ assert_response :unprocessable_entity
78
78
  assert_equal 'Sorry, the code was incorrect. Please check your system clock is correct and try again.', flash[:alert]
79
79
  end
80
80
 
@@ -46,6 +46,9 @@ class PasswordTest < ActiveSupport::TestCase
46
46
 
47
47
  test 'model passes through password to quo_vadis' do
48
48
  user = User.new name: 'bob', email: 'bob@example.com'
49
+ assert user.valid?
50
+
51
+ user = User.new name: 'bob', email: 'bob@example.com', password: ''
49
52
  refute user.valid?
50
53
  refute_empty user.errors[:password]
51
54
 
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.0.2
4
+ version: 2.1.0
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-05-24 00:00:00.000000000 Z
11
+ date: 2021-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -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.2
227
+ rubygems_version: 3.1.4
228
228
  signing_key:
229
229
  specification_version: 4
230
230
  summary: Multifactor authentication for Rails 6.