main
1# frozen_string_literal: true
2
3require 'rails_helper'
4
5RSpec.describe '/my/mfa' do
6 context "when logged in" do
7 let(:current_user) { create(:user) }
8
9 before { http_login(current_user) }
10
11 describe "GET /my/mfa" do
12 context "when MFA is set up" do
13 let(:current_user) { create(:user, :mfa_configured) }
14
15 before { get '/my/mfa' }
16
17 specify { expect(response).to redirect_to(edit_my_mfa_path) }
18 end
19
20 context "when MFA is not set up" do
21 before { get '/my/mfa' }
22
23 specify { expect(response).to redirect_to(new_my_mfa_path) }
24 end
25 end
26
27 describe "GET /my/mfa/new" do
28 context "when mfa has not been set up yet" do
29 before { get '/my/mfa/new' }
30
31 specify { expect(response).to have_http_status(:ok) }
32 specify { expect(response.body).to include('Provisioning URI') }
33 end
34
35 context "when mfa has been set up" do
36 let(:current_user) { create(:user, :mfa_configured) }
37
38 before { get '/my/mfa/new' }
39
40 specify { expect(response).to redirect_to(edit_my_mfa_path) }
41 end
42 end
43
44 describe "POST /my/mfa" do
45 context "when the secret is valid" do
46 let(:secret) { SecureRandom.hex(20) }
47
48 before { post '/my/mfa', params: { user: { mfa_secret: secret } } }
49
50 specify { expect(current_user.reload.mfa_secret).to eql(secret) }
51 specify { expect(response).to redirect_to(my_dashboard_path) }
52 specify { expect(flash[:notice]).to include("successfully updated!") }
53 end
54 end
55
56 describe "POST /my/mfa/test" do
57 context "when the code is correct" do
58 let(:mfa_secret) { ::ROTP::Base32.random_base32 }
59 let(:current_code) { ROTP::TOTP.new(mfa_secret).now }
60
61 before { post '/my/mfa/test', params: { user: { code: current_code, mfa_secret: mfa_secret } }, xhr: true }
62
63 specify { expect(response).to have_http_status(:ok) }
64 end
65
66 context "when the code is incorrect" do
67 let(:mfa_secret) { ::ROTP::Base32.random_base32 }
68 let(:current_code) { "12345" }
69
70 before { post '/my/mfa/test', params: { user: { code: current_code, mfa_secret: mfa_secret } }, xhr: true }
71
72 specify { expect(response).to have_http_status(:ok) }
73 specify { expect(response.body).to include(I18n.t('my.mfas.test.invalid')) }
74 end
75 end
76
77 describe "DELETE /my/mfa" do
78 context "when mfa is enabled and the code is correct" do
79 let(:current_user) { create(:user, :mfa_configured) }
80 let(:current_code) { current_user.mfa.current_totp }
81
82 before { delete '/my/mfa', params: { user: { code: current_code } } }
83
84 specify { expect(current_user.reload.mfa_secret).to be_nil }
85 specify { expect(response).to redirect_to(my_dashboard_path) }
86 specify { expect(flash[:notice]).to include("MFA has been disabled") }
87 end
88
89 context "when mfa is enabled and the code is incorrect" do
90 let(:current_user) { create(:user, :mfa_configured) }
91 let!(:original_secret) { current_user.mfa_secret }
92
93 before { delete '/my/mfa', params: { user: { code: 'incorrect' } } }
94
95 specify { expect(current_user.reload.mfa_secret).to eql(original_secret) }
96 specify { expect(response).to redirect_to(edit_my_mfa_path) }
97 specify { expect(flash[:error]).to include("MFA code is incorrect") }
98 end
99 end
100 end
101
102 context "when not logged in" do
103 before { get '/my/mfa/new' }
104
105 specify { expect(response).to redirect_to(new_session_path) }
106 end
107end