File: authorization_spec.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (145 lines) | stat: -rw-r--r-- 4,623 bytes parent folder | download
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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Ci::JobToken::Authorization, feature_category: :secrets_management do
  let_it_be(:origin_project) { create(:project) }
  let_it_be(:accessed_project) { create(:project) }
  let_it_be(:another_project) { create(:project) }

  describe 'associations' do
    it { is_expected.to belong_to(:origin_project).class_name('Project') }
    it { is_expected.to belong_to(:accessed_project).class_name('Project') }
  end

  describe '.capture', :request_store do
    subject(:capture) do
      described_class.capture(origin_project: origin_project, accessed_project: accessed_project)
    end

    context 'when no authorizations have been captured' do
      it 'captures the authorization in the RequestStore' do
        capture
        expect(described_class.captured_authorizations).to eq(
          origin_project_id: origin_project.id,
          accessed_project_id: accessed_project.id)
      end

      context 'when origin project is the same as the accessed project' do
        let(:accessed_project) { origin_project }

        it 'does not capture the authorization' do
          capture
          expect(described_class.captured_authorizations).to be_nil
        end
      end
    end
  end

  describe '.log_captures_async', :request_store do
    subject(:log_captures_async) do
      described_class.log_captures_async
    end

    shared_examples 'does not log the authorization' do
      it 'does not schedule the worker' do
        expect(Ci::JobToken::LogAuthorizationWorker).not_to receive(:perform_in)

        log_captures_async
      end
    end

    context 'when authorizations have been captured during the request' do
      before do
        described_class.capture(
          origin_project: origin_project,
          accessed_project: accessed_project)
      end

      context 'when authorization is cross project' do
        it 'schedules the log' do
          expect(::Ci::JobToken::LogAuthorizationWorker)
            .to receive(:perform_in).with(5.minutes, accessed_project.id, origin_project.id)

          log_captures_async
        end
      end

      context 'when authorization is self-referential' do
        let(:accessed_project) { origin_project }

        it_behaves_like 'does not log the authorization'
      end
    end

    context 'when authorizations have not been captured during the request' do
      it_behaves_like 'does not log the authorization'
    end
  end

  describe '.log_captures!' do
    subject(:log_captures) do
      described_class.log_captures!(origin_project_id: origin_project.id, accessed_project_id: accessed_project.id)
    end

    context 'when authorization does not exist in the database' do
      it 'creates a new authorization' do
        expect { log_captures }.to change { described_class.count }.by(1)

        expect(described_class.last).to have_attributes(
          origin_project: origin_project,
          accessed_project: accessed_project)
      end
    end

    context 'when authorization for the same projects already exists in the database' do
      let!(:existing_authorization) do
        create(:ci_job_token_authorization,
          origin_project: origin_project,
          accessed_project: accessed_project,
          last_authorized_at: 1.day.ago)
      end

      it 'updates the timestamp instead of creating a new record' do
        expect { log_captures }
          .to change { existing_authorization.reload.last_authorized_at }
          .and not_change { described_class.count }
      end
    end
  end

  describe '.preload_origin_project' do
    before do
      create_list(:ci_job_token_authorization, 5)
    end

    it 'does not perform N+1 queries' do
      control = ActiveRecord::QueryRecorder.new do
        described_class.preload_origin_project.map { |a| a.origin_project.full_path }
      end

      create(:ci_job_token_authorization)

      expect do
        described_class.preload_origin_project.map { |a| a.origin_project.full_path }
      end.not_to exceed_query_limit(control)
    end
  end

  describe '.for_project scope' do
    let(:project) { create(:project) }

    let!(:current_authorizations) do
      create_list(:ci_job_token_authorization, 2, accessed_project: project)
    end

    let!(:other_authorization) { create(:ci_job_token_authorization) }

    it 'contains only the authorizations targeting the project' do
      authorizations = described_class.for_project(project)
      expect(authorizations).to eq(current_authorizations)

      expect(authorizations).not_to include(other_authorization)
    end
  end
end