diff --git a/app/finders/clusters/agents/authorizations/ci_access/finder.rb b/app/finders/clusters/agents/authorizations/ci_access/finder.rb index 0209cb357e5aaccd2e10dd727624a22ff923653a..65ae3dc45ea16171b301982b0c402484bf845a1a 100644 --- a/app/finders/clusters/agents/authorizations/ci_access/finder.rb +++ b/app/finders/clusters/agents/authorizations/ci_access/finder.rb @@ -38,9 +38,12 @@ def project_authorizations .where(project_id: project.id) .joins(agent: :project) .preload(agent: :project) - .where(cluster_agents: { projects: { namespace_id: namespace_ids } }) .with_available_ci_access_fields(project) + unless organization_agents_enabled? + query = query.where(cluster_agents: { projects: { namespace_id: namespace_ids } }) + end + query = query.where(agent_id: agent.id) if agent query.to_a end @@ -64,7 +67,6 @@ def group_authorizations .joins(cte_join_sources) .joins(agent: :project) .with_available_ci_access_fields(project) - .where(projects: { namespace_id: all_namespace_ids }) .order( Arel.sql( 'agent_id, array_position(ARRAY(SELECT id FROM ordered_ancestors)::bigint[], ' \ @@ -74,6 +76,7 @@ def group_authorizations .select('DISTINCT ON (agent_id) agent_group_authorizations.*') .preload(agent: :project) + query = query.where(projects: { namespace_id: all_namespace_ids }) unless organization_agents_enabled? query = query.where(agent_id: agent.id) if agent query.to_a end diff --git a/app/services/clusters/agents/authorizations/ci_access/refresh_service.rb b/app/services/clusters/agents/authorizations/ci_access/refresh_service.rb index 68d603edb6418210db53afe8d7aad0b2ff09c246..c9622b3c042c69365e209888ff608b383711446b 100644 --- a/app/services/clusters/agents/authorizations/ci_access/refresh_service.rb +++ b/app/services/clusters/agents/authorizations/ci_access/refresh_service.rb @@ -10,7 +10,7 @@ class RefreshService AUTHORIZED_ENTITY_LIMIT = 500 delegate :project, to: :agent, private: true - delegate :root_ancestor, to: :project, private: true + delegate :root_ancestor, :organization, to: :project, private: true def initialize(agent, config:) @agent = agent @@ -59,10 +59,8 @@ def refresh_organization! return unless organization_agents_enabled? if organization_configuration - organization_id = agent.project.organization_id - agent.ci_access_organization_authorizations.upsert_all( - [{ agent_id: agent.id, organization_id: organization_id, config: organization_configuration }], + [{ agent_id: agent.id, organization_id: organization.id, config: organization_configuration }], unique_by: [:agent_id] ) else @@ -107,11 +105,17 @@ def extract_config_entries(entity:) end def allowed_projects - root_ancestor.all_projects + if organization_agents_enabled? + organization.projects + else + root_ancestor.all_projects + end end def allowed_groups - if group_root_ancestor? + if organization_agents_enabled? + organization.groups + elsif group_root_ancestor? root_ancestor.self_and_descendants else ::Group.none diff --git a/app/views/admin/application_settings/_cluster_agents.haml b/app/views/admin/application_settings/_cluster_agents.haml index f639b954715d8aeafad237bc2e936082ed09d154..5990aaf813c61a6b8057efb944ea11f876776138 100644 --- a/app/views/admin/application_settings/_cluster_agents.haml +++ b/app/views/admin/application_settings/_cluster_agents.haml @@ -11,7 +11,7 @@ = render_if_exists 'admin/application_settings/cluster_agents_receptive_enabled', form: f .form-group - - help_text = s_('ClusterAgents|Allow configuring agents to be authorized for the entire instance.') + - help_text = s_('ClusterAgents|Allow configuring agents to be authorized for the entire instance and across top level groups.') - help_link = link_to _('Learn more.'), help_page_path('user/clusters/agent/ci_cd_workflow.md', anchor: 'authorize-all-projects-in-your-gitlab-instance-to-access-the-agent'), target: '_blank', rel: 'noopener noreferrer' = f.gitlab_ui_checkbox_component :organization_cluster_agent_authorization_enabled, s_('ClusterAgents|Enable instance level authorization'), help_text: '%{help_text} %{help_link}'.html_safe % { help_text: help_text, help_link: help_link } diff --git a/doc/user/clusters/agent/ci_cd_workflow.md b/doc/user/clusters/agent/ci_cd_workflow.md index 5753445c8ed3779c9299621a29f6159550165ba7..a319f3813fdef87857516772b8e84e6558dbd1d6 100644 --- a/doc/user/clusters/agent/ci_cd_workflow.md +++ b/doc/user/clusters/agent/ci_cd_workflow.md @@ -70,6 +70,7 @@ Authorization configuration can take one or two minutes to propagate. - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/346566) to remove hierarchy restrictions in GitLab 15.6. - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/356831) to allow authorizing projects in a user namespace in GitLab 15.7. +- [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/377932) to allow the authorization of groups that belong to different top-level groups in GitLab 18.1. {{< /history >}} @@ -85,7 +86,8 @@ To authorize the GitLab project where you keep Kubernetes manifests to access th - id: path/to/project ``` - - Authorized projects must have the same top-level group or user namespace as the agent's configuration project. + - Authorized projects must have the same top-level group or user namespace as the agent's configuration project, unless the + [instance level authorization](#authorize-all-projects-in-your-gitlab-instance-to-access-the-agent) application setting is enabled. - You can install additional agents into the same cluster to accommodate additional hierarchies. - You can authorize up to 500 projects. @@ -100,6 +102,7 @@ After making these changes: {{< history >}} - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/346566) to remove hierarchy restrictions in GitLab 15.6. +- [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/377932) to allow the authorization of groups that belong to different top-level groups in GitLab 18.1. {{< /history >}} @@ -115,7 +118,8 @@ To authorize all of the GitLab projects in a group or subgroup to access the age - id: path/to/group/subgroup ``` - - Authorized groups must have the same top-level group as the agent's configuration project. + - Authorized groups must have the same top-level group as the agent's configuration project, unless the + [instance level authorization](#authorize-all-projects-in-your-gitlab-instance-to-access-the-agent) application setting is enabled. - You can install additional agents into the same cluster to accommodate additional hierarchies. - All of the subgroups of an authorized group also have access to the same agent (without being specified individually). - You can authorize up to 500 groups. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7b8e026b0f1ab0bab58c263742e6308d72d19f23..03de239adaee7d30a75533de4b74f280acfc4a77 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -13684,7 +13684,7 @@ msgstr "" msgid "ClusterAgents|Allow configuring agents in receptive mode." msgstr "" -msgid "ClusterAgents|Allow configuring agents to be authorized for the entire instance." +msgid "ClusterAgents|Allow configuring agents to be authorized for the entire instance and across top level groups." msgstr "" msgid "ClusterAgents|An error occurred while loading your agent" diff --git a/spec/finders/clusters/agents/authorizations/ci_access/finder_spec.rb b/spec/finders/clusters/agents/authorizations/ci_access/finder_spec.rb index 9166439837d3af8562b61f6ebd6f452112bc39d0..99c48c7b8eaebe37668c6de0b831420c5f37b9c4 100644 --- a/spec/finders/clusters/agents/authorizations/ci_access/finder_spec.rb +++ b/spec/finders/clusters/agents/authorizations/ci_access/finder_spec.rb @@ -54,13 +54,18 @@ describe 'project authorizations' do context 'when initialized without an agent' do context 'agent configuration project does not share a root namespace with the given project' do - let(:unrelated_agent) { create(:cluster_agent) } - - before do - create(:agent_ci_access_project_authorization, agent: unrelated_agent, project: requesting_project) - end + let_it_be(:unrelated_agent) { create(:cluster_agent) } + let_it_be(:project_authorization) { create(:agent_ci_access_project_authorization, agent: unrelated_agent, project: requesting_project) } it { is_expected.to be_empty } + + context 'when the organization authorization application setting is enabled' do + before do + stub_application_setting(organization_cluster_agent_authorization_enabled: true) + end + + it { is_expected.to match_array([project_authorization]) } + end end context 'agent configuration project shares a root namespace, but does not belong to an ancestor of the given project' do @@ -135,13 +140,18 @@ describe 'authorized groups' do context 'when initialized without an agent' do context 'agent configuration project is outside the requesting project hierarchy' do - let(:unrelated_agent) { create(:cluster_agent) } - - before do - create(:agent_ci_access_group_authorization, agent: unrelated_agent, group: top_level_group) - end + let_it_be(:unrelated_agent) { create(:cluster_agent) } + let_it_be(:project_authorization) { create(:agent_ci_access_group_authorization, agent: unrelated_agent, group: top_level_group) } it { is_expected.to be_empty } + + context 'when the organization authorization application setting is enabled' do + before do + stub_application_setting(organization_cluster_agent_authorization_enabled: true) + end + + it { is_expected.to match_array([project_authorization]) } + end end context 'multiple agents are authorized for the same group' do diff --git a/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb b/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb index 9fcb13fcf3d2e974cffdbb1f5b5a01823a9c6630..0e0fb2b6b4237cd60a6e0c7e710c54ef5d683325 100644 --- a/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb +++ b/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb @@ -14,6 +14,9 @@ let_it_be(:modified_project) { create(:project, namespace: root_ancestor) } let_it_be(:added_project) { create(:project, path: 'project-path-with-UPPERCASE', namespace: root_ancestor) } + let_it_be(:user_project_outside_of_hierarchy) { create(:project) } + let_it_be(:group_project_outside_of_hierarchy) { create(:project, :in_group) } + let(:project) { create(:project, namespace: root_ancestor) } let(:agent) { create(:cluster_agent, project: project) } @@ -61,7 +64,7 @@ end context 'config contains groups outside of the configuration project hierarchy' do - let(:project) { create(:project, namespace: create(:group)) } + let(:project) { group_project_outside_of_hierarchy } it 'removes all authorizations' do expect(subject).to be_truthy @@ -70,7 +73,7 @@ end context 'configuration project does not belong to a group' do - let(:project) { create(:project) } + let(:project) { user_project_outside_of_hierarchy } it 'removes all authorizations' do expect(subject).to be_truthy @@ -92,6 +95,19 @@ 'protected_branches_only' => 'true' }) end + context 'when the organization authorization application setting is enabled' do + let(:project) { group_project_outside_of_hierarchy } + + before do + stub_application_setting(organization_cluster_agent_authorization_enabled: true) + end + + it 'allows authorizing groups outside of the configuration project hierarchy' do + expect(subject).to be_truthy + expect(agent.ci_access_authorized_groups).to contain_exactly(added_group, modified_group) + end + end + context 'config contains too many groups' do before do stub_const("#{described_class}::AUTHORIZED_ENTITY_LIMIT", 1) @@ -131,6 +147,19 @@ end end + context 'when the organization authorization application setting is enabled' do + let(:project) { group_project_outside_of_hierarchy } + + before do + stub_application_setting(organization_cluster_agent_authorization_enabled: true) + end + + it 'allows authorizing groups outside of the configuration project hierarchy' do + expect(subject).to be_truthy + expect(agent.ci_access_authorized_groups).to contain_exactly(added_group, modified_group) + end + end + context 'project does not belong to a group, and is authorizing itself' do let(:root_ancestor) { create(:namespace) } let(:added_project) { project }