diff --git a/app/graphql/mutations/ci/job_token_scope/add_group_or_project.rb b/app/graphql/mutations/ci/job_token_scope/add_group_or_project.rb index b52ac2f301ec4c39656a5123f3a7e31f95d9f818..b7d28c8b12ba5f1d95ab2ca168e2dc63984ea629 100644 --- a/app/graphql/mutations/ci/job_token_scope/add_group_or_project.rb +++ b/app/graphql/mutations/ci/job_token_scope/add_group_or_project.rb @@ -18,6 +18,13 @@ class AddGroupOrProject < BaseMutation required: true, description: 'Group or project to be added to the CI job token scope.' + argument :default_permissions, GraphQL::Types::Boolean, + required: false, + default_value: true, + experiment: { milestone: '17.8' }, + description: 'Indicates whether default permissions are enabled (true) or fine-grained permissions are ' \ + 'enabled (false).' + argument :job_token_policies, [Types::Ci::JobTokenScope::PoliciesEnum], required: false, default_value: [], @@ -36,16 +43,16 @@ class AddGroupOrProject < BaseMutation null: true, description: "CI job token's access scope." - def resolve(args) - project = authorized_find!(args[:project_path]) - - target = find_target_path(args[:target_path]) - - args.delete(:job_token_policies) unless Feature.enabled?(:add_policies_to_ci_job_token, project) + def resolve(project_path:, target_path:, default_permissions:, job_token_policies:) + project = authorized_find!(project_path) + target = find_target_path(target_path) + policies_enabled = Feature.enabled?(:add_policies_to_ci_job_token, project) + # Use default permissions if policies feature isn't enabled. + default = policies_enabled ? default_permissions : true result = ::Ci::JobTokenScope::AddGroupOrProjectService .new(project, current_user) - .execute(target, policies: args[:job_token_policies]) + .execute(target, default_permissions: default, policies: job_token_policies) if result.success? { diff --git a/app/graphql/mutations/ci/job_token_scope/update_job_token_policies.rb b/app/graphql/mutations/ci/job_token_scope/update_job_token_policies.rb index ab53c09cde788bbacefe9ff289327387c3d00906..d5f9bf37b3f9ecdf865b8e0097ade9dd046d9afc 100644 --- a/app/graphql/mutations/ci/job_token_scope/update_job_token_policies.rb +++ b/app/graphql/mutations/ci/job_token_scope/update_job_token_policies.rb @@ -18,6 +18,11 @@ class UpdateJobTokenPolicies < BaseMutation required: true, description: 'Group or project that the CI job token targets.' + argument :default_permissions, GraphQL::Types::Boolean, + required: true, + description: 'Indicates whether default permissions are enabled (true) or fine-grained permissions are ' \ + 'enabled (false).' + argument :job_token_policies, [Types::Ci::JobTokenScope::PoliciesEnum], required: true, description: 'List of policies added to the CI job token scope.' @@ -28,7 +33,7 @@ class UpdateJobTokenPolicies < BaseMutation experiment: { milestone: '17.6' }, description: "Allowlist entry for the CI job token's access scope." - def resolve(project_path:, target_path:, job_token_policies:) + def resolve(project_path:, target_path:, default_permissions:, job_token_policies:) project = authorized_find!(project_path) target = find_target_using_path(target_path) @@ -38,7 +43,7 @@ def resolve(project_path:, target_path:, job_token_policies:) result = ::Ci::JobTokenScope::UpdatePoliciesService .new(project, current_user) - .execute(target, job_token_policies) + .execute(target, default_permissions, job_token_policies) if result.success? { diff --git a/app/graphql/types/ci/job_token_scope/allowlist_entry_type.rb b/app/graphql/types/ci/job_token_scope/allowlist_entry_type.rb index 3e976ef69193516c301a11f90ac8b5e8b1131f7d..c05927a79738c8c853825f0c9f944ae4e1603e64 100644 --- a/app/graphql/types/ci/job_token_scope/allowlist_entry_type.rb +++ b/app/graphql/types/ci/job_token_scope/allowlist_entry_type.rb @@ -25,6 +25,11 @@ class AllowlistEntryType < BaseObject null: true, description: 'Direction of access. Defaults to INBOUND.' + field :default_permissions, + GraphQL::Types::Boolean, + description: 'Indicates whether default permissions are enabled (true) or fine-grained permissions are ' \ + 'enabled (false).' + field :job_token_policies, [Types::Ci::JobTokenScope::PoliciesEnum], null: true, @@ -63,6 +68,10 @@ def direction end end + def default_permissions + Feature.enabled?(:add_policies_to_ci_job_token, object.source_project) ? object.default_permissions : true + end + def job_token_policies return unless Feature.enabled?(:add_policies_to_ci_job_token, object.source_project) diff --git a/app/models/ci/job_token/allowlist.rb b/app/models/ci/job_token/allowlist.rb index c5248823d567f0aa54d661e116c0d9926c3f3160..2a4ec179b8c8481000e36c91c05cbc773f4bfc72 100644 --- a/app/models/ci/job_token/allowlist.rb +++ b/app/models/ci/job_token/allowlist.rb @@ -30,25 +30,29 @@ def groups ::Group.id_in(group_links.pluck(:target_group_id)) end - def add!(target_project, user:, policies: []) + def add!(target_project, user:, default_permissions: true, policies: []) job_token_policies = add_policies_to_ci_job_token_enabled ? policies : [] + default_permissions = add_policies_to_ci_job_token_enabled ? default_permissions : true Ci::JobToken::ProjectScopeLink.create!( source_project: @source_project, direction: @direction, target_project: target_project, added_by: user, + default_permissions: default_permissions, job_token_policies: job_token_policies ) end - def add_group!(target_group, user:, policies: []) + def add_group!(target_group, user:, default_permissions: true, policies: []) job_token_policies = add_policies_to_ci_job_token_enabled ? policies : [] + default_permissions = add_policies_to_ci_job_token_enabled ? default_permissions : true Ci::JobToken::GroupScopeLink.create!( source_project: @source_project, target_group: target_group, added_by: user, + default_permissions: default_permissions, job_token_policies: job_token_policies ) end diff --git a/app/services/ci/job_token_scope/add_group_or_project_service.rb b/app/services/ci/job_token_scope/add_group_or_project_service.rb index 6b816cbc4dc80f66c68775e306881398fa6b990e..9d0fc40159a610eb63544347dcd590f0794c3ee9 100644 --- a/app/services/ci/job_token_scope/add_group_or_project_service.rb +++ b/app/services/ci/job_token_scope/add_group_or_project_service.rb @@ -5,13 +5,15 @@ module JobTokenScope class AddGroupOrProjectService < ::BaseService include EditScopeValidations - def execute(target, policies: []) + def execute(target, default_permissions: true, policies: []) validate_target_exists!(target) if target.is_a?(::Group) - ::Ci::JobTokenScope::AddGroupService.new(project, current_user).execute(target, policies: policies) + ::Ci::JobTokenScope::AddGroupService.new(project, current_user).execute(target, + default_permissions: default_permissions, policies: policies) else - ::Ci::JobTokenScope::AddProjectService.new(project, current_user).execute(target, policies: policies) + ::Ci::JobTokenScope::AddProjectService.new(project, current_user).execute(target, + default_permissions: default_permissions, policies: policies) end rescue EditScopeValidations::NotFoundError => e diff --git a/app/services/ci/job_token_scope/add_group_service.rb b/app/services/ci/job_token_scope/add_group_service.rb index 2ab8493212e3da3892b5276c39d8c1609c3afd69..05a89a523eaac686d41201480be9d66963d259e1 100644 --- a/app/services/ci/job_token_scope/add_group_service.rb +++ b/app/services/ci/job_token_scope/add_group_service.rb @@ -5,11 +5,11 @@ module JobTokenScope class AddGroupService < ::BaseService include EditScopeValidations - def execute(target_group, policies: []) + def execute(target_group, default_permissions: true, policies: []) validate_source_project_and_target_group_access!(project, target_group, current_user) link = allowlist - .add_group!(target_group, policies: policies, user: current_user) + .add_group!(target_group, default_permissions: default_permissions, policies: policies, user: current_user) ServiceResponse.success(payload: { group_link: link }) diff --git a/app/services/ci/job_token_scope/add_project_service.rb b/app/services/ci/job_token_scope/add_project_service.rb index 0c9f37f732224b7400d1a58a0ae1c720b1880896..b1dfa1b5ccd4040421910d61f3490f7db07b7f74 100644 --- a/app/services/ci/job_token_scope/add_project_service.rb +++ b/app/services/ci/job_token_scope/add_project_service.rb @@ -5,11 +5,11 @@ module JobTokenScope class AddProjectService < ::BaseService include EditScopeValidations - def execute(target_project, policies: [], direction: :inbound) + def execute(target_project, default_permissions: true, policies: [], direction: :inbound) validate_source_project_and_target_project_access!(project, target_project, current_user) link = allowlist(direction) - .add!(target_project, policies: policies, user: current_user) + .add!(target_project, default_permissions: default_permissions, policies: policies, user: current_user) ServiceResponse.success(payload: { project_link: link }) diff --git a/app/services/ci/job_token_scope/update_policies_service.rb b/app/services/ci/job_token_scope/update_policies_service.rb index 8cad91deb0de03fb2e333d90e66a04bb4abcfeb6..2f9d7b4787b138b0c4e37bee6ee4c98524527d40 100644 --- a/app/services/ci/job_token_scope/update_policies_service.rb +++ b/app/services/ci/job_token_scope/update_policies_service.rb @@ -5,7 +5,7 @@ module JobTokenScope class UpdatePoliciesService < ::BaseService include EditScopeValidations - def execute(target, policies) + def execute(target, default_permissions, policies) return unless Feature.enabled?(:add_policies_to_ci_job_token, project) validate_target_exists!(target) @@ -15,7 +15,7 @@ def execute(target, policies) return error_link_not_found unless link - if link.update(job_token_policies: policies) + if link.update(default_permissions: default_permissions, job_token_policies: policies) ServiceResponse.success(payload: link) else error_updating(link) diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 36492cc22e38249df47e0bb3d23fc41f8796170b..a03a2862e42c990107953210d54734f0a0a93927 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -3134,6 +3134,7 @@ Input type: `CiJobTokenScopeAddGroupOrProjectInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationcijobtokenscopeaddgrouporprojectclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationcijobtokenscopeaddgrouporprojectdefaultpermissions"></a>`defaultPermissions` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.8. | | <a id="mutationcijobtokenscopeaddgrouporprojectjobtokenpolicies"></a>`jobTokenPolicies` **{warning-solid}** | [`[CiJobTokenScopePolicies!]`](#cijobtokenscopepolicies) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.5. | | <a id="mutationcijobtokenscopeaddgrouporprojectprojectpath"></a>`projectPath` | [`ID!`](#id) | Project that the CI job token scope belongs to. | | <a id="mutationcijobtokenscopeaddgrouporprojecttargetpath"></a>`targetPath` | [`ID!`](#id) | Group or project to be added to the CI job token scope. | @@ -3225,6 +3226,7 @@ Input type: `CiJobTokenScopeUpdatePoliciesInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationcijobtokenscopeupdatepoliciesclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationcijobtokenscopeupdatepoliciesdefaultpermissions"></a>`defaultPermissions` | [`Boolean!`](#boolean) | Indicates whether default permissions are enabled (true) or fine-grained permissions are enabled (false). | | <a id="mutationcijobtokenscopeupdatepoliciesjobtokenpolicies"></a>`jobTokenPolicies` | [`[CiJobTokenScopePolicies!]!`](#cijobtokenscopepolicies) | List of policies added to the CI job token scope. | | <a id="mutationcijobtokenscopeupdatepoliciesprojectpath"></a>`projectPath` | [`ID!`](#id) | Project that the CI job token scope belongs to. | | <a id="mutationcijobtokenscopeupdatepoliciestargetpath"></a>`targetPath` | [`ID!`](#id) | Group or project that the CI job token targets. | @@ -20496,6 +20498,7 @@ Represents an allowlist entry for the CI_JOB_TOKEN. | ---- | ---- | ----------- | | <a id="cijobtokenscopeallowlistentryaddedby"></a>`addedBy` | [`UserCore`](#usercore) | User that added the entry. | | <a id="cijobtokenscopeallowlistentrycreatedat"></a>`createdAt` | [`Time!`](#time) | When the entry was created. | +| <a id="cijobtokenscopeallowlistentrydefaultpermissions"></a>`defaultPermissions` | [`Boolean`](#boolean) | Indicates whether default permissions are enabled (true) or fine-grained permissions are enabled (false). | | <a id="cijobtokenscopeallowlistentrydirection"></a>`direction` | [`String`](#string) | Direction of access. Defaults to INBOUND. | | <a id="cijobtokenscopeallowlistentryjobtokenpolicies"></a>`jobTokenPolicies` **{warning-solid}** | [`[CiJobTokenScopePolicies!]`](#cijobtokenscopepolicies) | **Introduced** in GitLab 17.5. **Status**: Experiment. List of policies for the entry. | | <a id="cijobtokenscopeallowlistentrysourceproject"></a>`sourceProject` | [`Project!`](#project) | Project that owns the allowlist entry. | diff --git a/ee/app/services/ee/ci/job_token_scope/add_group_service.rb b/ee/app/services/ee/ci/job_token_scope/add_group_service.rb index 29d1df3df1044634da4b0965703156505001df84..b0d3bc68068a1bf7ec36d98d324d3202df5f57aa 100644 --- a/ee/app/services/ee/ci/job_token_scope/add_group_service.rb +++ b/ee/app/services/ee/ci/job_token_scope/add_group_service.rb @@ -7,21 +7,22 @@ module AddGroupService extend ::Gitlab::Utils::Override override :execute - def execute(target_group, policies: []) + def execute(target_group, default_permissions: true, policies: []) super.tap do |response| - audit(project, target_group, current_user, policies) if response.success? + audit(project, target_group, current_user, default_permissions, policies) if response.success? end end private - def audit(scope, target, author, policies) + def audit(scope, target, author, default_permissions, policies) audit_message = "Group #{target.full_path} was added to list of allowed groups for #{scope.full_path}" event_name = 'secure_ci_job_token_group_added' if ::Feature.enabled?(:add_policies_to_ci_job_token, scope) && policies.present? - audit_message += ", with job token permissions: #{policies.join(', ')}" + audit_message += ", with default permissions: #{default_permissions}, " \ + "job token policies: #{policies.join(', ')}" end audit_context = { diff --git a/ee/app/services/ee/ci/job_token_scope/add_project_service.rb b/ee/app/services/ee/ci/job_token_scope/add_project_service.rb index ae6920ba5da4faebd1a9542e8878241575a1fd77..2be230a96d051559328923e2c16ff1d89e642148 100644 --- a/ee/app/services/ee/ci/job_token_scope/add_project_service.rb +++ b/ee/app/services/ee/ci/job_token_scope/add_project_service.rb @@ -7,21 +7,25 @@ module AddProjectService extend ::Gitlab::Utils::Override override :execute - def execute(target_project, policies: [], direction: :inbound) + def execute(target_project, default_permissions: true, policies: [], direction: :inbound) super.tap do |response| - audit(project, target_project, current_user, policies) if direction == :inbound && response.success? + if direction == :inbound && response.success? + audit(project, target_project, current_user, default_permissions, + policies) + end end end private - def audit(scope, target, author, policies) + def audit(scope, target, author, default_permissions, policies) audit_message = "Project #{target.full_path} was added to inbound list of allowed projects for #{scope.full_path}" event_name = 'secure_ci_job_token_project_added' if ::Feature.enabled?(:add_policies_to_ci_job_token, scope) && policies.present? - audit_message += ", with job token permissions: #{policies.join(', ')}" + audit_message += ", with default permissions: #{default_permissions}, " \ + "job token policies: #{policies.join(', ')}" end audit_context = { diff --git a/ee/app/services/ee/ci/job_token_scope/remove_group_service.rb b/ee/app/services/ee/ci/job_token_scope/remove_group_service.rb index ca2faf5ebffd9c3b00344866ec4e0825b7f77a19..204239e698d48ccb0cdc70125a38f884f147d09c 100644 --- a/ee/app/services/ee/ci/job_token_scope/remove_group_service.rb +++ b/ee/app/services/ee/ci/job_token_scope/remove_group_service.rb @@ -21,7 +21,7 @@ def audit(scope, target, author, policies) event_name = 'secure_ci_job_token_group_removed' if ::Feature.enabled?(:add_policies_to_ci_job_token, scope) && policies.present? - audit_message += ", with job token permissions: #{policies.join(', ')}" + audit_message += ", with job token policies: #{policies.join(', ')}" end audit_context = { diff --git a/ee/app/services/ee/ci/job_token_scope/remove_project_service.rb b/ee/app/services/ee/ci/job_token_scope/remove_project_service.rb index 30ad7c14d1154095aad0315e73e0d462425a6a49..aa5aef5ae64050b7131330d161df85708d10ed14 100644 --- a/ee/app/services/ee/ci/job_token_scope/remove_project_service.rb +++ b/ee/app/services/ee/ci/job_token_scope/remove_project_service.rb @@ -23,7 +23,7 @@ def audit(scope, target, author, policies) event_name = 'secure_ci_job_token_project_removed' if ::Feature.enabled?(:add_policies_to_ci_job_token, scope) && policies.present? - audit_message += ", with job token permissions: #{policies.join(', ')}" + audit_message += ", with job token policies: #{policies.join(', ')}" end audit_context = { diff --git a/ee/app/services/ee/ci/job_token_scope/update_policies_service.rb b/ee/app/services/ee/ci/job_token_scope/update_policies_service.rb index 6f0544e59fb6c541e9041ad2474469d834e83fb3..e29d45f7cadd3bf195c67cc4db2c9fcf37abf1c5 100644 --- a/ee/app/services/ee/ci/job_token_scope/update_policies_service.rb +++ b/ee/app/services/ee/ci/job_token_scope/update_policies_service.rb @@ -7,17 +7,17 @@ module UpdatePoliciesService extend ::Gitlab::Utils::Override override :execute - def execute(target, policies) + def execute(target, default_permissions, policies) super.tap do |response| - audit(project, target, current_user, policies) if response.success? + audit(project, target, current_user, default_permissions, policies) if response.success? end end private - def audit(scope, target, author, policies) + def audit(scope, target, author, default_permissions, policies) audit_message = - "CI job token policies updated to: #{policies.join(', ')}" + "CI job token updated to default permissions: #{default_permissions}, policies: #{policies.join(', ')}" event_name = 'secure_ci_job_token_policies_updated' diff --git a/ee/spec/graphql/ee/mutations/ci/job_token_scope/add_group_or_project_spec.rb b/ee/spec/graphql/ee/mutations/ci/job_token_scope/add_group_or_project_spec.rb index 92b086f568d6f712e080de95ec8f5ba7218909ba..5cb7bc37d5b629b83627cf4f2fc834289b32c73a 100644 --- a/ee/spec/graphql/ee/mutations/ci/job_token_scope/add_group_or_project_spec.rb +++ b/ee/spec/graphql/ee/mutations/ci/job_token_scope/add_group_or_project_spec.rb @@ -33,8 +33,8 @@ { project_path: project.full_path, target_path: target.full_path, - job_token_policies: policies, - direction: :inbound + default_permissions: false, + job_token_policies: policies } end @@ -51,7 +51,7 @@ let(:expected_audit_message) do "Group #{target_group_path} was added to list of allowed groups for #{project_path}, " \ - "with job token permissions: read_containers, read_packages" + "with default permissions: false, job token policies: read_containers, read_packages" end let(:event_name) { 'secure_ci_job_token_group_added' } @@ -104,7 +104,7 @@ let(:expected_audit_message) do "Project #{target_project_path} was added to inbound list of allowed projects for #{project_path}, " \ - "with job token permissions: read_containers, read_packages" + "with default permissions: false, job token policies: read_containers, read_packages" end let(:event_name) { 'secure_ci_job_token_project_added' } diff --git a/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_group_spec.rb b/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_group_spec.rb index 57ecd73105c225118701ba4ac512bfa569da8ba1..d2a761a91c172dbdee2ecd49ae70e4a419e39f8f 100644 --- a/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_group_spec.rb +++ b/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_group_spec.rb @@ -51,7 +51,7 @@ context 'when user removes target group to the job token scope' do let(:expected_audit_message) do "Group #{target_group_path} was removed from list of allowed groups for #{project_path}, " \ - "with job token permissions: read_containers, read_packages" + "with job token policies: read_containers, read_packages" end let(:event_name) { 'secure_ci_job_token_group_removed' } diff --git a/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_project_spec.rb b/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_project_spec.rb index 20db1a312ed9a2fd5cab123a02f507a1cea653e0..e86b1047cb3c0967ea8c4210c0e53f880b11d317 100644 --- a/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_project_spec.rb +++ b/ee/spec/graphql/ee/mutations/ci/job_token_scope/remove_project_spec.rb @@ -59,7 +59,7 @@ let(:expected_audit_message) do "Project #{target_project_path} was removed from inbound list of allowed projects for #{project_path}, " \ - "with job token permissions: read_containers, read_packages" + "with job token policies: read_containers, read_packages" end let(:event_name) { 'secure_ci_job_token_project_removed' } diff --git a/ee/spec/services/ee/ci/job_token_scope/add_group_service_spec.rb b/ee/spec/services/ee/ci/job_token_scope/add_group_service_spec.rb index be3884a5a6fe3155962a4e0765a3baf5abe6a60b..b905c24cba46feecdb6948da97b487586987af33 100644 --- a/ee/spec/services/ee/ci/job_token_scope/add_group_service_spec.rb +++ b/ee/spec/services/ee/ci/job_token_scope/add_group_service_spec.rb @@ -10,12 +10,14 @@ let_it_be(:policies) { %w[read_containers read_packages] } - subject(:service_result) { described_class.new(project, current_user).execute(target_group, policies: policies) } + subject(:service_result) do + described_class.new(project, current_user).execute(target_group, default_permissions: false, policies: policies) + end describe '#execute' do let(:expected_audit_message) do "Group #{target_group.full_path} was added to list of allowed groups for #{project.full_path}, " \ - "with job token permissions: read_containers, read_packages" + "with default permissions: false, job token policies: read_containers, read_packages" end let(:audit_event) do diff --git a/ee/spec/services/ee/ci/job_token_scope/add_project_service_spec.rb b/ee/spec/services/ee/ci/job_token_scope/add_project_service_spec.rb index ce33ee00f39f703afa53890742b7aa0b669d46af..e01b676383f1c0af81fdccfbb0cd972a681172dc 100644 --- a/ee/spec/services/ee/ci/job_token_scope/add_project_service_spec.rb +++ b/ee/spec/services/ee/ci/job_token_scope/add_project_service_spec.rb @@ -13,14 +13,15 @@ let_it_be(:direction) { :inbound } subject(:service_result) do - described_class.new(project, current_user).execute(target_project, policies: policies, direction: direction) + described_class.new(project, current_user).execute(target_project, default_permissions: false, policies: policies, + direction: direction) end describe '#execute' do context 'when the direction is inbound' do let(:expected_audit_message) do "Project #{target_project.full_path} was added to inbound list of allowed projects for #{project.full_path}, " \ - "with job token permissions: read_containers, read_packages" + "with default permissions: false, job token policies: read_containers, read_packages" end let(:audit_event) do diff --git a/ee/spec/services/ee/ci/job_token_scope/remove_group_service_spec.rb b/ee/spec/services/ee/ci/job_token_scope/remove_group_service_spec.rb index dbe43c999f5f0e329688402078e49330a826aa7e..d330fc2c03f9d776f9cfd9a5ee537749eadb06f0 100644 --- a/ee/spec/services/ee/ci/job_token_scope/remove_group_service_spec.rb +++ b/ee/spec/services/ee/ci/job_token_scope/remove_group_service_spec.rb @@ -23,7 +23,7 @@ describe '#execute' do let(:expected_audit_message) do "Group #{target_group.full_path} was removed from list of allowed groups for #{project.full_path}, " \ - "with job token permissions: read_containers, read_packages" + "with job token policies: read_containers, read_packages" end let(:audit_event) do diff --git a/ee/spec/services/ee/ci/job_token_scope/remove_project_service_spec.rb b/ee/spec/services/ee/ci/job_token_scope/remove_project_service_spec.rb index 5cd8f249d62d068835866b817c07c86d2cea73b9..f0655e5620b53f756988cc1c35e1156901768d3c 100644 --- a/ee/spec/services/ee/ci/job_token_scope/remove_project_service_spec.rb +++ b/ee/spec/services/ee/ci/job_token_scope/remove_project_service_spec.rb @@ -29,7 +29,7 @@ let(:expected_audit_message) do "Project #{target_project.full_path} was removed from inbound list of allowed projects " \ - "for #{project.full_path}, with job token permissions: read_containers, read_packages" + "for #{project.full_path}, with job token policies: read_containers, read_packages" end let(:audit_event) do diff --git a/ee/spec/services/ee/ci/job_token_scope/update_policies_service_spec.rb b/ee/spec/services/ee/ci/job_token_scope/update_policies_service_spec.rb index ba07f2b18c85e7113ace76ef33a7c46d3b1d18ba..f21fd8aa6285321a784cf82c9b4aa5e2bb1d4ee2 100644 --- a/ee/spec/services/ee/ci/job_token_scope/update_policies_service_spec.rb +++ b/ee/spec/services/ee/ci/job_token_scope/update_policies_service_spec.rb @@ -8,10 +8,11 @@ let_it_be(:current_user) { create(:user, maintainer_of: project, developer_of: target_project) } + let(:default_permissions) { false } let(:policies) { %w[read_containers read_packages] } subject(:service_result) do - described_class.new(project, current_user).execute(target_project, policies) + described_class.new(project, current_user).execute(target_project, default_permissions, policies) end describe '#execute' do @@ -20,6 +21,7 @@ create(:ci_job_token_project_scope_link, source_project: project, target_project: target_project, + default_permissions: true, job_token_policies: %w[read_containers read_packages], direction: :inbound ) @@ -31,7 +33,7 @@ author: current_user, scope: project, target: target_project, - message: 'CI job token policies updated to: read_containers, read_packages' + message: 'CI job token updated to default permissions: false, policies: read_containers, read_packages' } end @@ -42,6 +44,7 @@ expect(link.source_project).to eq(project) expect(link.target_project).to eq(target_project) + expect(link.default_permissions).to be(default_permissions) expect(link.job_token_policies).to eq(policies) end diff --git a/spec/graphql/mutations/ci/job_token_scope/add_group_or_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/add_group_or_project_spec.rb index 86385bfcd0c0be48bfedfd9a3b810a5b67815ae0..d4c1ee16ee5ac1778c7d38fa76e6e4f41acf8adf 100644 --- a/spec/graphql/mutations/ci/job_token_scope/add_group_or_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/add_group_or_project_spec.rb @@ -39,7 +39,12 @@ let(:policies) { %w[read_containers read_packages] } let(:mutation_args) do - { project_path: project.full_path, target_path: target_project_path, job_token_policies: policies } + { + project_path: project.full_path, + target_path: target_project_path, + default_permissions: false, + job_token_policies: policies + } end it_behaves_like 'when user is not logged in' @@ -64,6 +69,7 @@ expect(project_link.source_project).to eq(project) expect(project_link.target_project).to eq(target_project) expect(project_link.added_by).to eq(current_user) + expect(project_link.default_permissions).to be(false) expect(project_link.job_token_policies).to eq(policies) end @@ -99,7 +105,12 @@ let(:policies) { %w[read_containers read_packages] } let(:mutation_args) do - { project_path: project.full_path, target_path: target_group_path, job_token_policies: policies } + { + project_path: project.full_path, + target_path: target_group_path, + default_permissions: false, + job_token_policies: policies + } end it_behaves_like 'when user is not logged in' @@ -124,6 +135,7 @@ expect(group_link.source_project).to eq(project) expect(group_link.target_group).to eq(target_group) expect(group_link.added_by).to eq(current_user) + expect(group_link.default_permissions).to be(false) expect(group_link.job_token_policies).to eq(policies) end diff --git a/spec/graphql/types/ci/job_token_scope/allowlist_entry_type_spec.rb b/spec/graphql/types/ci/job_token_scope/allowlist_entry_type_spec.rb index 790a46050b04d3267eb420306c2c1efcc8593b3e..58dd4cd18b2690b8fbd776ec00b45521c05d8a8f 100644 --- a/spec/graphql/types/ci/job_token_scope/allowlist_entry_type_spec.rb +++ b/spec/graphql/types/ci/job_token_scope/allowlist_entry_type_spec.rb @@ -10,6 +10,7 @@ source_project target direction + default_permissions job_token_policies added_by created_at diff --git a/spec/models/ci/job_token/allowlist_spec.rb b/spec/models/ci/job_token/allowlist_spec.rb index c08020a5380afba7f4afc2f76d7cdbd17665d91d..0c840818ae7f71fcbf2cc58804787758263b9a02 100644 --- a/spec/models/ci/job_token/allowlist_spec.rb +++ b/spec/models/ci/job_token/allowlist_spec.rb @@ -68,8 +68,23 @@ let_it_be(:added_project) { create(:project) } let_it_be(:user) { create(:user) } let_it_be(:policies) { %w[read_containers read_packages] } + let_it_be(:default_permissions) { false } - subject(:add_project) { allowlist.add!(added_project, policies: policies, user: user) } + subject(:add_project) do + allowlist.add!(added_project, default_permissions: default_permissions, policies: policies, user: user) + end + + [true, false].each do |d| + context "with default permissions #{d}" do + let_it_be(:default_permissions) { d } + + it "sets default permissions to #{d}" do + project_link = add_project + + expect(project_link.default_permissions).to eq(default_permissions) + end + end + end [:inbound, :outbound].each do |d| context "with #{d}" do @@ -97,6 +112,7 @@ expect(project_link.added_by_id).to eq(user.id) expect(project_link.source_project_id).to eq(source_project.id) expect(project_link.target_project_id).to eq(added_project.id) + expect(project_link.default_permissions).to be(true) expect(project_link.job_token_policies).to eq([]) end end @@ -108,8 +124,23 @@ let_it_be(:added_group) { create(:group) } let_it_be(:user) { create(:user) } let_it_be(:policies) { %w[read_containers read_packages] } + let_it_be(:default_permissions) { false } - subject(:add_group) { allowlist.add_group!(added_group, policies: policies, user: user) } + subject(:add_group) do + allowlist.add_group!(added_group, default_permissions: default_permissions, policies: policies, user: user) + end + + [true, false].each do |d| + context "with default permissions #{d}" do + let_it_be(:default_permissions) { d } + + it "sets default permissions to #{d}" do + group_link = add_group + + expect(group_link.default_permissions).to eq(default_permissions) + end + end + end it 'adds the group scope link' do group_link = add_group @@ -133,6 +164,7 @@ expect(group_link.added_by_id).to eq(user.id) expect(group_link.source_project_id).to eq(source_project.id) expect(group_link.target_group_id).to eq(added_group.id) + expect(group_link.default_permissions).to be(true) expect(group_link.job_token_policies).to eq([]) end end diff --git a/spec/requests/api/graphql/ci/job_token_scope/allowlist_query_spec.rb b/spec/requests/api/graphql/ci/job_token_scope/allowlist_query_spec.rb index 3778d47c06ac2fe91d7b667c9cf9f8c30d52e8b4..484bc7637f75fe14d99528f894078099e10d8e92 100644 --- a/spec/requests/api/graphql/ci/job_token_scope/allowlist_query_spec.rb +++ b/spec/requests/api/graphql/ci/job_token_scope/allowlist_query_spec.rb @@ -34,6 +34,7 @@ addedBy { username } + defaultPermissions jobTokenPolicies direction } @@ -52,6 +53,7 @@ addedBy { username } + defaultPermissions jobTokenPolicies direction } @@ -70,6 +72,7 @@ { 'addedBy' => { 'username' => current_user.username }, 'direction' => 'inbound', + 'defaultPermissions' => false, 'jobTokenPolicies' => ['READ_CONTAINERS'], 'sourceProject' => { 'fullPath' => project.full_path }, 'target' => { 'fullPath' => target_group_1.full_path } @@ -85,6 +88,7 @@ { 'addedBy' => { 'username' => current_user.username }, 'direction' => 'outbound', + 'defaultPermissions' => false, 'jobTokenPolicies' => ['READ_CONTAINERS'], 'sourceProject' => { 'fullPath' => project.full_path }, 'target' => { 'fullPath' => target_project_2.full_path } @@ -92,6 +96,7 @@ { 'addedBy' => { 'username' => current_user.username }, 'direction' => 'inbound', + 'defaultPermissions' => false, 'jobTokenPolicies' => ['READ_CONTAINERS'], 'sourceProject' => { 'fullPath' => project.full_path }, 'target' => { 'fullPath' => target_project_1.full_path } @@ -138,6 +143,7 @@ :ci_job_token_project_scope_link, source_project: project, target_project: target_project_1, + default_permissions: false, job_token_policies: %w[read_containers], added_by: current_user, direction: :inbound @@ -147,6 +153,7 @@ :ci_job_token_project_scope_link, source_project: project, target_project: target_project_2, + default_permissions: false, job_token_policies: %w[read_containers], added_by: current_user, direction: :outbound @@ -157,6 +164,7 @@ source_project: project, target_group: target_group_1, added_by: current_user, + default_permissions: false, job_token_policies: %w[read_containers] ) end @@ -180,6 +188,14 @@ expect(allowlist.dig('projectsAllowlist', 'nodes', 0, 'jobTokenPolicies')).to be_nil expect(allowlist.dig('projectsAllowlist', 'nodes', 1, 'jobTokenPolicies')).to be_nil end + + it 'returns default permissions as true', :aggregate_failures do + post_graphql(query, current_user: current_user) + + expect(allowlist.dig('groupsAllowlist', 'nodes', 0, 'defaultPermissions')).to be(true) + expect(allowlist.dig('projectsAllowlist', 'nodes', 0, 'defaultPermissions')).to be(true) + expect(allowlist.dig('projectsAllowlist', 'nodes', 1, 'defaultPermissions')).to be(true) + end end it 'avoids N+1 queries', :use_sql_query_cache do diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/update_job_token_policies_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/update_job_token_policies_spec.rb index acecd77b2443db464435a418bff5f205658a211b..101428e322e4361ab37f0e9280884a8c7a72b073 100644 --- a/spec/requests/api/graphql/mutations/ci/job_token_scope/update_job_token_policies_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/update_job_token_policies_spec.rb @@ -13,6 +13,7 @@ { project_path: project.full_path, target_path: target_path, + default_permissions: true, job_token_policies: policies } end @@ -33,6 +34,7 @@ fullPath } } + defaultPermissions jobTokenPolicies } QL @@ -74,6 +76,7 @@ :ci_job_token_project_scope_link, source_project: project, target_project: target_project, + default_permissions: false, job_token_policies: %w[read_containers], direction: :inbound ) @@ -88,6 +91,7 @@ 'fullPath')).to eq(project.full_path) expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'target', 'fullPath')).to eq(target_project.full_path) + expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'defaultPermissions')).to be(true) expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'jobTokenPolicies')).to eq(policies) end @@ -152,6 +156,7 @@ :ci_job_token_group_scope_link, source_project: project, target_group: target_group, + default_permissions: false, job_token_policies: %w[read_containers] ) end @@ -165,6 +170,7 @@ 'fullPath')).to eq(project.full_path) expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'target', 'fullPath')).to eq(target_group.full_path) + expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'defaultPermissions')).to be(true) expect(mutation_response.dig('ciJobTokenScopeAllowlistEntry', 'jobTokenPolicies')).to eq(policies) end diff --git a/spec/services/ci/job_token_scope/add_group_or_project_service_spec.rb b/spec/services/ci/job_token_scope/add_group_or_project_service_spec.rb index 8fbc181dfee6922aa9828e048792b8cf1c554baf..2e494f1828e91e69c4d65476244f8f517d67e012 100644 --- a/spec/services/ci/job_token_scope/add_group_or_project_service_spec.rb +++ b/spec/services/ci/job_token_scope/add_group_or_project_service_spec.rb @@ -11,7 +11,9 @@ let(:response_success) { ServiceResponse.success } - subject(:service_execute) { described_class.new(source_project, current_user).execute(target, policies: policies) } + subject(:service_execute) do + described_class.new(source_project, current_user).execute(target, default_permissions: false, policies: policies) + end describe '#execute' do context 'when group is a target to add' do @@ -26,7 +28,7 @@ it 'calls AddGroupService to add a target' do expect(add_group_service_double) - .to receive(:execute).with(target, policies: policies) + .to receive(:execute).with(target, default_permissions: false, policies: policies) .and_return(response_success) expect(service_execute).to eq(response_success) @@ -46,7 +48,7 @@ it 'calls AddProjectService to add a target' do expect(add_project_service_double) - .to receive(:execute).with(target, policies: policies) + .to receive(:execute).with(target, default_permissions: false, policies: policies) .and_return(response_success) expect(service_execute).to eq(response_success) diff --git a/spec/services/ci/job_token_scope/add_group_service_spec.rb b/spec/services/ci/job_token_scope/add_group_service_spec.rb index b6e087a25d23c40f8f46d4d9211ef05ac78c649c..0309b7e4a869365fc1b6eff10169acac67301487 100644 --- a/spec/services/ci/job_token_scope/add_group_service_spec.rb +++ b/spec/services/ci/job_token_scope/add_group_service_spec.rb @@ -21,6 +21,7 @@ expect(group_link.source_project).to eq(project) expect(group_link.target_group).to eq(target_group) expect(group_link.added_by).to eq(current_user) + expect(group_link.default_permissions).to eq(default_permissions) expect(group_link.job_token_policies).to eq(policies) end @@ -39,13 +40,16 @@ expect(group_link.source_project).to eq(project) expect(group_link.target_group).to eq(target_group) expect(group_link.added_by).to eq(current_user) + expect(group_link.default_permissions).to be(true) expect(group_link.job_token_policies).to eq([]) end end end describe '#execute' do - subject(:result) { service.execute(target_group, policies: policies) } + subject(:result) { service.execute(target_group, default_permissions: default_permissions, policies: policies) } + + let(:default_permissions) { false } it_behaves_like 'editable group job token scope' do context 'when user has permissions on source and target groups' do @@ -56,6 +60,12 @@ it_behaves_like 'adds group' + context 'when default_permissions is set to true' do + let(:default_permissions) { true } + + it_behaves_like 'adds group' + end + context 'when token scope is disabled' do before do project.ci_cd_settings.update!(job_token_scope_enabled: false) diff --git a/spec/services/ci/job_token_scope/add_project_service_spec.rb b/spec/services/ci/job_token_scope/add_project_service_spec.rb index 55815e2852a829b968d5b40234f479a51cb11b41..13e0cf12192333f6681462cf8567425ca48ab78a 100644 --- a/spec/services/ci/job_token_scope/add_project_service_spec.rb +++ b/spec/services/ci/job_token_scope/add_project_service_spec.rb @@ -20,6 +20,7 @@ expect(project_link.source_project).to eq(project) expect(project_link.target_project).to eq(target_project) expect(project_link.added_by).to eq(current_user) + expect(project_link.default_permissions).to eq(default_permissions) expect(project_link.job_token_policies).to eq(policies) end @@ -38,13 +39,16 @@ expect(project_link.source_project).to eq(project) expect(project_link.target_project).to eq(target_project) expect(project_link.added_by).to eq(current_user) + expect(project_link.default_permissions).to be(true) expect(project_link.job_token_policies).to eq([]) end end end describe '#execute' do - subject(:result) { service.execute(target_project, policies: policies) } + subject(:result) { service.execute(target_project, default_permissions: default_permissions, policies: policies) } + + let(:default_permissions) { false } it_behaves_like 'editable job token scope' do context 'when user has permissions on source and target projects' do @@ -57,6 +61,12 @@ it_behaves_like 'adds project' + context 'when default_permissions is set to true' do + let(:default_permissions) { true } + + it_behaves_like 'adds project' + end + context 'when token scope is disabled' do before do project.ci_cd_settings.update!(job_token_scope_enabled: false) diff --git a/spec/services/ci/job_token_scope/update_policies_service_spec.rb b/spec/services/ci/job_token_scope/update_policies_service_spec.rb index cbcbba5f1e004f3f34a7319cca50ad3c05f23802..bd333b17f5710e475e48c953f0d1d077a5bc7d30 100644 --- a/spec/services/ci/job_token_scope/update_policies_service_spec.rb +++ b/spec/services/ci/job_token_scope/update_policies_service_spec.rb @@ -9,7 +9,7 @@ let_it_be(:target_group) { create(:group, :private) } subject(:execute) do - described_class.new(project, current_user).execute(target, policies) + described_class.new(project, current_user).execute(target, default_permissions, policies) end describe '#execute' do @@ -78,11 +78,13 @@ :ci_job_token_project_scope_link, source_project: project, target_project: target_project, + default_permissions: true, job_token_policies: %w[read_containers], direction: :inbound ) end + let(:default_permissions) { false } let(:policies) { %w[read_containers read_packages] } it_behaves_like 'when user is not logged in' @@ -106,6 +108,7 @@ expect(project_link.source_project).to eq(project) expect(project_link.target_project).to eq(target_project) + expect(project_link.default_permissions).to be(false) expect(project_link.job_token_policies).to eq(%w[read_containers read_packages]) end @@ -132,10 +135,12 @@ :ci_job_token_group_scope_link, source_project: project, target_group: target_group, + default_permissions: true, job_token_policies: %w[read_containers] ) end + let(:default_permissions) { false } let(:policies) { %w[read_containers read_packages] } it_behaves_like 'when user is not logged in' @@ -159,6 +164,7 @@ expect(group_link.source_project).to eq(project) expect(group_link.target_group).to eq(target_group) + expect(group_link.default_permissions).to be(false) expect(group_link.job_token_policies).to eq(%w[read_containers read_packages]) end