BigW Consortium Gitlab

git_http_spec.rb 30.1 KB
Newer Older
Douwe Maan committed
1 2
require "spec_helper"

3
describe 'Git HTTP requests', lib: true do
4
  include GitHttpHelpers
5
  include WorkhorseHelpers
Rémy Coutable committed
6
  include UserActivitiesHelpers
7

8 9 10 11 12 13 14 15 16
  shared_examples 'pulls require Basic HTTP Authentication' do
    context "when no credentials are provided" do
      it "responds to downloads with status 401 Unauthorized (no project existence information leak)" do
        download(path) do |response|
          expect(response).to have_http_status(:unauthorized)
          expect(response.header['WWW-Authenticate']).to start_with('Basic ')
        end
      end
    end
Jacob Vosmaer committed
17

18 19 20 21 22 23 24 25
    context "when only username is provided" do
      it "responds to downloads with status 401 Unauthorized" do
        download(path, user: user.username) do |response|
          expect(response).to have_http_status(:unauthorized)
          expect(response.header['WWW-Authenticate']).to start_with('Basic ')
        end
      end
    end
Jacob Vosmaer committed
26

27 28 29 30 31 32 33 34 35
    context "when username and password are provided" do
      context "when authentication fails" do
        it "responds to downloads with status 401 Unauthorized" do
          download(path, user: user.username, password: "wrong-password") do |response|
            expect(response).to have_http_status(:unauthorized)
            expect(response.header['WWW-Authenticate']).to start_with('Basic ')
          end
        end
      end
Douwe Maan committed
36

37 38 39 40 41
      context "when authentication succeeds" do
        it "does not respond to downloads with status 401 Unauthorized" do
          download(path, user: user.username, password: user.password) do |response|
            expect(response).not_to have_http_status(:unauthorized)
            expect(response.header['WWW-Authenticate']).to be_nil
Douwe Maan committed
42 43
          end
        end
44
      end
45 46
    end
  end
Douwe Maan committed
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
  shared_examples 'pushes require Basic HTTP Authentication' do
    context "when no credentials are provided" do
      it "responds to uploads with status 401 Unauthorized (no project existence information leak)" do
        upload(path) do |response|
          expect(response).to have_http_status(:unauthorized)
          expect(response.header['WWW-Authenticate']).to start_with('Basic ')
        end
      end
    end

    context "when only username is provided" do
      it "responds to uploads with status 401 Unauthorized" do
        upload(path, user: user.username) do |response|
          expect(response).to have_http_status(:unauthorized)
          expect(response.header['WWW-Authenticate']).to start_with('Basic ')
        end
      end
    end

    context "when username and password are provided" do
      context "when authentication fails" do
        it "responds to uploads with status 401 Unauthorized" do
          upload(path, user: user.username, password: "wrong-password") do |response|
            expect(response).to have_http_status(:unauthorized)
            expect(response.header['WWW-Authenticate']).to start_with('Basic ')
Douwe Maan committed
73 74
          end
        end
75
      end
76

77 78 79 80 81
      context "when authentication succeeds" do
        it "does not respond to uploads with status 401 Unauthorized" do
          upload(path, user: user.username, password: user.password) do |response|
            expect(response).not_to have_http_status(:unauthorized)
            expect(response.header['WWW-Authenticate']).to be_nil
82 83
          end
        end
84 85
      end
    end
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  end

  shared_examples_for 'pulls are allowed' do
    it do
      download(path, env) do |response|
        expect(response).to have_http_status(:ok)
        expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
      end
    end
  end

  shared_examples_for 'pushes are allowed' do
    it do
      upload(path, env) do |response|
        expect(response).to have_http_status(:ok)
        expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
      end
    end
  end

  describe "User with no identities" do
    let(:user) { create(:user) }
108

109 110
    context "when the project doesn't exist" do
      let(:path) { 'doesnt/exist.git' }
Jacob Vosmaer committed
111

112 113
      it_behaves_like 'pulls require Basic HTTP Authentication'
      it_behaves_like 'pushes require Basic HTTP Authentication'
114

115 116 117 118 119
      context 'when authenticated' do
        it 'rejects downloads and uploads with 404 Not Found' do
          download_or_upload(path, user: user.username, password: user.password) do |response|
            expect(response).to have_http_status(:not_found)
          end
120
        end
121
      end
122 123 124 125 126 127 128 129 130 131
    end

    context "when requesting the Wiki" do
      let(:wiki) { ProjectWiki.new(project) }
      let(:path) { "/#{wiki.repository.path_with_namespace}.git" }

      context "when the project is public" do
        let(:project) { create(:project, :repository, :public, :wiki_enabled) }

        it_behaves_like 'pushes require Basic HTTP Authentication'
132

133 134
        context 'when unauthenticated' do
          let(:env) { {} }
135

136 137 138 139 140 141 142 143 144
          it_behaves_like 'pulls are allowed'

          it "responds to pulls with the wiki's repo" do
            download(path) do |response|
              json_body = ActiveSupport::JSON.decode(response.body)

              expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace)
            end
          end
145 146
        end

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
        context 'when authenticated' do
          let(:env) { { user: user.username, password: user.password } }

          context 'and as a developer on the team' do
            before do
              project.team << [user, :developer]
            end

            context 'but the repo is disabled' do
              let(:project) { create(:project, :repository, :public, :repository_disabled, :wiki_enabled) }

              it_behaves_like 'pulls are allowed'
              it_behaves_like 'pushes are allowed'
            end
          end

          context 'and not on the team' do
            it_behaves_like 'pulls are allowed'

            it 'rejects pushes with 403 Forbidden' do
              upload(path, env) do |response|
                expect(response).to have_http_status(:forbidden)
                expect(response.body).to eq(git_access_wiki_error(:write_to_wiki))
              end
            end
172 173
          end
        end
174
      end
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
      context "when the project is private" do
        let(:project) { create(:project, :repository, :private, :wiki_enabled) }

        it_behaves_like 'pulls require Basic HTTP Authentication'
        it_behaves_like 'pushes require Basic HTTP Authentication'

        context 'when authenticated' do
          context 'and as a developer on the team' do
            before do
              project.team << [user, :developer]
            end

            context 'but the repo is disabled' do
              let(:project) { create(:project, :repository, :private, :repository_disabled, :wiki_enabled) }

              it 'allows clones' do
                download(path, user: user.username, password: user.password) do |response|
                  expect(response).to have_http_status(:ok)
                end
              end

              it 'pushes are allowed' do
                upload(path, user: user.username, password: user.password) do |response|
                  expect(response).to have_http_status(:ok)
                end
              end
            end
          end

          context 'and not on the team' do
            it 'rejects clones with 404 Not Found' do
              download(path, user: user.username, password: user.password) do |response|
                expect(response).to have_http_status(:not_found)
                expect(response.body).to eq(git_access_error(:project_not_found))
              end
            end

            it 'rejects pushes with 404 Not Found' do
              upload(path, user: user.username, password: user.password) do |response|
                expect(response).to have_http_status(:not_found)
                expect(response.body).to eq(git_access_error(:project_not_found))
              end
            end
219 220 221
          end
        end
      end
222 223 224 225
    end

    context "when the project exists" do
      let(:path) { "#{project.path_with_namespace}.git" }
Jacob Vosmaer committed
226

227
      context "when the project is public" do
228
        let(:project) { create(:project, :repository, :public) }
229

230
        it_behaves_like 'pushes require Basic HTTP Authentication'
Jacob Vosmaer committed
231

232 233 234 235
        context 'when not authenticated' do
          let(:env) { {} }

          it_behaves_like 'pulls are allowed'
Jacob Vosmaer committed
236
        end
237

238
        context "when authenticated" do
239
          let(:env) { { user: user.username, password: user.password } }
240

241 242 243
          context 'as a developer on the team' do
            before do
              project.team << [user, :developer]
Jacob Vosmaer committed
244 245
            end

246 247
            it_behaves_like 'pulls are allowed'
            it_behaves_like 'pushes are allowed'
248

249 250 251 252 253 254 255 256
            context 'but git-receive-pack over HTTP is disabled in config' do
              before do
                allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false)
              end

              it 'rejects pushes with 403 Forbidden' do
                upload(path, env) do |response|
                  expect(response).to have_http_status(:forbidden)
257
                  expect(response.body).to eq(git_access_error(:receive_pack_disabled_over_http))
258 259 260 261 262 263 264 265 266 267
                end
              end
            end

            context 'but git-upload-pack over HTTP is disabled in config' do
              it "rejects pushes with 403 Forbidden" do
                allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false)

                download(path, env) do |response|
                  expect(response).to have_http_status(:forbidden)
268
                  expect(response.body).to eq(git_access_error(:upload_pack_disabled_over_http))
269
                end
270 271
              end
            end
272
          end
273

274 275
          context 'and not a member of the team' do
            it_behaves_like 'pulls are allowed'
276

277 278 279 280 281
            it 'rejects pushes with 403 Forbidden' do
              upload(path, env) do |response|
                expect(response).to have_http_status(:forbidden)
                expect(response.body).to eq(change_access_error(:push_code))
              end
282
            end
283 284
          end
        end
285

286 287 288 289 290
        context 'when the request is not from gitlab-workhorse' do
          it 'raises an exception' do
            expect do
              get("/#{project.path_with_namespace}.git/info/refs?service=git-upload-pack")
            end.to raise_error(JWT::DecodeError)
291 292
          end
        end
293 294 295

        context 'when the repo is public' do
          context 'but the repo is disabled' do
296 297 298
            let(:project) { create(:project, :public, :repository, :repository_disabled) }
            let(:path) { "#{project.path_with_namespace}.git" }
            let(:env) { {} }
299

300 301
            it_behaves_like 'pulls require Basic HTTP Authentication'
            it_behaves_like 'pushes require Basic HTTP Authentication'
302 303 304
          end

          context 'but the repo is enabled' do
305 306 307
            let(:project) { create(:project, :public, :repository, :repository_enabled) }
            let(:path) { "#{project.path_with_namespace}.git" }
            let(:env) { {} }
308

309
            it_behaves_like 'pulls are allowed'
310 311 312
          end

          context 'but only project members are allowed' do
313
            let(:project) { create(:project, :public, :repository, :repository_private) }
314

315 316
            it_behaves_like 'pulls require Basic HTTP Authentication'
            it_behaves_like 'pushes require Basic HTTP Authentication'
317 318
          end
        end
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

        context 'and the user requests a redirected path' do
          let!(:redirect) { project.route.create_redirect('foo/bar') }
          let(:path) { "#{redirect.path}.git" }
          let(:project_moved_message) do
            <<-MSG.strip_heredoc
              Project '#{redirect.path}' was moved to '#{project.full_path}'.

              Please update your Git remote and try again:

                git remote set-url origin #{project.http_url_to_repo}
            MSG
          end

          it 'downloads get status 404 with "project was moved" message' do
            clone_get(path, {})
            expect(response).to have_http_status(:not_found)
            expect(response.body).to match(project_moved_message)
          end
        end
Douwe Maan committed
339 340
      end

341
      context "when the project is private" do
342
        let(:project) { create(:project, :repository, :private) }
Douwe Maan committed
343

344 345
        it_behaves_like 'pulls require Basic HTTP Authentication'
        it_behaves_like 'pushes require Basic HTTP Authentication'
Douwe Maan committed
346

347 348
        context "when username and password are provided" do
          let(:env) { { user: user.username, password: 'nope' } }
349

350 351 352 353 354
          context "when authentication fails" do
            context "when the user is IP banned" do
              it "responds with status 401" do
                expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
                allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
Douwe Maan committed
355

356 357
                clone_get(path, env)

358
                expect(response).to have_http_status(:unauthorized)
Douwe Maan committed
359
              end
360
            end
361
          end
Douwe Maan committed
362

363 364
          context "when authentication succeeds" do
            let(:env) { { user: user.username, password: user.password } }
365

366 367 368 369
            context "when the user has access to the project" do
              before do
                project.team << [user, :master]
              end
370

371
              context "when the user is blocked" do
372
                it "rejects pulls with 401 Unauthorized" do
373 374 375 376
                  user.block
                  project.team << [user, :master]

                  download(path, env) do |response|
377
                    expect(response).to have_http_status(:unauthorized)
378 379 380
                  end
                end

381
                it "rejects pulls with 401 Unauthorized for unknown projects (no project existence information leak)" do
382 383 384
                  user.block

                  download('doesnt/exist.git', env) do |response|
385
                    expect(response).to have_http_status(:unauthorized)
386 387
                  end
                end
Douwe Maan committed
388
              end
389

390
              context "when the user isn't blocked" do
391 392
                it "resets the IP in Rack Attack on download" do
                  expect(Rack::Attack::Allow2Ban).to receive(:reset).twice
393

394 395 396 397
                  download(path, env) do
                    expect(response).to have_http_status(:ok)
                    expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
                  end
398
                end
399

400 401 402 403 404
                it "resets the IP in Rack Attack on upload" do
                  expect(Rack::Attack::Allow2Ban).to receive(:reset).twice

                  upload(path, env) do
                    expect(response).to have_http_status(:ok)
405 406 407
                    expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
                  end
                end
408 409 410 411 412 413 414 415

                it 'updates the user last activity', :redis do
                  expect(user_activity(user)).to be_nil

                  download(path, env) do |response|
                    expect(user_activity(user)).to be_present
                  end
                end
416 417
              end

418 419 420
              context "when an oauth token is provided" do
                before do
                  application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
421
                  @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api")
422
                end
423

424 425
                let(:path) { "#{project.path_with_namespace}.git" }
                let(:env) { { user: 'oauth2', password: @token.token } }
426

427 428
                it_behaves_like 'pulls are allowed'
                it_behaves_like 'pushes are allowed'
429 430
              end

431 432 433
              context 'when user has 2FA enabled' do
                let(:user) { create(:user, :two_factor) }
                let(:access_token) { create(:personal_access_token, user: user) }
434
                let(:path) { "#{project.path_with_namespace}.git" }
435

436 437 438
                before do
                  project.team << [user, :master]
                end
439

440
                context 'when username and password are provided' do
441
                  it 'rejects pulls with personal access token error message' do
442 443
                    download(path, user: user.username, password: user.password) do |response|
                      expect(response).to have_http_status(:unauthorized)
444
                      expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
445
                    end
446 447
                  end

448
                  it 'rejects the push attempt with personal access token error message' do
449 450
                    upload(path, user: user.username, password: user.password) do |response|
                      expect(response).to have_http_status(:unauthorized)
451
                      expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
452
                    end
453 454 455
                  end
                end

456
                context 'when username and personal access token are provided' do
457
                  let(:env) { { user: user.username, password: access_token.token } }
458

459 460
                  it_behaves_like 'pulls are allowed'
                  it_behaves_like 'pushes are allowed'
461 462 463
                end
              end

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
              context 'when internal auth is disabled' do
                before do
                  allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
                end

                it 'rejects pulls with personal access token error message' do
                  download(path, user: 'foo', password: 'bar') do |response|
                    expect(response).to have_http_status(:unauthorized)
                    expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
                  end
                end

                it 'rejects pushes with personal access token error message' do
                  upload(path, user: 'foo', password: 'bar') do |response|
                    expect(response).to have_http_status(:unauthorized)
                    expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
                  end
                end

                context 'when LDAP is configured' do
                  before do
                    allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
486 487
                    allow_any_instance_of(Gitlab::LDAP::Authentication)
                      .to receive(:login).and_return(nil)
488 489 490 491 492 493 494 495 496 497 498
                  end

                  it 'does not display the personal access token error message' do
                    upload(path, user: 'foo', password: 'bar') do |response|
                      expect(response).to have_http_status(:unauthorized)
                      expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
                    end
                  end
                end
              end

499 500 501 502 503 504
              context "when blank password attempts follow a valid login" do
                def attempt_login(include_password)
                  password = include_password ? user.password : ""
                  clone_get path, user: user.username, password: password
                  response.status
                end
505

506 507 508 509
                it "repeated attempts followed by successful attempt" do
                  options = Gitlab.config.rack_attack.git_basic_auth
                  maxretry = options[:maxretry] - 1
                  ip = '1.2.3.4'
510

511 512
                  allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
                  Rack::Attack::Allow2Ban.reset(ip, options)
513

514 515 516
                  maxretry.times.each do
                    expect(attempt_login(false)).to eq(401)
                  end
517

518 519
                  expect(attempt_login(true)).to eq(200)
                  expect(Rack::Attack::Allow2Ban.banned?(ip)).to be_falsey
520

521 522 523
                  maxretry.times.each do
                    expect(attempt_login(false)).to eq(401)
                  end
524

525 526
                  Rack::Attack::Allow2Ban.reset(ip, options)
                end
527
              end
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554

              context 'and the user requests a redirected path' do
                let!(:redirect) { project.route.create_redirect('foo/bar') }
                let(:path) { "#{redirect.path}.git" }
                let(:project_moved_message) do
                  <<-MSG.strip_heredoc
                    Project '#{redirect.path}' was moved to '#{project.full_path}'.

                    Please update your Git remote and try again:

                      git remote set-url origin #{project.http_url_to_repo}
                  MSG
                end

                it 'downloads get status 404 with "project was moved" message' do
                  clone_get(path, env)
                  expect(response).to have_http_status(:not_found)
                  expect(response.body).to match(project_moved_message)
                end

                it 'uploads get status 404 with "project was moved" message' do
                  upload(path, env) do |response|
                    expect(response).to have_http_status(:not_found)
                    expect(response.body).to match(project_moved_message)
                  end
                end
              end
Douwe Maan committed
555 556
            end

557
            context "when the user doesn't have access to the project" do
558
              it "pulls get status 404" do
559
                download(path, user: user.username, password: user.password) do |response|
560
                  expect(response).to have_http_status(:not_found)
561
                end
Douwe Maan committed
562
              end
563

564 565
              it "uploads get status 404" do
                upload(path, user: user.username, password: user.password) do |response|
566
                  expect(response).to have_http_status(:not_found)
567
                end
Jacob Vosmaer committed
568 569
              end
            end
Douwe Maan committed
570 571
          end
        end
572

573
        context "when a gitlab ci token is provided" do
574
          let(:project) { create(:project, :repository) }
575 576
          let(:build) { create(:ci_build, :running) }
          let(:other_project) { create(:empty_project) }
577

578 579 580 581
          before do
            build.update!(project: project) # can't associate it on factory create
          end

582
          context 'when build created by system is authenticated' do
583 584
            let(:path) { "#{project.path_with_namespace}.git" }
            let(:env) { { user: 'gitlab-ci-token', password: build.token } }
585

586
            it_behaves_like 'pulls are allowed'
587

588 589 590 591 592 593 594 595
            # A non-401 here is not an information leak since the system is
            # "authenticated" as CI using the correct token. It does not have
            # push access, so pushes should be rejected as forbidden, and giving
            # a reason is fine.
            #
            # We know for sure it is not an information leak since pulls using
            # the build token must be allowed.
            it "rejects pushes with 403 Forbidden" do
596
              push_get(path, env)
597

598 599
              expect(response).to have_http_status(:forbidden)
              expect(response.body).to eq(git_access_error(:upload))
600
            end
601

602 603
            # We are "authenticated" as CI using a valid token here. But we are
            # not authorized to see any other project, so return "not found".
604 605
            it "rejects pulls for other project with 404 Not Found" do
              clone_get("#{other_project.path_with_namespace}.git", env)
606

607
              expect(response).to have_http_status(:not_found)
608
              expect(response.body).to eq(git_access_error(:project_not_found))
609
            end
610 611
          end

612 613 614 615 616
          context 'and build created by' do
            before do
              build.update(user: user)
              project.team << [user, :reporter]
            end
617

618
            shared_examples 'can download code only' do
619 620
              let(:path) { "#{project.path_with_namespace}.git" }
              let(:env) { { user: 'gitlab-ci-token', password: build.token } }
621

622
              it_behaves_like 'pulls are allowed'
623

624 625
              context 'when the repo does not exist' do
                let(:project) { create(:empty_project) }
626

627 628
                it 'rejects pulls with 403 Forbidden' do
                  clone_get path, env
629

630 631 632
                  expect(response).to have_http_status(:forbidden)
                  expect(response.body).to eq(git_access_error(:no_repo))
                end
633 634
              end

635 636
              it 'rejects pushes with 403 Forbidden' do
                push_get path, env
637

638 639
                expect(response).to have_http_status(:forbidden)
                expect(response.body).to eq(git_access_error(:upload))
640
              end
641 642
            end

643 644
            context 'administrator' do
              let(:user) { create(:admin) }
645

646
              it_behaves_like 'can download code only'
647

648 649
              it 'downloads from other project get status 403' do
                clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
650

651
                expect(response).to have_http_status(:forbidden)
652 653 654 655 656 657 658 659 660 661 662
              end
            end

            context 'regular user' do
              let(:user) { create(:user) }

              it_behaves_like 'can download code only'

              it 'downloads from other project get status 404' do
                clone_get "#{other_project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token

663
                expect(response).to have_http_status(:not_found)
664
              end
665 666
            end
          end
667
        end
Douwe Maan committed
668
      end
Jacob Vosmaer committed
669

670 671 672 673 674
      context "when the project path doesn't end in .git" do
        let(:project) { create(:project, :repository, :public, path: 'project.git-project') }

        context "GET info/refs" do
          let(:path) { "/#{project.path_with_namespace}/info/refs" }
675

676
          context "when no params are added" do
677 678 679
            before do
              get path
            end
680

681 682 683
            it "redirects to the .git suffix version" do
              expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs")
            end
684
          end
685

686 687
          context "when the upload-pack service is requested" do
            let(:params) { { service: 'git-upload-pack' } }
688 689 690 691

            before do
              get path, params
            end
692

693 694 695
            it "redirects to the .git suffix version" do
              expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
            end
696
          end
697

698 699
          context "when the receive-pack service is requested" do
            let(:params) { { service: 'git-receive-pack' } }
700 701 702 703

            before do
              get path, params
            end
704

705 706 707
            it "redirects to the .git suffix version" do
              expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
            end
708
          end
709

710 711
          context "when the params are anything else" do
            let(:params) { { service: 'git-implode-pack' } }
712 713 714 715

            before do
              get path, params
            end
716

717 718 719
            it "redirects to the sign-in page" do
              expect(response).to redirect_to(new_user_session_path)
            end
720
          end
721 722
        end

723 724 725 726
        context "POST git-upload-pack" do
          it "fails to find a route" do
            expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
          end
727
        end
728

729
        context "POST git-receive-pack" do
730
          it "fails to find a route" do
731 732
            expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
          end
733
        end
734 735
      end

736 737 738 739 740 741 742 743
      context "retrieving an info/refs file" do
        let(:project) { create(:project, :repository, :public) }

        context "when the file exists" do
          before do
            # Provide a dummy file in its place
            allow_any_instance_of(Repository).to receive(:blob_at).and_call_original
            allow_any_instance_of(Repository).to receive(:blob_at).with('b83d6e391c22777fca1ed3012fce84f633d7fed0', 'info/refs') do
744
              Blob.decorate(Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt'), project)
745
            end
746

747
            get "/#{project.path_with_namespace}/blob/master/info/refs"
748
          end
749

750 751 752
          it "returns the file" do
            expect(response).to have_http_status(:ok)
          end
753
        end
754

755
        context "when the file does not exist" do
756 757 758
          before do
            get "/#{project.path_with_namespace}/blob/master/info/refs"
          end
759

760 761 762
          it "returns not found" do
            expect(response).to have_http_status(:not_found)
          end
763
        end
764 765 766 767
      end
    end
  end

768 769 770
  describe "User with LDAP identity" do
    let(:user) { create(:omniauth_user, extern_uid: dn) }
    let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
771
    let(:path) { 'doesnt/exist.git' }
Jacob Vosmaer committed
772

773 774 775 776 777
    before do
      allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
      allow(Gitlab::LDAP::Authentication).to receive(:login).and_return(nil)
      allow(Gitlab::LDAP::Authentication).to receive(:login).with(user.username, user.password).and_return(user)
    end
778

779 780
    it_behaves_like 'pulls require Basic HTTP Authentication'
    it_behaves_like 'pushes require Basic HTTP Authentication'
781

782 783
    context "when authentication succeeds" do
      context "when the project doesn't exist" do
784 785 786
        it "responds with status 404 Not Found" do
          download(path, user: user.username, password: user.password) do |response|
            expect(response).to have_http_status(:not_found)
787 788 789
          end
        end
      end
Jacob Vosmaer committed
790

791
      context "when the project exists" do
792 793 794
        let(:project) { create(:project, :repository) }
        let(:path) { "#{project.full_path}.git" }
        let(:env) { { user: user.username, password: user.password } }
Jacob Vosmaer committed
795

796 797 798 799
        context 'and the user is on the team' do
          before do
            project.team << [user, :master]
          end
Jacob Vosmaer committed
800

801 802 803 804
          it "responds with status 200" do
            clone_get(path, env) do |response|
              expect(response).to have_http_status(200)
            end
805
          end
806 807 808

          it_behaves_like 'pulls are allowed'
          it_behaves_like 'pushes are allowed'
809 810
        end
      end
811 812
    end
  end
Douwe Maan committed
813
end