summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/middleware/go_spec.rb
blob: 7a3a9ab875b0c74af6941c69447c24f850561eb3 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
require 'spec_helper'

describe Gitlab::Middleware::Go do
  let(:app) { double(:app) }
  let(:middleware) { described_class.new(app) }
  let(:env) do
    {
      'rack.input' => '',
      'REQUEST_METHOD' => 'GET'
    }
  end

  describe '#call' do
    describe 'when go-get=0' do
      before do
        env['QUERY_STRING'] = 'go-get=0'
      end

      it 'skips go-import generation' do
        expect(app).to receive(:call).with(env).and_return('no-go')
        middleware.call(env)
      end
    end

    describe 'when go-get=1' do
      before do
        env['QUERY_STRING'] = 'go-get=1'
        env['PATH_INFO'] = "/#{path}"
      end

      shared_examples 'go-get=1' do |enabled_protocol:|
        context 'with simple 2-segment project path' do
          let!(:project) { create(:project, :private) }

          context 'with subpackages' do
            let(:path) { "#{project.full_path}/subpackage" }

            it 'returns the full project path' do
              expect_response_with_path(go, enabled_protocol, project.full_path)
            end
          end

          context 'without subpackages' do
            let(:path) { project.full_path }

            it 'returns the full project path' do
              expect_response_with_path(go, enabled_protocol, project.full_path)
            end
          end
        end

        context 'with a nested project path' do
          let(:group) { create(:group, :nested) }
          let!(:project) { create(:project, :public, namespace: group) }

          shared_examples 'a nested project' do
            context 'when the project is public' do
              it 'returns the full project path' do
                expect_response_with_path(go, enabled_protocol, project.full_path)
              end
            end

            context 'when the project is private' do
              before do
                project.update_attribute(:visibility_level, Project::PRIVATE)
              end

              shared_examples 'unauthorized' do
                it 'returns the 2-segment group path' do
                  expect_response_with_path(go, enabled_protocol, group.full_path)
                end
              end

              context 'when not authenticated' do
                it_behaves_like 'unauthorized'
              end

              context 'when authenticated' do
                let(:current_user) { project.creator }

                before do
                  project.team.add_maintainer(current_user)
                end

                shared_examples 'authenticated' do
                  context 'with access to the project' do
                    it 'returns the full project path' do
                      expect_response_with_path(go, enabled_protocol, project.full_path)
                    end
                  end

                  context 'without access to the project' do
                    before do
                      project.team.find_member(current_user).destroy
                    end

                    it_behaves_like 'unauthorized'
                  end
                end

                context 'using warden' do
                  before do
                    env['warden'] = double(authenticate: current_user)
                  end

                  context 'when active' do
                    it_behaves_like 'authenticated'
                  end

                  context 'when blocked' do
                    before do
                      current_user.block!
                    end

                    it_behaves_like 'unauthorized'
                  end
                end

                context 'using a personal access token' do
                  let(:personal_access_token) { create(:personal_access_token, user: current_user) }

                  before do
                    env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token
                  end

                  context 'with api scope' do
                    it_behaves_like 'authenticated'
                  end

                  context 'with read_user scope' do
                    before do
                      personal_access_token.update_attribute(:scopes, [:read_user])
                    end

                    it_behaves_like 'unauthorized'
                  end
                end
              end
            end
          end

          context 'with subpackages' do
            let(:path) { "#{project.full_path}/subpackage" }

            it_behaves_like 'a nested project'
          end

          context 'with a subpackage that is not a valid project path' do
            let(:path) { "#{project.full_path}/---subpackage" }

            it_behaves_like 'a nested project'
          end

          context 'without subpackages' do
            let(:path) { project.full_path }

            it_behaves_like 'a nested project'
          end
        end

        context 'with a bogus path' do
          let(:path) { "http:;url=http://www.example.com'http-equiv='refresh'x='?go-get=1" }

          it 'skips go-import generation' do
            expect(app).to receive(:call).and_return('no-go')

            go
          end
        end
      end

      context 'with SSH disabled' do
        before do
          stub_application_setting(enabled_git_access_protocol: 'http')
        end

        include_examples 'go-get=1', enabled_protocol: :http
      end

      context 'with HTTP disabled' do
        before do
          stub_application_setting(enabled_git_access_protocol: 'ssh')
        end

        include_examples 'go-get=1', enabled_protocol: :ssh
      end

      context 'with nothing disabled' do
        before do
          stub_application_setting(enabled_git_access_protocol: nil)
        end

        include_examples 'go-get=1', enabled_protocol: nil
      end

      context 'with nothing disabled (blank string)' do
        before do
          stub_application_setting(enabled_git_access_protocol: '')
        end

        include_examples 'go-get=1', enabled_protocol: nil
      end
    end

    def go
      middleware.call(env)
    end

    def expect_response_with_path(response, protocol, path)
      repository_url = case protocol
                       when :ssh
                         "ssh://git@#{Gitlab.config.gitlab.host}/#{path}.git"
                       when :http, nil
                         "http://#{Gitlab.config.gitlab.host}/#{path}.git"
                       end
      expect(response[0]).to eq(200)
      expect(response[1]['Content-Type']).to eq('text/html')
      expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /></head></html>}
      expect(response[2].body).to eq([expected_body])
    end
  end
end