diff --git a/app/models/user.rb b/app/models/user.rb index 6e1f7be1a..fb7e4b978 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -490,15 +490,13 @@ returns # try second lookup with email user ||= User.find_by(email: username.downcase.strip, active: true) - # check if email address exists - return if !user - return if !user.email + return if !user || !user.email - # generate token - token = Token.create(action: 'PasswordReset', user_id: user.id) + # Discard any possible previous tokens for safety reasons. + Token.where(action: 'PasswordReset', user_id: user.id).destroy_all { - token: token, + token: Token.create(action: 'PasswordReset', user_id: user.id, persistent: false), user: user, } end diff --git a/spec/system/password_reset_spec.rb b/spec/system/password_reset_spec.rb index d6d21e153..e433a8c2e 100644 --- a/spec/system/password_reset_spec.rb +++ b/spec/system/password_reset_spec.rb @@ -3,31 +3,67 @@ require 'rails_helper' RSpec.describe 'Password Reset', type: :system do - before do - visit 'password_reset' - end + context 'when logged in already' do + before do + visit 'password_reset' + end - it 'logged in user cannot open password reset' do - expect(page).to have_no_text 'password' + it 'logged in user cannot open password reset' do + expect(page).to have_no_text 'password' + end end context 'when not logged in', authenticated_as: false do - it 'proceeds with non-existant user' do - fill_in 'username', with: 'nonexisting' - + def request_reset + visit 'password_reset' + fill_in 'username', with: username click '.reset_password .btn--primary' - - expect(page).to have_text 'sent password reset instructions' end - it 'proceeds with an actual user' do - user = create(:agent) + before do + freeze_time + request_reset + end - fill_in 'username', with: user.email + context 'with non-existant user' do + let(:username) { 'nonexisting' } - click '.reset_password .btn--primary' + it 'pretends to proceed' do + expect(page).to have_text 'sent password reset instructions' + end + end - expect(page).to have_text 'sent password reset instructions' + context 'with existing user' do + let(:user) { create(:agent) } + let(:username) { user.email } + let(:generated_tokens) { Token.where(action: 'PasswordReset', user_id: user.id) } + + it 'proceeds' do + expect(page).to have_text 'sent password reset instructions' + end + + it 'creates a token' do + expect(generated_tokens.count).to eq 1 + end + + it 'token will expire' do + expect(generated_tokens.first.persistent).to be false + end + + context 'when submitting multiple times' do + before do + refresh + request_reset # a second time now + end + + it 'proceeds' do + expect(page).to have_text 'sent password reset instructions' + end + + it 'discards the previous token' do + expect(generated_tokens.count).to eq 1 + end + end end end end