Commit 89dc20ad authored by Hordur Freyr Yngvason's avatar Hordur Freyr Yngvason Committed by Steve Abrams

Add License checks to AgentAuthorizationsFinder

The CI/CD tunnel's ci_user and ci_job impersonation features are
EE-only, so these should only be returned when the license is available.
We check the license on the CI job's project under the assumption that
this project and the agent fall under the same license.

Part of https://gitlab.com/gitlab-org/gitlab/-/issues/327848
parent a2091532
......@@ -31,6 +31,7 @@ module Clusters
.joins(agent: :project)
.preload(agent: :project)
.where(cluster_agents: { projects: { namespace_id: ancestor_ids } })
.with_available_ci_access_fields(project)
.to_a
end
......@@ -53,6 +54,7 @@ module Clusters
.joins(cte_join_sources)
.joins(agent: :project)
.where('projects.namespace_id IN (SELECT id FROM ordered_ancestors)')
.with_available_ci_access_fields(project)
.order(Arel.sql('agent_id, array_position(ARRAY(SELECT id FROM ordered_ancestors)::bigint[], agent_group_authorizations.group_id)'))
.select('DISTINCT ON (agent_id) agent_group_authorizations.*')
.preload(agent: :project)
......
......@@ -3,6 +3,8 @@
module Clusters
module Agents
class GroupAuthorization < ApplicationRecord
include ::Clusters::Agents::AuthorizationConfigScopes
self.table_name = 'agent_group_authorizations'
belongs_to :agent, class_name: 'Clusters::Agent', optional: false
......
......@@ -3,6 +3,8 @@
module Clusters
module Agents
class ProjectAuthorization < ApplicationRecord
include ::Clusters::Agents::AuthorizationConfigScopes
self.table_name = 'agent_project_authorizations'
belongs_to :agent, class_name: 'Clusters::Agent', optional: false
......
# frozen_string_literal: true
module Clusters
module Agents
module AuthorizationConfigScopes
extend ActiveSupport::Concern
included do
scope :with_available_ci_access_fields, ->(project) {
where("config->'access_as' IS NULL")
.or(where("config->'access_as' = '{}'"))
.or(where("config->'access_as' ?| array[:fields]", fields: available_ci_access_fields(project)))
}
end
class_methods do
def available_ci_access_fields(_project)
%w(agent)
end
end
end
end
end
Clusters::Agents::AuthorizationConfigScopes.prepend_mod
# frozen_string_literal: true
module EE
module Clusters
module Agents
module AuthorizationConfigScopes
extend ActiveSupport::Concern
prepended do
class_methods do
alias_method :base_available_ci_access_fields, :available_ci_access_fields
def available_ci_access_fields(project)
base_available_ci_access_fields(project).tap do |fields|
if project.licensed_feature_available?(:cluster_agents_ci_impersonation)
fields << "ci_job"
fields << "ci_user"
fields << "impersonate"
end
end
end
end
end
end
end
end
end
......@@ -68,6 +68,7 @@ class License < ApplicationRecord
ci_cd_projects
ci_secrets_management
cluster_agents_gitops
cluster_agents_ci_impersonation
cluster_deployments
code_owner_approval_required
commit_committer_check
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Clusters::AgentAuthorizationsFinder do
describe '#execute' do
let_it_be(:top_level_group) { create(:group) }
let_it_be(:agent_configuration_project) { create(:project, namespace: top_level_group) }
let_it_be(:bottom_level_group) { create(:group, parent: top_level_group) }
let_it_be(:requesting_project, reload: true) { create(:project, namespace: bottom_level_group) }
let_it_be(:production_agent) { create(:cluster_agent, project: agent_configuration_project) }
subject { described_class.new(requesting_project).execute }
shared_examples_for 'licensed access_as' do
context 'impersonate' do
let(:config) { { access_as: { impersonate: {} } } }
it { is_expected.to be_empty }
context 'when available' do
before do
stub_licensed_features(cluster_agents_ci_impersonation: true)
end
it { is_expected.to match_array [authorization] }
end
end
context 'ci_user' do
let(:config) { { access_as: { ci_user: {} } } }
it { is_expected.to be_empty }
context 'when available' do
before do
stub_licensed_features(cluster_agents_ci_impersonation: true)
end
it { is_expected.to match_array [authorization] }
end
end
context 'ci_job' do
let(:config) { { access_as: { ci_job: {} } } }
it { is_expected.to be_empty }
context 'when available' do
before do
stub_licensed_features(cluster_agents_ci_impersonation: true)
end
it { is_expected.to match_array [authorization] }
end
end
end
describe 'project authorizations' do
it_behaves_like 'licensed access_as' do
let!(:authorization) do
create(
:agent_project_authorization,
agent: production_agent,
project: requesting_project,
config: config
)
end
end
end
describe 'group authorizations' do
it_behaves_like 'licensed access_as' do
let!(:authorization) do
create(
:agent_group_authorization,
agent: production_agent,
group: top_level_group,
config: config
)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe EE::Clusters::Agents::AuthorizationConfigScopes do
describe '.with_available_ci_access_fields' do
let_it_be(:project) { create(:project) }
let_it_be(:agent_authorization_0) { create(:agent_project_authorization, project: project) }
let_it_be(:agent_authorization_1) { create(:agent_project_authorization, project: project, config: { access_as: {} }) }
let_it_be(:agent_authorization_2) { create(:agent_project_authorization, project: project, config: { access_as: { agent: {} } }) }
let_it_be(:impersonate_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { impersonate: {} } }) }
let_it_be(:ci_user_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { ci_user: {} } }) }
let_it_be(:ci_job_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { ci_job: {} } }) }
subject { Clusters::Agents::ProjectAuthorization.with_available_ci_access_fields(project) }
it { is_expected.not_to include(ci_job_authorization)}
it { is_expected.not_to include(ci_user_authorization)}
it { is_expected.not_to include(impersonate_authorization)}
context 'with :cluster_agents_ci_impersonation' do
before do
stub_licensed_features(cluster_agents_ci_impersonation: true)
end
it { is_expected.to include(ci_job_authorization, ci_user_authorization, impersonate_authorization) }
end
end
end
......@@ -17,6 +17,34 @@ RSpec.describe Clusters::AgentAuthorizationsFinder do
subject { described_class.new(requesting_project).execute }
shared_examples_for 'access_as' do
let(:config) { { access_as: { access_as => {} } } }
context 'agent' do
let(:access_as) { :agent }
it { is_expected.to match_array [authorization] }
end
context 'impersonate' do
let(:access_as) { :impersonate }
it { is_expected.to be_empty }
end
context 'ci_user' do
let(:access_as) { :ci_user }
it { is_expected.to be_empty }
end
context 'ci_job' do
let(:access_as) { :ci_job }
it { is_expected.to be_empty }
end
end
describe 'project authorizations' do
context 'agent configuration project does not share a root namespace with the given project' do
let(:unrelated_agent) { create(:cluster_agent) }
......@@ -29,7 +57,7 @@ RSpec.describe Clusters::AgentAuthorizationsFinder do
end
context 'with project authorizations present' do
let!(:authorization) {create(:agent_project_authorization, agent: production_agent, project: requesting_project) }
let!(:authorization) { create(:agent_project_authorization, agent: production_agent, project: requesting_project) }
it { is_expected.to match_array [authorization] }
end
......@@ -41,6 +69,10 @@ RSpec.describe Clusters::AgentAuthorizationsFinder do
it { is_expected.to match_array [project_authorization] }
end
it_behaves_like 'access_as' do
let!(:authorization) { create(:agent_project_authorization, agent: production_agent, project: requesting_project, config: config) }
end
end
describe 'implicit authorizations' do
......@@ -83,6 +115,10 @@ RSpec.describe Clusters::AgentAuthorizationsFinder do
expect(subject).to contain_exactly(bottom_level_auth)
end
end
it_behaves_like 'access_as' do
let!(:authorization) { create(:agent_group_authorization, agent: production_agent, group: top_level_group, config: config) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Clusters::Agents::AuthorizationConfigScopes do
describe '.with_available_ci_access_fields' do
let(:project) { create(:project) }
let!(:agent_authorization_0) { create(:agent_project_authorization, project: project) }
let!(:agent_authorization_1) { create(:agent_project_authorization, project: project, config: { access_as: {} }) }
let!(:agent_authorization_2) { create(:agent_project_authorization, project: project, config: { access_as: { agent: {} } }) }
let!(:impersonate_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { impersonate: {} } }) }
let!(:ci_user_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { ci_user: {} } }) }
let!(:ci_job_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { ci_job: {} } }) }
let!(:unexpected_authorization) { create(:agent_project_authorization, project: project, config: { access_as: { unexpected: {} } }) }
subject { Clusters::Agents::ProjectAuthorization.with_available_ci_access_fields(project) }
it { is_expected.to contain_exactly(agent_authorization_0, agent_authorization_1, agent_authorization_2) }
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment