quo_vadis 2.1.11 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +50 -99
  4. data/app/controllers/quo_vadis/confirmations_controller.rb +26 -61
  5. data/app/controllers/quo_vadis/password_resets_controller.rb +64 -32
  6. data/app/controllers/quo_vadis/sessions_controller.rb +0 -5
  7. data/app/mailers/quo_vadis/mailer.rb +2 -2
  8. data/app/models/quo_vadis/account.rb +32 -0
  9. data/app/models/quo_vadis/password.rb +6 -1
  10. data/app/views/quo_vadis/confirmations/new.html.erb +19 -7
  11. data/app/views/quo_vadis/mailer/account_confirmation.text.erb +2 -3
  12. data/app/views/quo_vadis/mailer/reset_password.text.erb +2 -2
  13. data/app/views/quo_vadis/password_resets/edit.html.erb +13 -1
  14. data/app/views/quo_vadis/password_resets/new.html.erb +1 -1
  15. data/config/locales/quo_vadis.en.yml +9 -7
  16. data/config/routes.rb +4 -12
  17. data/lib/quo_vadis/controller.rb +20 -10
  18. data/lib/quo_vadis/defaults.rb +2 -2
  19. data/lib/quo_vadis/version.rb +1 -1
  20. data/lib/quo_vadis.rb +2 -2
  21. data/test/dummy/app/controllers/sign_ups_controller.rb +2 -11
  22. data/test/fixtures/quo_vadis/mailer/account_confirmation.text +2 -3
  23. data/test/fixtures/quo_vadis/mailer/reset_password.text +2 -2
  24. data/test/integration/account_confirmation_test.rb +42 -86
  25. data/test/integration/controller_test.rb +8 -8
  26. data/test/integration/logging_test.rb +31 -7
  27. data/test/integration/password_login_test.rb +1 -1
  28. data/test/integration/password_reset_test.rb +89 -54
  29. data/test/mailers/mailer_test.rb +2 -2
  30. data/test/models/account_test.rb +48 -0
  31. data/test/models/session_test.rb +4 -0
  32. metadata +2 -10
  33. data/app/models/quo_vadis/account_confirmation_token.rb +0 -17
  34. data/app/models/quo_vadis/password_reset_token.rb +0 -17
  35. data/app/models/quo_vadis/token.rb +0 -42
  36. data/app/views/quo_vadis/confirmations/edit.html.erb +0 -10
  37. data/app/views/quo_vadis/confirmations/edit_email.html.erb +0 -14
  38. data/app/views/quo_vadis/confirmations/index.html.erb +0 -14
  39. data/app/views/quo_vadis/password_resets/index.html.erb +0 -5
  40. data/test/models/token_test.rb +0 -70
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rotp'
4
+
3
5
  module QuoVadis
4
6
  class Account < ActiveRecord::Base
5
7
 
@@ -18,6 +20,26 @@ module QuoVadis
18
20
  after_update :log_identifier_change, if: :saved_change_to_identifier?
19
21
  after_update :notify_identifier_change, if: :saved_change_to_identifier?
20
22
 
23
+ scope :unconfirmed, -> { where confirmed_at: nil }
24
+
25
+ def otp_for_confirmation(counter)
26
+ hotp_for_confirmation.at(counter)
27
+ end
28
+
29
+ # If the `otp` is valid for the `counter`, confirms the account and returns truthy.
30
+ # Otherwise returns falsey.
31
+ def confirm(otp, counter)
32
+ hotp_for_confirmation.verify(otp, counter) && confirmed!
33
+ end
34
+
35
+ def otp_for_password_reset(counter)
36
+ hotp_for_password_reset.at(counter)
37
+ end
38
+
39
+ def verify_password_reset(otp, counter)
40
+ hotp_for_password_reset.verify(otp, counter)
41
+ end
42
+
21
43
  def confirmed?
22
44
  confirmed_at.present?
23
45
  end
@@ -51,6 +73,16 @@ module QuoVadis
51
73
 
52
74
  private
53
75
 
76
+ def hotp_for_confirmation
77
+ key = ROTP::Base32.encode("#{id}-#{Rails.application.secret_key_base}")
78
+ ROTP::HOTP.new(key)
79
+ end
80
+
81
+ def hotp_for_password_reset
82
+ key = ROTP::Base32.encode("#{id}-#{password.password_digest}")
83
+ ROTP::HOTP.new(key)
84
+ end
85
+
54
86
  def log_identifier_change
55
87
  from, to = saved_change_to_identifier
56
88
  Log.create(
@@ -46,7 +46,12 @@ module QuoVadis
46
46
 
47
47
  self.password = new_plaintext
48
48
  self.password_confirmation = new_plaintext_confirmation
49
- save
49
+ if save
50
+ # Logout account's sessions because password has changed.
51
+ # Assumes model is not logged in.
52
+ account.sessions.destroy_all
53
+ true
54
+ end
50
55
  end
51
56
 
52
57
  private
@@ -1,16 +1,28 @@
1
- <h1>Resend confirmation instructions</h1>
1
+ <h1>Account confirmation</h1>
2
2
 
3
- <%# Note that in this example the User identifier is :email. %>
3
+ <p>We have sent a confirmation code to <%= @account.model.email %>.</p>
4
4
 
5
- <p>If you didn't receive the confirmation email, please enter your email address and we'll send you another one.</p>
5
+ <!-- flash could go here -->
6
6
 
7
- <%= form_with url: confirmations_path, method: :post do |f| %>
7
+ <%= form_with url: confirm_path, method: :post do %>
8
8
  <p>
9
- <%= f.label :email %>
10
- <%= f.text_field :email, inputmode: 'email', autocomplete: 'email' %>
9
+ <label for="otp">Your confirmation code:</label>
10
+ <input
11
+ name="otp"
12
+ id="otp"
13
+ type="text"
14
+ inputmode="numeric"
15
+ pattern="\d*"
16
+ autofocus="true"
17
+ autocomplete="one-time-password"
18
+ maxlength="6">
11
19
  </p>
12
20
 
13
21
  <p>
14
- <%= f.submit %>
22
+ <button type="submit">Confirm me!</button>
15
23
  </p>
16
24
  <% end %>
25
+
26
+ <p>
27
+ <%= button_to "Send me a new confirmation code", send_confirmation_path %>
28
+ </p>
@@ -1,4 +1,3 @@
1
- You can confirm your account here:
2
-
3
- <%= @url %>
1
+ This is your confirmation code:
4
2
 
3
+ <%= @otp %>
@@ -1,4 +1,4 @@
1
- You can reset your password here:
1
+ Here is your code so you can reset your password:
2
2
 
3
- <%= @url %>
3
+ <%= @otp %>
4
4
 
@@ -1,6 +1,18 @@
1
1
  <h1>Reset password</h1>
2
2
 
3
- <%= form_with model: @password, url: password_reset_path(params[:token]), method: :put do |f| %>
3
+ <%= form_with model: @password, url: password_reset_path, method: :put do |f| %>
4
+ <p>
5
+ <%= f.label :otp, 'Your password reset code' %>
6
+ <%= f.text_field :otp,
7
+ value: params.dig(:password, :otp),
8
+ inputmode: 'numeric',
9
+ pattern: '\d*',
10
+ autofocus: true,
11
+ autocomplete: 'one-time-password',
12
+ maxlength: 6 %>
13
+ </p>
14
+
15
+
4
16
  <p>
5
17
  <%= f.label :password %>
6
18
  <%= f.password_field :password, autocomplete: 'new-password' %>
@@ -1,6 +1,6 @@
1
1
  <h1>Reset password</h1>
2
2
 
3
- <%= form_with url: password_resets_path, method: :post do |f| %>
3
+ <%= form_with url: password_reset_path, method: :post do |f| %>
4
4
  <p>
5
5
  <%= f.label :email %>
6
6
  <%= f.text_field :email, inputmode: 'email', autocomplete: 'email' %>
@@ -11,15 +11,17 @@ en:
11
11
  password:
12
12
  update: Your password has been changed.
13
13
  password_reset:
14
- create: A link to change your password has been emailed to you.
15
- unknown: Either the link has expired or you have already reset your password.
14
+ create: Please check your email for your reset code.
15
+ unknown: Please check your email for your reset code.
16
+ expired: Your reset code has expired. Please request another one.
17
+ invalid: Sorry, the code was incorrect. Please try again.
16
18
  reset: Your password has been changed and you are logged in.
17
19
  confirmation:
18
- create: A link to confirm your account has been emailed to you.
19
- required: Please confirm your account first.
20
- identifier: Sorry, your account could not be found. Please try again.
21
- unknown: Either the link has expired or your account has already been confirmed.
22
- confirmed: Thanks for confirming your account. You are now logged in.
20
+ unknown: You have already confirmed your account.
21
+ sent: Please check your email for your confirmation code.
22
+ expired: Your confirmation code has expired. Please request another one.
23
+ invalid: Sorry, the code was incorrect. Please try again.
24
+ confirmed: Thanks for confirming your account.
23
25
  totp:
24
26
  unverified: Sorry, the code was incorrect. Please check your system clock is correct and try again.
25
27
  setup: Please set up two factor authentication.
data/config/routes.rb CHANGED
@@ -11,19 +11,11 @@ QuoVadis::Engine.routes.draw do
11
11
 
12
12
  resource :password, only: [:edit, :update]
13
13
 
14
- resources :password_resets, only: [:new, :create, :index]
15
- get '/pwd-reset/:token', to: 'password_resets#edit', as: 'password_reset'
16
- put '/pwd-reset/:token', to: 'password_resets#update'
14
+ resource :password_reset, only: [:new, :create, :edit, :update]
17
15
 
18
- resources :confirmations, only: [:new, :create, :index] do
19
- collection do
20
- get :edit_email
21
- put :update_email
22
- post :resend
23
- end
24
- end
25
- get '/confirm/:token', to: 'confirmations#edit', as: 'confirmation'
26
- put '/confirm/:token', to: 'confirmations#update'
16
+ get '/confirm', to: 'confirmations#new'
17
+ post '/confirm', to: 'confirmations#create'
18
+ post '/confirm/send', to: 'confirmations#resend', as: 'send_confirmation'
27
19
 
28
20
  resources :totps, only: [:new, :create] do
29
21
  collection do
@@ -16,7 +16,13 @@ module QuoVadis
16
16
 
17
17
 
18
18
  def require_password_authentication
19
- return if logged_in?
19
+ if logged_in?
20
+ if QuoVadis.accounts_require_confirmation && !authenticated_model.qv_account.confirmed?
21
+ qv.request_confirmation authenticated_model
22
+ redirect_to quo_vadis.confirm_path
23
+ end
24
+ return
25
+ end
20
26
  session[:qv_bookmark] = request.original_fullpath
21
27
  redirect_to quo_vadis.login_path, notice: QuoVadis.translate('flash.require_authentication')
22
28
  end
@@ -85,15 +91,6 @@ module QuoVadis
85
91
  end
86
92
 
87
93
 
88
- def request_confirmation(model)
89
- token = QuoVadis::AccountConfirmationToken.generate model.qv_account
90
- QuoVadis.deliver :account_confirmation, {email: model.email, url: quo_vadis.confirmation_url(token)}
91
- session[:account_pending_confirmation] = model.qv_account.id
92
-
93
- flash[:notice] = QuoVadis.translate 'flash.confirmation.create'
94
- end
95
-
96
-
97
94
  def qv
98
95
  @qv_wrapper ||= QuoVadisWrapper.new self
99
96
  end
@@ -143,6 +140,19 @@ module QuoVadis
143
140
  old_session.each { |k,v| rails_session[k] = v }
144
141
  end
145
142
 
143
+ def request_confirmation(model)
144
+ rails_session[:account_pending_confirmation] = model.qv_account.id
145
+
146
+ expiration = QuoVadis.account_confirmation_otp_lifetime.from_now.to_i
147
+ rails_session[:account_confirmation_expires_at] = expiration
148
+
149
+ otp = model.qv_account.otp_for_confirmation(expiration)
150
+
151
+ QuoVadis.deliver :account_confirmation, {email: model.email, otp: otp}
152
+
153
+ controller.flash[:notice] = QuoVadis.translate 'flash.confirmation.sent'
154
+ end
155
+
146
156
  # Assumes user is logged in.
147
157
  def second_factor_required?
148
158
  QuoVadis.two_factor_authentication_mandatory || authenticated_model.qv_account.has_two_factors?
@@ -7,9 +7,9 @@ QuoVadis.configure do
7
7
  session_lifetime :session
8
8
  session_lifetime_extend_to_end_of_day false
9
9
  session_idle_timeout :lifetime
10
- password_reset_token_lifetime 10.minutes
10
+ password_reset_otp_lifetime 10.minutes
11
11
  accounts_require_confirmation false
12
- account_confirmation_token_lifetime 10.minutes
12
+ account_confirmation_otp_lifetime 10.minutes
13
13
  mailer_superclass 'ApplicationMailer'
14
14
  mail_headers ({ from: 'Example App <support@example.com>' })
15
15
  enqueue_transactional_emails true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuoVadis
4
- VERSION = '2.1.11'
4
+ VERSION = '2.2.0'
5
5
  end
data/lib/quo_vadis.rb CHANGED
@@ -24,11 +24,11 @@ module QuoVadis
24
24
  :session_lifetime, # :session | ActiveSupport::Duration | [integer] seconds
25
25
  :session_lifetime_extend_to_end_of_day,# true | false
26
26
  :session_idle_timeout, # :lifetime | ActiveSupport::Duration | [integer] seconds
27
- :password_reset_token_lifetime, # ActiveSupport::Duration | [integer] seconds
27
+ :password_reset_otp_lifetime, # ActiveSupport::Duration | [integer] seconds
28
28
  :mailer_superclass, # string
29
29
  :mail_headers, # hash
30
30
  :accounts_require_confirmation, # true | false
31
- :account_confirmation_token_lifetime, # ActiveSupport::Duration | [integer] seconds
31
+ :account_confirmation_otp_lifetime, # ActiveSupport::Duration | [integer] seconds
32
32
  :enqueue_transactional_emails, # true | false
33
33
  :app_name, # string
34
34
  :two_factor_authentication_mandatory, # true | false
@@ -1,8 +1,6 @@
1
1
  # To test sign-ups with confirmation (activation / verification)
2
2
  class SignUpsController < ApplicationController
3
3
 
4
- around_action :toggle_confirmation
5
-
6
4
  def new
7
5
  @user = User.new
8
6
  end
@@ -11,9 +9,9 @@ class SignUpsController < ApplicationController
11
9
  def create
12
10
  @user = User.new user_params
13
11
  if @user.save
12
+ login @user
14
13
  if QuoVadis.accounts_require_confirmation
15
- request_confirmation @user
16
- redirect_to quo_vadis.confirmations_path
14
+ redirect_to secret_articles_path
17
15
  else
18
16
  redirect_to articles_path
19
17
  end
@@ -37,11 +35,4 @@ class SignUpsController < ApplicationController
37
35
  params.require(:user).permit(:name, :email, :password, :password_confirmation)
38
36
  end
39
37
 
40
- def toggle_confirmation
41
- QuoVadis.accounts_require_confirmation true
42
- yield
43
- ensure
44
- QuoVadis.accounts_require_confirmation false
45
- end
46
-
47
38
  end
@@ -1,4 +1,3 @@
1
- You can confirm your account here:
2
-
3
- http://example.com/confirm/123abc
1
+ This is your confirmation code:
4
2
 
3
+ 271828
@@ -1,4 +1,4 @@
1
- You can reset your password here:
1
+ Here is your code so you can reset your password:
2
2
 
3
- http://example.com/pwd-reset/123abc
3
+ 314159
4
4
 
@@ -14,136 +14,92 @@ class AccountConfirmationTest < IntegrationTest
14
14
  test 'new signup requiring confirmation' do
15
15
  assert_emails 1 do
16
16
  post sign_ups_path(user: {name: 'Bob', email: 'bob@example.com', password: '123456789abc'})
17
+ refute QuoVadis::Account.last.confirmed?
18
+ assert controller.logged_in?
19
+
20
+ # verify response
21
+ assert_redirected_to '/articles/secret'
22
+ follow_redirect!
23
+ assert_redirected_to '/confirm'
24
+ follow_redirect!
25
+ assert_equal 'Please check your email for your confirmation code.', flash[:notice]
17
26
  end
18
- refute QuoVadis::Account.last.confirmed?
19
-
20
- # verify response
21
- assert_response :redirect
22
- follow_redirect!
23
- assert_equal 'A link to confirm your account has been emailed to you.', flash[:notice]
24
27
 
25
- # click link in email
26
- url = extract_url_from_email
27
- get url
28
- assert_response :success
29
- action_path = extract_path_from_email
30
- assert_select "form[action='#{action_path}']"
31
-
32
- # click button on confirmation page
33
- put action_path
28
+ # type in confirmation code from email
29
+ code = extract_code_from_email
30
+ post quo_vadis.confirm_path(otp: code)
34
31
 
35
32
  # verify logged in
36
33
  assert_redirected_to '/sign_ups/confirmed'
37
34
  follow_redirect!
38
35
  assert_redirected_to '/articles/secret'
39
- assert_equal 'Thanks for confirming your account. You are now logged in.', flash[:notice]
40
- assert controller.logged_in?
36
+ assert_equal 'Thanks for confirming your account.', flash[:notice]
41
37
  assert QuoVadis::Account.last.confirmed?
42
38
  end
43
39
 
44
40
 
45
- test 'new signup updates email' do
46
- assert_emails 1 do
47
- post sign_ups_path(user: {name: 'Bob', email: 'bob@example.com', password: '123456789abc'})
48
- end
49
-
50
- get quo_vadis.edit_email_confirmations_path
51
- assert_response :success
52
-
53
- # First email: changed-email notifier sent to original address
54
- # Second email: confirmation email sent to new address
55
- assert_emails 2 do
56
- put quo_vadis.update_email_confirmations_path(email: 'bobby@example.com')
57
- end
58
- assert_equal ['bobby@example.com'], ActionMailer::Base.deliveries.last.to
59
- assert_redirected_to quo_vadis.confirmations_path
60
- end
61
-
62
-
63
- test 'resend confirmation email in same session' do
41
+ test 'resend confirmation email' do
64
42
  assert_emails 1 do
65
43
  post sign_ups_path(user: {name: 'Bob', email: 'bob@example.com', password: '123456789abc'})
44
+ assert_redirected_to '/articles/secret'
45
+ follow_redirect!
66
46
  end
67
47
 
68
48
  assert_emails 1 do
69
- post quo_vadis.resend_confirmations_path
49
+ post quo_vadis.send_confirmation_path
70
50
  end
71
51
  end
72
52
 
73
53
 
74
- test 'resend confirmation email: valid identifier' do
75
- User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
76
-
77
- get quo_vadis.new_confirmation_path
78
- assert_response :success
79
-
80
- assert_emails 1 do
81
- post quo_vadis.confirmations_path(email: 'bob@example.com')
82
- end
83
-
84
- assert_redirected_to '/confirmations'
85
- assert_equal 'A link to confirm your account has been emailed to you.', flash[:notice]
86
- end
87
-
88
-
89
- test 'resend confirmation email: unknown identifier' do
90
- assert_no_emails do
91
- post quo_vadis.confirmations_path(email: 'bob@example.com')
92
- end
93
-
94
- assert_redirected_to quo_vadis.new_confirmation_path
95
- assert_equal 'Sorry, your account could not be found. Please try again.', flash[:alert]
96
- end
97
-
98
-
99
- test 'reusing a token' do
54
+ test 'cannot reuse a confirmation code' do
100
55
  assert_emails 1 do
101
56
  post sign_ups_path(user: {name: 'Bob', email: 'bob@example.com', password: '123456789abc'})
57
+ follow_redirect!
102
58
  end
103
59
 
104
- put extract_path_from_email
60
+ code = extract_code_from_email
61
+ post quo_vadis.confirm_path(otp: code)
62
+ assert QuoVadis::Account.last.confirmed?
63
+ assert_equal 'Thanks for confirming your account.', flash[:notice]
105
64
 
65
+ post quo_vadis.confirm_path(otp: code)
66
+ assert_equal 'You have already confirmed your account.', flash[:alert]
106
67
  assert_redirected_to '/sign_ups/confirmed'
107
- follow_redirect!
108
- assert_redirected_to '/articles/secret'
109
- assert_equal 'Thanks for confirming your account. You are now logged in.', flash[:notice]
110
-
111
- put extract_path_from_email
112
- assert_redirected_to quo_vadis.new_confirmation_path
113
- assert_equal 'Either the link has expired or your account has already been confirmed.', flash[:alert]
114
68
  end
115
69
 
116
70
 
117
- test 'token expired' do
71
+ test 'confirmation code expired' do
118
72
  assert_emails 1 do
119
73
  post sign_ups_path(user: {name: 'Bob', email: 'bob@example.com', password: '123456789abc'})
74
+ follow_redirect!
120
75
  end
121
76
 
122
- travel QuoVadis.account_confirmation_token_lifetime + 1.minute
123
- get extract_url_from_email
77
+ travel QuoVadis.account_confirmation_otp_lifetime + 1.minute
124
78
 
125
- assert_redirected_to quo_vadis.new_confirmation_path
126
- assert_equal 'Either the link has expired or your account has already been confirmed.', flash[:alert]
79
+ code = extract_code_from_email
80
+ post quo_vadis.confirm_path(otp: code)
81
+ refute QuoVadis::Account.last.confirmed?
82
+ assert_equal 'Your confirmation code has expired. Please request another one.', flash[:alert]
83
+ assert_redirected_to quo_vadis.confirm_path
127
84
  end
128
85
 
129
86
 
130
- test 'accounts requiring confirmation cannot log in' do
87
+ test 'accounts requiring confirmation can log in but have to confirm' do
131
88
  User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
132
89
  post quo_vadis.login_path(email: 'bob@example.com', password: '123456789abc')
133
- assert_redirected_to quo_vadis.new_confirmation_path
134
- assert_equal 'Please confirm your account first.', flash[:notice]
135
- refute controller.logged_in?
90
+ assert_redirected_to '/articles/secret'
91
+ follow_redirect!
92
+ assert_redirected_to quo_vadis.confirm_path
93
+ follow_redirect!
94
+ assert controller.logged_in?
95
+ assert_equal 'Please check your email for your confirmation code.', flash[:notice]
136
96
  end
137
97
 
138
98
 
139
99
  private
140
100
 
141
- def extract_url_from_email
142
- ActionMailer::Base.deliveries.last.decoded[%r{^http://.*$}, 0]
143
- end
144
-
145
- def extract_path_from_email
146
- extract_url_from_email.sub 'http://www.example.com', ''
101
+ def extract_code_from_email
102
+ ActionMailer::Base.deliveries.last.decoded[%r{\d{6}}, 0]
147
103
  end
148
104
 
149
105
  end
@@ -206,14 +206,14 @@ class ControllerTest < IntegrationTest
206
206
  end
207
207
 
208
208
 
209
- test 'request_confirmation' do
210
- get articles_path
211
-
212
- assert_emails 1 do
213
- controller.request_confirmation User.last
214
- end
215
- assert_equal 'A link to confirm your account has been emailed to you.', flash[:notice]
216
- end
209
+ # test 'request_confirmation' do
210
+ # get articles_path
211
+
212
+ # assert_emails 1 do
213
+ # controller.request_confirmation User.last
214
+ # end
215
+ # assert_equal 'A link to confirm your account has been emailed to you.', flash[:notice]
216
+ # end
217
217
 
218
218
 
219
219
  test 'session lifetime exceeded' do
@@ -163,22 +163,42 @@ class LoggingTest < IntegrationTest
163
163
 
164
164
 
165
165
  test 'password.reset' do
166
+ assert_emails 1 do
167
+ post quo_vadis.password_reset_path(email: 'bob@example.com')
168
+ follow_redirect!
169
+ end
170
+
166
171
  assert_difference 'QuoVadis::Log.count', 2 do
167
- token = QuoVadis::PasswordResetToken.generate @account
168
- put quo_vadis.password_reset_path(token, password: {password: 'xxxxxxxxxxxx', password_confirmation: 'xxxxxxxxxxxx'})
172
+ put quo_vadis.password_reset_path(password: {
173
+ otp: extract_code_from_email,
174
+ password: 'secretsecret',
175
+ password_confirmation: 'secretsecret',
176
+ })
169
177
  end
178
+
170
179
  assert_equal QuoVadis::Log::PASSWORD_RESET, QuoVadis::Log.first.action
171
180
  assert_equal QuoVadis::Log::LOGIN_SUCCESS, log.action
172
181
  end
173
182
 
174
183
 
175
184
  test 'account.confirmation' do
176
- assert_difference 'QuoVadis::Log.count', 2 do
177
- token = QuoVadis::AccountConfirmationToken.generate @account
178
- put quo_vadis.confirmation_path(token)
185
+ QuoVadis.accounts_require_confirmation true
186
+
187
+ assert_emails 1 do
188
+ login
189
+ assert_redirected_to '/articles/secret'
190
+ follow_redirect!
191
+ assert_redirected_to '/confirm'
192
+ follow_redirect!
179
193
  end
180
- assert_equal QuoVadis::Log::ACCOUNT_CONFIRMATION, QuoVadis::Log.first.action
181
- assert_equal QuoVadis::Log::LOGIN_SUCCESS, log.action
194
+
195
+ assert_difference 'QuoVadis::Log.count' do
196
+ post quo_vadis.confirm_path(otp: extract_code_from_email)
197
+ end
198
+
199
+ assert_equal QuoVadis::Log::ACCOUNT_CONFIRMATION, log.action
200
+ ensure
201
+ QuoVadis.accounts_require_confirmation false
182
202
  end
183
203
 
184
204
 
@@ -241,4 +261,8 @@ class LoggingTest < IntegrationTest
241
261
  end
242
262
  end
243
263
 
264
+ def extract_code_from_email
265
+ ActionMailer::Base.deliveries.last.decoded[%r{\d{6}}, 0]
266
+ end
267
+
244
268
  end
@@ -105,7 +105,7 @@ class PasswordLoginTest < IntegrationTest
105
105
  travel 5.days
106
106
 
107
107
  get articles_path
108
- assert controller.logged_in? # flakey
108
+ assert controller.logged_in?
109
109
 
110
110
  travel 3.days
111
111