git_access_spec.rb 24 KB
Newer Older
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
1 2
require 'spec_helper'

3
describe Gitlab::GitAccess do
4 5
  set(:user) { create(:user) }

6
  let(:actor) { user }
7
  let(:project) { create(:project, :repository) }
8
  let(:protocol) { 'ssh' }
9
  let(:authentication_abilities) { %i[read_project download_code push_code] }
10
  let(:redirected_path) { nil }
11 12 13 14

  let(:access) { described_class.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
  let(:push_access_check) { access.check('git-receive-pack', '_any') }
  let(:pull_access_check) { access.check('git-upload-pack', '_any') }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
15

16 17
  describe '#check with single protocols allowed' do
    def disable_protocol(protocol)
18
      allow(Gitlab::ProtocolAccess).to receive(:allowed?).with(protocol).and_return(false)
19 20 21 22 23 24 25
    end

    context 'ssh disabled' do
      before do
        disable_protocol('ssh')
      end

26 27 28 29 30
      it 'blocks ssh git push and pull' do
        aggregate_failures do
          expect { push_access_check }.to raise_unauthorized('Git access over SSH is not allowed')
          expect { pull_access_check }.to raise_unauthorized('Git access over SSH is not allowed')
        end
31 32 33 34
      end
    end

    context 'http disabled' do
Michael Kozono's avatar
Michael Kozono committed
35 36
      let(:protocol) { 'http' }

37 38 39 40
      before do
        disable_protocol('http')
      end

41 42 43 44 45
      it 'blocks http push and pull' do
        aggregate_failures do
          expect { push_access_check }.to raise_unauthorized('Git access over HTTP is not allowed')
          expect { pull_access_check }.to raise_unauthorized('Git access over HTTP is not allowed')
        end
46 47 48 49
      end
    end
  end

50 51 52 53 54 55 56 57
  describe '#check_project_accessibility!' do
    context 'when the project exists' do
      context 'when actor exists' do
        context 'when actor is a DeployKey' do
          let(:deploy_key) { create(:deploy_key, user: user, can_push: true) }
          let(:actor) { deploy_key }

          context 'when the DeployKey has access to the project' do
58 59 60
            before do
              deploy_key.projects << project
            end
61

62 63 64 65 66
            it 'allows push and pull access' do
              aggregate_failures do
                expect { push_access_check }.not_to raise_error
                expect { pull_access_check }.not_to raise_error
              end
67 68 69 70
            end
          end

          context 'when the Deploykey does not have access to the project' do
71 72
            it 'blocks push and pull with "not found"' do
              aggregate_failures do
73 74
                expect { push_access_check }.to raise_not_found
                expect { pull_access_check }.to raise_not_found
75
              end
76 77 78 79 80 81
            end
          end
        end

        context 'when actor is a User' do
          context 'when the User can read the project' do
82
            before do
83
              project.add_master(user)
84
            end
85

86 87 88 89 90
            it 'allows push and pull access' do
              aggregate_failures do
                expect { pull_access_check }.not_to raise_error
                expect { push_access_check }.not_to raise_error
              end
91 92 93 94
            end
          end

          context 'when the User cannot read the project' do
95 96
            it 'blocks push and pull with "not found"' do
              aggregate_failures do
97 98
                expect { push_access_check }.to raise_not_found
                expect { pull_access_check }.to raise_not_found
99
              end
100 101 102 103 104 105 106 107 108 109
            end
          end
        end

        # For backwards compatibility
        context 'when actor is :ci' do
          let(:actor) { :ci }
          let(:authentication_abilities) { build_authentication_abilities }

          it 'allows pull access' do
110
            expect { pull_access_check }.not_to raise_error
111 112 113
          end

          it 'does not block pushes with "not found"' do
114
            expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload])
115 116 117 118 119 120 121 122 123 124 125
          end
        end
      end

      context 'when actor is nil' do
        let(:actor) { nil }

        context 'when guests can read the project' do
          let(:project) { create(:project, :repository, :public) }

          it 'allows pull access' do
126
            expect { pull_access_check }.not_to raise_error
127 128 129
          end

          it 'does not block pushes with "not found"' do
130
            expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload])
131 132 133 134 135
          end
        end

        context 'when guests cannot read the project' do
          it 'blocks pulls with "not found"' do
136
            expect { pull_access_check }.to raise_not_found
137 138 139
          end

          it 'blocks pushes with "not found"' do
140
            expect { push_access_check }.to raise_not_found
141 142 143 144 145 146 147 148
          end
        end
      end
    end

    context 'when the project is nil' do
      let(:project) { nil }

149 150
      it 'blocks push and pull with "not found"' do
        aggregate_failures do
151 152
          expect { pull_access_check }.to raise_not_found
          expect { push_access_check }.to raise_not_found
153
        end
154 155 156 157
      end
    end
  end

158 159 160 161 162 163 164
  shared_examples '#check with a key that is not valid' do
    before do
      project.add_master(user)
    end

    context 'key is too small' do
      before do
165
        stub_application_setting(rsa_key_restriction: 4096)
166 167
      end

Nick Thomas's avatar
Nick Thomas committed
168 169 170 171
      it 'does not allow keys which are too small', aggregate_failures: true do
        expect(actor).not_to be_valid
        expect { pull_access_check }.to raise_unauthorized('Your SSH key must be at least 4096 bits.')
        expect { push_access_check }.to raise_unauthorized('Your SSH key must be at least 4096 bits.')
172 173 174 175 176
      end
    end

    context 'key type is not allowed' do
      before do
177
        stub_application_setting(rsa_key_restriction: ApplicationSetting::FORBIDDEN_KEY_VALUE)
178 179
      end

Nick Thomas's avatar
Nick Thomas committed
180 181 182 183
      it 'does not allow keys which are too small', aggregate_failures: true do
        expect(actor).not_to be_valid
        expect { pull_access_check }.to raise_unauthorized(/Your SSH key type is forbidden/)
        expect { push_access_check }.to raise_unauthorized(/Your SSH key type is forbidden/)
184 185 186 187 188 189 190 191 192 193 194 195
      end
    end
  end

  it_behaves_like '#check with a key that is not valid' do
    let(:actor) { build(:rsa_key_2048, user: user) }
  end

  it_behaves_like '#check with a key that is not valid' do
    let(:actor) { build(:rsa_deploy_key_2048, user: user) }
  end

196 197
  describe '#check_project_moved!' do
    before do
198
      project.add_master(user)
199 200 201
    end

    context 'when a redirect was not followed to find the project' do
202 203 204 205 206
      it 'allows push and pull access' do
        aggregate_failures do
          expect { push_access_check }.not_to raise_error
          expect { pull_access_check }.not_to raise_error
        end
207 208 209 210 211 212
      end
    end

    context 'when a redirect was followed to find the project' do
      let(:redirected_path) { 'some/other-path' }

213 214
      it 'blocks push and pull access' do
        aggregate_failures do
215 216
          expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/)
          expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
217

218 219
          expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/)
          expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
220 221 222
        end
      end

223 224
      context 'http protocol' do
        let(:protocol) { 'http' }
225

226 227
        it 'includes the path to the project using HTTP' do
          aggregate_failures do
228 229
            expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
            expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
230
          end
231 232 233 234 235
        end
      end
    end
  end

236
  describe '#check_command_disabled!' do
237 238 239
    before do
      project.team << [user, :master]
    end
240 241 242 243 244 245 246 247 248 249

    context 'over http' do
      let(:protocol) { 'http' }

      context 'when the git-upload-pack command is disabled in config' do
        before do
          allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false)
        end

        context 'when calling git-upload-pack' do
Michael Kozono's avatar
Michael Kozono committed
250
          it { expect { pull_access_check }.to raise_unauthorized('Pulling over HTTP is not allowed.') }
251 252 253
        end

        context 'when calling git-receive-pack' do
254
          it { expect { push_access_check }.not_to raise_error }
255 256 257 258 259 260 261 262 263
        end
      end

      context 'when the git-receive-pack command is disabled in config' do
        before do
          allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false)
        end

        context 'when calling git-receive-pack' do
Michael Kozono's avatar
Michael Kozono committed
264
          it { expect { push_access_check }.to raise_unauthorized('Pushing over HTTP is not allowed.') }
265 266 267
        end

        context 'when calling git-upload-pack' do
268
          it { expect { pull_access_check }.not_to raise_error }
269 270 271 272 273
        end
      end
    end
  end

274
  describe '#check_download_access!' do
275 276
    it 'allows masters to pull' do
      project.add_master(user)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
277

278
      expect { pull_access_check }.not_to raise_error
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
279 280
    end

281 282
    it 'disallows guests to pull' do
      project.add_guest(user)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
283

284
      expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download])
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
285 286
    end

287 288 289
    it 'disallows blocked users to pull' do
      project.add_master(user)
      user.block
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
290

291
      expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.')
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
292 293
    end

294
    describe 'without access to project' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
295
      context 'pull code' do
296
        it { expect { pull_access_check }.to raise_not_found }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
297
      end
298 299

      context 'when project is public' do
300
        let(:public_project) { create(:project, :public, :repository) }
301
        let(:access) { described_class.new(nil, public_project, 'web', authentication_abilities: []) }
302 303 304

        context 'when repository is enabled' do
          it 'give access to download code' do
305
            expect { pull_access_check }.not_to raise_error
306 307 308 309 310 311 312
          end
        end

        context 'when repository is disabled' do
          it 'does not give access to download code' do
            public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)

313
            expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download])
314 315 316
          end
        end
      end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
317
    end
318 319

    describe 'deploy key permissions' do
320
      let(:key) { create(:deploy_key, user: user) }
321
      let(:actor) { key }
322 323

      context 'pull code' do
324
        context 'when project is authorized' do
325 326 327
          before do
            key.projects << project
          end
328

329
          it { expect { pull_access_check }.not_to raise_error }
330 331 332 333
        end

        context 'when unauthorized' do
          context 'from public project' do
334
            let(:project) { create(:project, :public, :repository) }
335

336
            it { expect { pull_access_check }.not_to raise_error }
337 338 339
          end

          context 'from internal project' do
340
            let(:project) { create(:project, :internal, :repository) }
341

342
            it { expect { pull_access_check }.to raise_not_found }
343 344 345
          end

          context 'from private project' do
346
            let(:project) { create(:project, :private, :repository) }
347

348
            it { expect { pull_access_check }.to raise_not_found }
349 350
          end
        end
351 352
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
353

354 355
    describe 'build authentication_abilities permissions' do
      let(:authentication_abilities) { build_authentication_abilities }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
356

357
      describe 'owner' do
358
        let(:project) { create(:project, :repository, namespace: user.namespace) }
359 360

        context 'pull code' do
361
          it { expect { pull_access_check }.not_to raise_error }
362 363 364
        end
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
365
      describe 'reporter user' do
366 367 368
        before do
          project.team << [user, :reporter]
        end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
369 370

        context 'pull code' do
371
          it { expect { pull_access_check }.not_to raise_error }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
372 373 374 375 376 377 378
        end
      end

      describe 'admin user' do
        let(:user) { create(:admin) }

        context 'when member of the project' do
379 380 381
          before do
            project.team << [user, :reporter]
          end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
382 383

          context 'pull code' do
384
            it { expect { pull_access_check }.not_to raise_error }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
385 386 387 388 389
          end
        end

        context 'when is not member of the project' do
          context 'pull code' do
390
            it { expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download]) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
391 392 393
          end
        end
      end
394 395 396 397 398

      describe 'generic CI (build without a user)' do
        let(:actor) { :ci }

        context 'pull code' do
399
          it { expect { pull_access_check }.not_to raise_error }
400 401
        end
      end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
402
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
403 404
  end

405
  describe '#check_push_access!' do
406 407 408
    before do
      merge_into_protected_branch
    end
409
    let(:unprotected_branch) { 'unprotected_branch' }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
410

411 412
    let(:changes) do
      { push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
413 414
        push_master: '6f6d7e7ed 570e7b2ab refs/heads/master',
        push_protected_branch: '6f6d7e7ed 570e7b2ab refs/heads/feature',
415 416
        push_remove_protected_branch: "570e7b2ab #{Gitlab::Git::BLANK_SHA} "\
                                      'refs/heads/feature',
417
        push_tag: '6f6d7e7ed 570e7b2ab refs/tags/v1.0.0',
418
        push_new_tag: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/tags/v7.8.9",
419 420
        push_all: ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'],
        merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
421
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
422

423 424
    def stub_git_hooks
      # Running the `pre-receive` hook is expensive, and not necessary for this test.
425
      allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block|
Sean McGivern's avatar
Sean McGivern committed
426 427
        block.call(service)
      end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
428
    end
429

430 431 432 433 434
    def merge_into_protected_branch
      @protected_branch_merge_commit ||= begin
        stub_git_hooks
        project.repository.add_branch(user, unprotected_branch, 'feature')
        target_branch = project.repository.lookup('feature')
435
        source_branch = project.repository.create_file(
436
          user,
437
          'filename',
438 439
          'This is the file content',
          message: 'This is a good commit message',
440
          branch_name: unprotected_branch)
441 442
        rugged = project.repository.rugged
        author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
443

444 445
        merge_index = rugged.merge_commits(target_branch, source_branch)
        Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged))
446 447
      end
    end
448

449
    def self.run_permission_checks(permissions_matrix)
450 451 452 453 454 455 456 457 458 459 460
      permissions_matrix.each_pair do |role, matrix|
        # Run through the entire matrix for this role in one test to avoid
        # repeated setup.
        #
        # Expectations are given a custom failure message proc so that it's
        # easier to identify which check(s) failed.
        it "has the correct permissions for #{role}s" do
          if role == :admin
            user.update_attribute(:admin, true)
          else
            project.team << [user, role]
Sean McGivern's avatar
Sean McGivern committed
461 462
          end

463 464 465
          aggregate_failures do
            matrix.each do |action, allowed|
              check = -> { access.send(:check_push_access!, changes[action]) }
466

467 468 469 470 471 472
              if allowed
                expect(&check).not_to raise_error,
                  -> { "expected #{action} to be allowed" }
              else
                expect(&check).to raise_error(Gitlab::GitAccess::UnauthorizedError),
                  -> { "expected #{action} to be disallowed" }
473
              end
474 475 476 477 478
            end
          end
        end
      end
    end
479 480

    permissions_matrix = {
481 482 483 484 485 486 487 488 489 490 491
      admin: {
        push_new_branch: true,
        push_master: true,
        push_protected_branch: true,
        push_remove_protected_branch: false,
        push_tag: true,
        push_new_tag: true,
        push_all: true,
        merge_into_protected_branch: true
      },

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
      master: {
        push_new_branch: true,
        push_master: true,
        push_protected_branch: true,
        push_remove_protected_branch: false,
        push_tag: true,
        push_new_tag: true,
        push_all: true,
        merge_into_protected_branch: true
      },

      developer: {
        push_new_branch: true,
        push_master: true,
        push_protected_branch: false,
        push_remove_protected_branch: false,
        push_tag: false,
        push_new_tag: true,
        push_all: false,
        merge_into_protected_branch: false
      },

      reporter: {
        push_new_branch: false,
        push_master: false,
        push_protected_branch: false,
        push_remove_protected_branch: false,
        push_tag: false,
        push_new_tag: false,
        push_all: false,
        merge_into_protected_branch: false
      },

      guest: {
        push_new_branch: false,
        push_master: false,
        push_protected_branch: false,
        push_remove_protected_branch: false,
        push_tag: false,
        push_new_tag: false,
        push_all: false,
        merge_into_protected_branch: false
      }
    }

Douwe Maan's avatar
Douwe Maan committed
537
    [%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type|
538
      context do
539 540 541
        before do
          create(:protected_branch, name: protected_branch_name, project: project)
        end
542 543 544 545

        run_permission_checks(permissions_matrix)
      end

546
      context "when developers are allowed to push into the #{protected_branch_type} protected branch" do
547 548 549
        before do
          create(:protected_branch, :developers_can_push, name: protected_branch_name, project: project)
        end
550 551 552 553

        run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
      end

554
      context "developers are allowed to merge into the #{protected_branch_type} protected branch" do
555 556 557
        before do
          create(:protected_branch, :developers_can_merge, name: protected_branch_name, project: project)
        end
558 559 560 561

        context "when a merge request exists for the given source/target branch" do
          context "when the merge request is in progress" do
            before do
562
              create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature',
563
                                     state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
564 565
            end

566 567
            run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: true }))
          end
568

569 570 571
          context "when the merge request is not in progress" do
            before do
              create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', in_progress_merge_commit_sha: nil)
572
            end
573 574

            run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: false }))
575
          end
576

577
          context "when a merge request does not exist for the given source/target branch" do
578 579 580 581 582
            run_permission_checks(permissions_matrix.deep_merge(developer: { merge_into_protected_branch: false }))
          end
        end
      end

583
      context "when developers are allowed to push and merge into the #{protected_branch_type} protected branch" do
584 585 586
        before do
          create(:protected_branch, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project)
        end
587 588 589

        run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
      end
590

591
      context "when no one is allowed to push to the #{protected_branch_name} protected branch" do
592 593 594
        before do
          create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project)
        end
595

596 597 598 599
        run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
                                                            master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
                                                            admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }))
      end
600 601
    end
  end
602

603 604
  describe 'build authentication abilities' do
    let(:authentication_abilities) { build_authentication_abilities }
605

Kamil Trzcinski's avatar
Kamil Trzcinski committed
606
    context 'when project is authorized' do
607 608 609
      before do
        project.team << [user, :reporter]
      end
610

611
      it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
612 613 614 615
    end

    context 'when unauthorized' do
      context 'to public project' do
616
        let(:project) { create(:project, :public, :repository) }
617

618
        it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) }
619
      end
620

Kamil Trzcinski's avatar
Kamil Trzcinski committed
621
      context 'to internal project' do
622
        let(:project) { create(:project, :internal, :repository) }
623

624
        it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
625
      end
626

Kamil Trzcinski's avatar
Kamil Trzcinski committed
627
      context 'to private project' do
628
        let(:project) { create(:project, :private, :repository) }
629

630
        it { expect { push_access_check }.to raise_not_found }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
631 632
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
633 634 635
  end

  describe 'deploy key permissions' do
636
    let(:key) { create(:deploy_key, user: user, can_push: can_push) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
637 638
    let(:actor) { key }

639 640 641
    context 'when deploy_key can push' do
      let(:can_push) { true }

642
      context 'when project is authorized' do
643 644 645
        before do
          key.projects << project
        end
646 647 648 649 650 651 652 653

        it { expect { push_access_check }.not_to raise_error }
      end

      context 'when unauthorized' do
        context 'to public project' do
          let(:project) { create(:project, :public, :repository) }

654
          it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) }
655 656 657 658 659
        end

        context 'to internal project' do
          let(:project) { create(:project, :internal, :repository) }

660
          it { expect { push_access_check }.to raise_not_found }
661 662 663 664 665
        end

        context 'to private project' do
          let(:project) { create(:project, :private, :repository) }

666
          it { expect { push_access_check }.to raise_not_found }
667 668 669 670 671 672 673
        end
      end
    end

    context 'when deploy_key cannot push' do
      let(:can_push) { false }

674
      context 'when project is authorized' do
675 676 677
        before do
          key.projects << project
        end
678

679
        it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) }
680 681 682 683 684 685
      end

      context 'when unauthorized' do
        context 'to public project' do
          let(:project) { create(:project, :public, :repository) }

686
          it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) }
687 688 689 690 691
        end

        context 'to internal project' do
          let(:project) { create(:project, :internal, :repository) }

692
          it { expect { push_access_check }.to raise_not_found }
693 694 695 696 697
        end

        context 'to private project' do
          let(:project) { create(:project, :private, :repository) }

698
          it { expect { push_access_check }.to raise_not_found }
699
        end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
700 701
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
702 703 704 705
  end

  private

706 707 708 709
  def raise_unauthorized(message)
    raise_error(Gitlab::GitAccess::UnauthorizedError, message)
  end

710 711 712
  def raise_not_found
    raise_error(Gitlab::GitAccess::NotFoundError,
                Gitlab::GitAccess::ERROR_MESSAGES[:project_not_found])
713 714
  end

715
  def build_authentication_abilities
Kamil Trzcinski's avatar
Kamil Trzcinski committed
716 717 718 719 720
    [
      :read_project,
      :build_download_code
    ]
  end
721

722
  def full_authentication_abilities
723 724 725 726 727 728
    [
      :read_project,
      :download_code,
      :push_code
    ]
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
729
end