summaryrefslogtreecommitdiff
path: root/spec/controllers/profiles/two_factor_auths_controller_spec.rb
blob: aa09f1a758d4d148681dd4063ff1f76a8bfbfd31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require 'spec_helper'

describe Profiles::TwoFactorAuthsController do
  before do
    # `user` should be defined within the action-specific describe blocks
    sign_in(user)

    allow(subject).to receive(:current_user).and_return(user)
  end

  describe 'GET new' do
    let(:user) { create(:user) }

    it 'generates otp_secret for user' do
      expect(User).to receive(:generate_otp_secret).with(32).and_return('secret').once

      get :new
      get :new # Second hit shouldn't re-generate it
    end

    it 'assigns qr_code' do
      code = double('qr code')
      expect(subject).to receive(:build_qr_code).and_return(code)

      get :new
      expect(assigns[:qr_code]).to eq code
    end
  end

  describe 'POST create' do
    let(:user) { create(:user) }
    let(:pin)  { 'pin-code' }

    def go
      post :create, pin_code: pin
    end

    context 'with valid pin' do
      before do
        expect(user).to receive(:valid_otp?).with(pin).and_return(true)
      end

      it 'sets two_factor_enabled' do
        go

        user.reload
        expect(user).to be_two_factor_enabled
      end

      it 'presents plaintext codes for the user to save' do
        expect(user).to receive(:generate_otp_backup_codes!).and_return(%w(a b c))

        go

        expect(assigns[:codes]).to match_array %w(a b c)
      end

      it 'renders create' do
        go
        expect(response).to render_template(:create)
      end
    end

    context 'with invalid pin' do
      before do
        expect(user).to receive(:valid_otp?).with(pin).and_return(false)
      end

      it 'assigns error' do
        go
        expect(assigns[:error]).to eq 'Invalid pin code'
      end

      it 'assigns qr_code' do
        code = double('qr code')
        expect(subject).to receive(:build_qr_code).and_return(code)

        go
        expect(assigns[:qr_code]).to eq code
      end

      it 'renders new' do
        go
        expect(response).to render_template(:new)
      end
    end
  end

  describe 'POST codes' do
    let(:user) { create(:user, :two_factor) }

    it 'presents plaintext codes for the user to save' do
      expect(user).to receive(:generate_otp_backup_codes!).and_return(%w(a b c))

      post :codes
      expect(assigns[:codes]).to match_array %w(a b c)
    end

    it 'persists the generated codes' do
      post :codes

      user.reload
      expect(user.otp_backup_codes).not_to be_empty
    end
  end

  describe 'DELETE destroy' do
    let(:user)   { create(:user, :two_factor) }
    let!(:codes) { user.generate_otp_backup_codes! }

    it 'clears all 2FA-related fields' do
      expect(user).to be_two_factor_enabled
      expect(user.otp_backup_codes).not_to be_nil
      expect(user.encrypted_otp_secret).not_to be_nil

      delete :destroy

      expect(user).not_to be_two_factor_enabled
      expect(user.otp_backup_codes).to be_nil
      expect(user.encrypted_otp_secret).to be_nil
    end

    it 'redirects to profile_account_path' do
      delete :destroy

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