Skip to content

Commit

Permalink
tests: add controller specs
Browse files Browse the repository at this point in the history
  • Loading branch information
santiagorodriguez96 committed Sep 8, 2023
1 parent 1605036 commit 09265f2
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 22 deletions.
1 change: 1 addition & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ RSpec/AnyInstance:
- 'spec/controllers/auth/sessions_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
- 'spec/lib/request_spec.rb'
- 'spec/lib/status_filter_spec.rb'
- 'spec/models/account_spec.rb'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,44 @@
end

describe 'when creation succeeds' do
let!(:otp_backup_codes) { user.generate_otp_backup_codes! }
describe 'when user has already generated backup codes' do
it 'renders page with success' do
user.generate_otp_backup_codes!
user.save!
expect_any_instance_of(User).to_not receive(:generate_otp_backup_codes!)
prepare_user_otp_consumption

expect do
post :create,
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
end.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview'

expect(assigns(:recovery_codes)).to be_nil
expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled'
expect(response).to have_http_status(302)
expect(response).to redirect_to settings_two_factor_authentication_methods_path
end
end

it 'renders page with success' do
prepare_user_otp_generation
prepare_user_otp_consumption
describe 'when user has not generated backup codes yet' do
let!(:otp_backup_codes) { user.generate_otp_backup_codes! }

expect do
post :create,
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
end.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview'
it 'renders page with success' do
prepare_user_otp_generation
prepare_user_otp_consumption

expect(assigns(:recovery_codes)).to eq otp_backup_codes
expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled'
expect(response).to have_http_status(200)
expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index')
expect do
post :create,
params: { form_two_factor_confirmation: { otp_attempt: '123456' } },
session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' }
end.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview'

expect(assigns(:recovery_codes)).to eq otp_backup_codes
expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled'
expect(response).to have_http_status(200)
expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index')
end
end

def prepare_user_otp_generation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@

describe 'when user has OTP enabled' do
before do
user.update(otp_required_for_login: true)
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
user.generate_otp_backup_codes!
user.save
end

describe 'when creation succeeds' do
Expand All @@ -75,7 +77,28 @@
user.update(otp_required_for_login: false)
end

describe 'when creation succeeds' do
describe 'when user has not enabled 2FA yet' do
describe 'when creation succeeds' do
it 'redirects to code confirmation page without updating user secret and setting otp secret in the session' do
expect do
post :create, session: { challenge_passed_at: Time.now.utc }
end.to not_change { user.reload.otp_secret }
.and(change { session[:new_otp_secret] })

expect(response).to redirect_to(new_settings_two_factor_authentication_confirmation_path)
end
end
end

describe 'when user has already enabled 2FA' do
before do
user.update(webauthn_id: WebAuthn.generate_user_id)
Fabricate(:webauthn_credential, user_id: user.id, nickname: 'USB Key')
user.otp_secret = User.generate_otp_secret(32)
user.generate_otp_backup_codes!
user.save
end

it 'redirects to code confirmation page without updating user secret and setting otp secret in the session' do
expect do
post :create, session: { challenge_passed_at: Time.now.utc }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,66 @@ def add_webauthn_credential(user)
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to_not change(user, :webauthn_id)
end

context 'when user has already enabled 2FA' do
before do
user.update(webauthn_id: WebAuthn.generate_user_id)
Fabricate(:webauthn_credential, user_id: user.id, nickname: 'USB Key 2')
user.otp_secret = User.generate_otp_secret(32)
user.generate_otp_backup_codes!
user.save
end

it 'does not update user secret' do
@controller.session[:webauthn_challenge] = challenge

expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to(not_change { user.reload.otp_secret })
end

it 'does not change backup codes' do
@controller.session[:webauthn_challenge] = challenge

expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to(not_change { user.reload.otp_backup_codes })
end

it 'redirects to two factor authentication methods index' do
@controller.session[:webauthn_challenge] = challenge

post :create, params: { credential: new_webauthn_credential, nickname: nickname }

expect(response.parsed_body['redirect_path']).to eq settings_two_factor_authentication_methods_path
end
end

context 'when user has not enabled 2FA yet' do
it 'updates user secret' do
@controller.session[:webauthn_challenge] = challenge

expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to(change { user.reload.otp_secret })
end

it 'generates backup codes' do
@controller.session[:webauthn_challenge] = challenge

expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to(change { user.reload.otp_backup_codes })
end

it "returns backup codes page in response's html_data attribute" do
@controller.session[:webauthn_challenge] = challenge

post :create, params: { credential: new_webauthn_credential, nickname: nickname }

expect(response.parsed_body['html_data']).to match(/recovery codes/)
end
end
end

context 'when the nickname is already used' do
Expand Down Expand Up @@ -246,18 +306,68 @@ def add_webauthn_credential(user)
add_webauthn_credential(user)
end

context 'when deletion succeeds' do
it 'redirects to 2FA methods list and shows flash success' do
it 'redirects to 2FA methods list and shows flash success' do
delete :destroy, params: { id: user.webauthn_credentials.take.id }

expect(response).to redirect_to settings_two_factor_authentication_methods_path
expect(flash[:success]).to be_present
end

it 'deletes the credential' do
expect do
delete :destroy, params: { id: user.webauthn_credentials.take.id }
end.to change { user.webauthn_credentials.count }.by(-1)
end

expect(response).to redirect_to settings_two_factor_authentication_methods_path
expect(flash[:success]).to be_present
context "when deleted credential was not user's last webauthn credential" do
before do
Fabricate(:webauthn_credential, user_id: user.id)
end

it 'deletes the credential' do
expect do
context 'when user has OTP enabled' do
before do
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
user.generate_otp_backup_codes!
user.save
end

it 'does not disable 2FA' do
expect_any_instance_of(User).to_not receive(:disable_two_factor!)

delete :destroy, params: { id: user.webauthn_credentials.take.id }
end
end

context 'when user does not have OTP enabled' do
it 'does not disable 2FA' do
expect_any_instance_of(User).to_not receive(:disable_two_factor!)

delete :destroy, params: { id: user.webauthn_credentials.take.id }
end.to change { user.webauthn_credentials.count }.by(-1)
end
end
end

context "when deleted credential was user's last webauthn credential" do
context 'when user has OTP enabled' do
before do
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
user.generate_otp_backup_codes!
user.save
end

it 'does not disable 2FA' do
expect_any_instance_of(User).to_not receive(:disable_two_factor!)

delete :destroy, params: { id: user.webauthn_credentials.take.id }
end
end

context 'when user does not have OTP enabled' do
it 'disables 2FA' do
expect_any_instance_of(User).to receive(:disable_two_factor!)

delete :destroy, params: { id: user.webauthn_credentials.take.id }
end
end
end
end
Expand Down

0 comments on commit 09265f2

Please sign in to comment.