Skip to content

Commit e563a99

Browse files
tests: add controller specs
1 parent ca27fa9 commit e563a99

3 files changed

Lines changed: 185 additions & 26 deletions

File tree

spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,51 @@ def totp_provisioning_uri
6262
end
6363

6464
describe 'when creation succeeds' do
65-
let!(:otp_backup_codes) { user.generate_otp_backup_codes! }
66-
67-
before do
68-
prepare_user_otp_generation
69-
prepare_user_otp_consumption_response(true)
70-
allow(controller).to receive(:current_user).and_return(user)
65+
context 'when user has already generated backup codes' do
66+
before do
67+
user.generate_otp_backup_codes!
68+
user.save!
69+
prepare_user_otp_consumption_response(true)
70+
allow(controller).to receive(:current_user).and_return(user)
71+
end
72+
73+
it 'does not regenerate backup codes and redirects' do
74+
allow(user).to receive(:generate_otp_backup_codes!)
75+
76+
expect { post_create_with_options }
77+
.to change { user.reload.otp_secret }.to otp_secret_value
78+
79+
expect(user).to_not have_received(:generate_otp_backup_codes!)
80+
expect(flash[:notice])
81+
.to eq(I18n.t('two_factor_authentication.enabled_success'))
82+
expect(response)
83+
.to have_http_status(302)
84+
expect(response)
85+
.to redirect_to settings_two_factor_authentication_methods_path
86+
end
7187
end
7288

73-
it 'renders page with success' do
74-
expect { post_create_with_options }
75-
.to change { user.reload.otp_secret }.to otp_secret_value
76-
77-
expect(flash[:notice])
78-
.to eq(I18n.t('two_factor_authentication.enabled_success'))
79-
expect(response)
80-
.to have_http_status(200)
81-
expect(response.body)
82-
.to include(*otp_backup_codes)
83-
.and include(I18n.t('settings.two_factor_authentication'))
89+
context 'when user has not generated backup codes yet' do
90+
let!(:otp_backup_codes) { user.generate_otp_backup_codes! }
91+
92+
before do
93+
prepare_user_otp_generation
94+
prepare_user_otp_consumption_response(true)
95+
allow(controller).to receive(:current_user).and_return(user)
96+
end
97+
98+
it 'renders page with success' do
99+
expect { post_create_with_options }
100+
.to change { user.reload.otp_secret }.to otp_secret_value
101+
102+
expect(flash[:notice])
103+
.to eq(I18n.t('two_factor_authentication.enabled_success'))
104+
expect(response)
105+
.to have_http_status(200)
106+
expect(response.body)
107+
.to include(*otp_backup_codes)
108+
.and include(I18n.t('settings.two_factor_authentication'))
109+
end
84110
end
85111
end
86112

spec/controllers/settings/two_factor_authentication/otp_authentication_controller_spec.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@
5555

5656
describe 'when user has OTP enabled' do
5757
before do
58-
user.update(otp_required_for_login: true)
58+
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
59+
user.generate_otp_backup_codes!
60+
user.save
5961
end
6062

6163
describe 'when creation succeeds' do
@@ -75,7 +77,28 @@
7577
user.update(otp_required_for_login: false)
7678
end
7779

78-
describe 'when creation succeeds' do
80+
describe 'when user has not enabled 2FA yet' do
81+
describe 'when creation succeeds' do
82+
it 'redirects to code confirmation page without updating user secret and setting otp secret in the session' do
83+
expect do
84+
post :create, session: { challenge_passed_at: Time.now.utc }
85+
end.to not_change { user.reload.otp_secret }
86+
.and(change { session[:new_otp_secret] })
87+
88+
expect(response).to redirect_to(new_settings_two_factor_authentication_confirmation_path)
89+
end
90+
end
91+
end
92+
93+
describe 'when user has already enabled 2FA' do
94+
before do
95+
user.update(webauthn_id: WebAuthn.generate_user_id)
96+
Fabricate(:webauthn_credential, user_id: user.id, nickname: 'USB Key')
97+
user.otp_secret = User.generate_otp_secret(32)
98+
user.generate_otp_backup_codes!
99+
user.save
100+
end
101+
79102
it 'redirects to code confirmation page without updating user secret and setting otp secret in the session' do
80103
expect do
81104
post :create, session: { challenge_passed_at: Time.now.utc }

spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,66 @@ def add_webauthn_credential(user)
189189
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
190190
end.to_not change(user, :webauthn_id)
191191
end
192+
193+
context 'when user has already enabled 2FA' do
194+
before do
195+
user.update(webauthn_id: WebAuthn.generate_user_id)
196+
Fabricate(:webauthn_credential, user_id: user.id, nickname: 'USB Key 2')
197+
user.otp_secret = User.generate_otp_secret(32)
198+
user.generate_otp_backup_codes!
199+
user.save
200+
end
201+
202+
it 'does not update user secret' do
203+
controller.session[:webauthn_challenge] = challenge
204+
205+
expect do
206+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
207+
end.to(not_change { user.reload.otp_secret })
208+
end
209+
210+
it 'does not change backup codes' do
211+
controller.session[:webauthn_challenge] = challenge
212+
213+
expect do
214+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
215+
end.to(not_change { user.reload.otp_backup_codes })
216+
end
217+
218+
it 'redirects to two factor authentication methods index' do
219+
controller.session[:webauthn_challenge] = challenge
220+
221+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
222+
223+
expect(response.parsed_body['redirect_path']).to eq settings_two_factor_authentication_methods_path
224+
end
225+
end
226+
227+
context 'when user has not enabled 2FA yet' do
228+
it 'updates user secret' do
229+
controller.session[:webauthn_challenge] = challenge
230+
231+
expect do
232+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
233+
end.to(change { user.reload.otp_secret })
234+
end
235+
236+
it 'generates backup codes' do
237+
controller.session[:webauthn_challenge] = challenge
238+
239+
expect do
240+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
241+
end.to(change { user.reload.otp_backup_codes })
242+
end
243+
244+
it "returns backup codes page in response's html_data attribute" do
245+
controller.session[:webauthn_challenge] = challenge
246+
247+
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
248+
249+
expect(response.parsed_body['html_data']).to match(/recovery codes/)
250+
end
251+
end
192252
end
193253

194254
context 'when the nickname is already used' do
@@ -257,18 +317,68 @@ def add_webauthn_credential(user)
257317
add_webauthn_credential(user)
258318
end
259319

260-
context 'when deletion succeeds' do
261-
it 'redirects to 2FA methods list and shows flash success' do
320+
it 'redirects to 2FA methods list and shows flash success' do
321+
delete :destroy, params: { id: user.webauthn_credentials.take.id }
322+
323+
expect(response).to redirect_to settings_two_factor_authentication_methods_path
324+
expect(flash[:success]).to be_present
325+
end
326+
327+
it 'deletes the credential' do
328+
expect do
262329
delete :destroy, params: { id: user.webauthn_credentials.take.id }
330+
end.to change { user.webauthn_credentials.count }.by(-1)
331+
end
263332

264-
expect(response).to redirect_to settings_two_factor_authentication_methods_path
265-
expect(flash[:success]).to be_present
333+
context "when deleted credential was not user's last webauthn credential" do
334+
before do
335+
Fabricate(:webauthn_credential, user_id: user.id)
266336
end
267337

268-
it 'deletes the credential' do
269-
expect do
338+
context 'when user has OTP enabled' do
339+
before do
340+
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
341+
user.generate_otp_backup_codes!
342+
user.save
343+
end
344+
345+
it 'does not disable 2FA' do
346+
expect_any_instance_of(User).to_not receive(:disable_two_factor!) # rubocop:disable RSpec/AnyInstance
347+
348+
delete :destroy, params: { id: user.webauthn_credentials.take.id }
349+
end
350+
end
351+
352+
context 'when user does not have OTP enabled' do
353+
it 'does not disable 2FA' do
354+
expect_any_instance_of(User).to_not receive(:disable_two_factor!) # rubocop:disable RSpec/AnyInstance
355+
270356
delete :destroy, params: { id: user.webauthn_credentials.take.id }
271-
end.to change { user.webauthn_credentials.count }.by(-1)
357+
end
358+
end
359+
end
360+
361+
context "when deleted credential was user's last webauthn credential" do
362+
context 'when user has OTP enabled' do
363+
before do
364+
user.update(otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
365+
user.generate_otp_backup_codes!
366+
user.save
367+
end
368+
369+
it 'does not disable 2FA' do
370+
expect_any_instance_of(User).to_not receive(:disable_two_factor!) # rubocop:disable RSpec/AnyInstance
371+
372+
delete :destroy, params: { id: user.webauthn_credentials.take.id }
373+
end
374+
end
375+
376+
context 'when user does not have OTP enabled' do
377+
it 'disables 2FA' do
378+
expect_any_instance_of(User).to receive(:disable_two_factor!) # rubocop:disable RSpec/AnyInstance
379+
380+
delete :destroy, params: { id: user.webauthn_credentials.take.id }
381+
end
272382
end
273383
end
274384
end

0 commit comments

Comments
 (0)