summaryrefslogtreecommitdiff
path: root/spec/gitlab_shell_two_factor_recovery_spec.rb
blob: f2c76702633db8945468c76c41691956e11c93d8 (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
130
require_relative 'spec_helper'

require 'open3'

describe 'bin/gitlab-shell 2fa_recovery_codes' do
  include_context 'gitlab shell'

  def mock_server(server)
    server.mount_proc('/api/v4/internal/two_factor_recovery_codes') do |req, res|
      res.content_type = 'application/json'
      res.status = 200

      key_id = req.query['key_id'] || req.query['user_id']

      unless key_id
        body = JSON.parse(req.body)
        key_id = body['key_id'] || body['user_id'].to_s
      end

      if key_id == '100'
        res.body = '{"success":true, "recovery_codes": ["1", "2"]}'
      else
        res.body = '{"success":false, "message": "Forbidden!"}'
      end
    end

    server.mount_proc('/api/v4/internal/discover') do |req, res|
      res.status = 200
      res.content_type = 'application/json'
      res.body = '{"id":100, "name": "Some User", "username": "someuser"}'
    end
  end

  shared_examples 'dialog for regenerating recovery keys' do
    context 'when the user agrees to regenerate keys' do
      def verify_successful_regeneration!(cmd)
        p "+++++++++++++++++++++++++++#{env}---------------#{cmd}"

        Open3.popen2(env, cmd) do |stdin, stdout|
          expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
          expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")

          stdin.puts('yes')

          expect(stdout.flush.read).to eq(
            "\nYour two-factor authentication recovery codes are:\n\n" \
            "1\n2\n\n" \
            "During sign in, use one of the codes above when prompted for\n" \
            "your two-factor code. Then, visit your Profile Settings and add\n" \
            "a new device so you do not lose access to your account again.\n"
          )
        end
      end

      context 'when key is provided' do
        let(:cmd) { "#{gitlab_shell_path} key-100" }

        it 'the recovery keys are regenerated' do
          verify_successful_regeneration!(cmd)
        end
      end

      context 'when username is provided' do
        let(:cmd) { "#{gitlab_shell_path} -c/usr/share/webapps/gitlab-shell/bin/gitlab-shell username-someone" }

        it 'the recovery keys are regenerated' do
          verify_successful_regeneration!(cmd)
        end
      end
    end

    context 'when the user disagrees to regenerate keys' do
      let(:cmd) { "#{gitlab_shell_path} -c/usr/share/webapps/gitlab-shell/bin/gitlab-shell key-100" }

      it 'the recovery keys are not regenerated' do
        Open3.popen2(env, cmd) do |stdin, stdout|
          expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
          expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")

          stdin.puts('no')

          expect(stdout.flush.read).to eq(
            "\nNew recovery codes have *not* been generated. Existing codes will remain valid.\n"
          )
        end
      end
    end

    context 'when API error occurs' do
      let(:cmd) { "#{gitlab_shell_path} -c/usr/share/webapps/gitlab-shell/bin/gitlab-shell key-101" }

      context 'when the user agrees to regenerate keys' do
        it 'the recovery keys are regenerated' do
          Open3.popen2(env, cmd) do |stdin, stdout|
            expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
            expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")

            stdin.puts('yes')

            expect(stdout.flush.read).to eq("\nAn error occurred while trying to generate new recovery codes.\nForbidden!\n")
          end
        end
      end
    end
  end

  let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => '2fa_recovery_codes' } }

  describe 'without go features' do
    before(:context) do
      write_config(
        "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
      )
    end

    it_behaves_like 'dialog for regenerating recovery keys'
  end

  describe 'with go features' do
    before(:context) do
      write_config(
        "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
        "migration" => { "enabled" => true,
                        "features" => ["2fa_recovery_codes"] }
      )
    end

    it_behaves_like 'dialog for regenerating recovery keys'
  end
end