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