From 28b9ad70c8dc926c146db797917113bc14e3e577 Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Fri, 7 Jun 2024 12:01:00 -0700 Subject: [PATCH 1/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Changelog: added --- doc/api/project_packages_protection_rules.md | 40 +++++++++++++ lib/api/project_packages_protection_rules.rb | 28 +++++++++ .../project_packages_protection_rules_spec.rb | 57 +++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index 0d7cb9d3af6eb2..48887369145e44 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -72,6 +72,46 @@ Example response: ] ``` +## Create a package protection rule + +Create a package protection rule for a project. + +```plaintext +POST /api/v4/projects/:id/packages/protection/rules +``` + +Supported attributes: + +| Attribute | Type | Required | Description | +|---------------------------------------|-----------------|----------|--------------------------------| +| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. | +| `package_name_pattern` | string | Yes | Package name protected by the protection rule. For example `@my-scope/my-package-*`. Wildcard character * allowed. | +| `package_type` | string | Yes | Package type protected by the protection rule. For example `npm`. | +| `minimum_access_level_for_push` | string | Yes | Minimum GitLab access level able to push a package. For example `developer`, `maintainer`, `owner`. | + +If successful, returns [`201`](rest/index.md#status-codes) and the created package protection rule. + +Can return the following status codes: + +- `201 Created`: The package protection rule has been created successfully. +- `401 Unauthorized`: The access token is invalid. +- `403 Forbidden`: The user does not have permission to create a package protection rule. +- `404 Not Found`: The project was not found. +- `422 Unprocessable Entity`: The package protection rule was not valid. + +Example request: + +```shell +curl --request POST \ + --header "PRIVATE-TOKEN: " \ + --url "https://gitlab.example.com/api/v4/projects/7/packages/protection/rules" \ + --data '{ + "package_name_pattern": "package-name-pattern-*", + "package_type": "npm", + "minimum_access_level_for_push": "maintainer" + }' +``` + ## Delete a package protection rule Deletes a package protection rule from a project. diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index bcabecf47f662a..26aea69708fd9c 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -32,6 +32,34 @@ class ProjectPackagesProtectionRules < ::API::Base present user_project.package_protection_rules, with: Entities::Projects::Packages::Protection::Rule end + desc 'Create a package protection rule for a project' do + success Entities::Projects::Packages::Protection::Rule + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' }, + { code: 422, message: 'Unprocessable Entity' } + ] + tags %w[projects] + end + params do + requires :package_name_pattern, type: String, + desc: 'Package name protected by the rule. For example @my-scope/my-package-*. Wildcard character * allowed.' + requires :package_type, type: String, values: Packages::Protection::Rule.package_types, + desc: 'Package type protected by the rule. For example npm.' + requires :minimum_access_level_for_push, + values: Packages::Protection::Rule.minimum_access_level_for_pushes, type: String, + desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' + end + post ":id/packages/protection/rules" do + response = ::Packages::Protection::CreateRuleService.new(project: user_project, current_user: current_user, + params: params).execute + + render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? + + present response[:package_protection_rule], with: Entities::Projects::Packages::Protection::Rule + end + desc 'Delete package protection rule' do success code: 204, message: '204 No Content' failure [ diff --git a/spec/requests/api/project_packages_protection_rules_spec.rb b/spec/requests/api/project_packages_protection_rules_spec.rb index 20079cfb867a74..f2dde37cd52344 100644 --- a/spec/requests/api/project_packages_protection_rules_spec.rb +++ b/spec/requests/api/project_packages_protection_rules_spec.rb @@ -77,6 +77,63 @@ end end + describe 'POST /projects/:id/packages/protection/rules' do + let(:url) { "/projects/#{project.id}/packages/protection/rules" } + let(:params) do + { package_name_pattern: '@my-new-scope/my-package-*', package_type: package_protection_rule.package_type, + minimum_access_level_for_push: package_protection_rule.minimum_access_level_for_push } + end + + subject(:post_package_rule) { post(api(url, api_user), params: params) } + + it_behaves_like 'rejecting project packages protection rules request', :reporter, :forbidden + it_behaves_like 'rejecting project packages protection rules request', :developer, :forbidden + it_behaves_like 'rejecting project packages protection rules request', :guest, :forbidden + it_behaves_like 'rejecting project packages protection rules request', nil, :not_found + + context 'for maintainer' do + let(:api_user) { maintainer } + + it 'creates a package protection rule' do + expect { post_package_rule }.to change { Packages::Protection::Rule.count }.by(1) + + expect(response).to have_gitlab_http_status(:created) + end + + it 'does not create a package protection rule with invalid package_type' do + params[:package_type] = "not in enum" + expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + + it 'does not create a package protection rule with invalid minimum_access_level_for_push' do + params[:minimum_access_level_for_push] = "not in enum" + expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + + context 'when the project id is invalid' do + let(:url) { "/projects/invalid/packages/protection/rules" } + + it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + end + + context 'when the project id does not exist' do + let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } + + it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + end + + context 'when packages_protected_packages is disabled' do + before do + stub_feature_flags(packages_protected_packages: false) + end + + it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + end + end + end + describe 'DELETE /projects/:id/packages/protection/rules/:package_protection_rule_id' do let(:url) { "/projects/#{project.id}/packages/protection/rules/#{package_protection_rule.id}" } -- GitLab From 33c8d8567186fa13bf9f8e0877503bca0430c1ae Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Tue, 11 Jun 2024 01:45:45 -0700 Subject: [PATCH 2/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. https://gitlab.com/gitlab-org/gitlab/-/issues/457512 Changelog: added --- doc/api/project_packages_protection_rules.md | 2 +- lib/api/project_packages_protection_rules.rb | 4 +- .../project_packages_protection_rules_spec.rb | 72 ++++++++++++------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index 48887369145e44..fbe1a970f81c45 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -85,7 +85,7 @@ Supported attributes: | Attribute | Type | Required | Description | |---------------------------------------|-----------------|----------|--------------------------------| | `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. | -| `package_name_pattern` | string | Yes | Package name protected by the protection rule. For example `@my-scope/my-package-*`. Wildcard character * allowed. | +| `package_name_pattern` | string | Yes | Package name protected by the protection rule. For example `@my-scope/my-package-*`. Wildcard character `*` allowed. | | `package_type` | string | Yes | Package type protected by the protection rule. For example `npm`. | | `minimum_access_level_for_push` | string | Yes | Minimum GitLab access level able to push a package. For example `developer`, `maintainer`, `owner`. | diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index 26aea69708fd9c..f5d8dcdad4cb64 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -51,9 +51,9 @@ class ProjectPackagesProtectionRules < ::API::Base values: Packages::Protection::Rule.minimum_access_level_for_pushes, type: String, desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' end - post ":id/packages/protection/rules" do + post ':id/packages/protection/rules' do response = ::Packages::Protection::CreateRuleService.new(project: user_project, current_user: current_user, - params: params).execute + params: declared(params, include_parent_namespaces: false)).execute render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? diff --git a/spec/requests/api/project_packages_protection_rules_spec.rb b/spec/requests/api/project_packages_protection_rules_spec.rb index f2dde37cd52344..abe628351554b3 100644 --- a/spec/requests/api/project_packages_protection_rules_spec.rb +++ b/spec/requests/api/project_packages_protection_rules_spec.rb @@ -25,15 +25,29 @@ end end + shared_examples 'rejecting project packages protection rules request using roles' do + using RSpec::Parameterized::TableSyntax + + where(:user_role, :status) do + :reporter | :forbidden + :developer | :forbidden + :guest | :forbidden + nil | :not_found + end + + with_them do + it_behaves_like 'rejecting project packages protection rules request', params[:user_role], params[:status] + end + end + describe 'GET /projects/:id/packages/protection/rules' do let(:url) { "/projects/#{project.id}/packages/protection/rules" } subject(:get_package_rules) { get(api(url, api_user)) } - it_behaves_like 'rejecting project packages protection rules request', :reporter, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :developer, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :guest, :forbidden - it_behaves_like 'rejecting project packages protection rules request', nil, :not_found + context 'when not enough permissions' do + it_behaves_like 'rejecting project packages protection rules request using roles' + end context 'for maintainer' do let(:api_user) { maintainer } @@ -42,7 +56,7 @@ create(:package_protection_rule, project: project, package_name_pattern: "@my-scope/my-package-*") end - it 'gets the package protection rules' do + it 'gets the package protection rules', :aggregate_failures do get_package_rules expect(response).to have_gitlab_http_status(:ok) @@ -80,36 +94,45 @@ describe 'POST /projects/:id/packages/protection/rules' do let(:url) { "/projects/#{project.id}/packages/protection/rules" } let(:params) do - { package_name_pattern: '@my-new-scope/my-package-*', package_type: package_protection_rule.package_type, + { package_name_pattern: '@my-new-scope/my-package-*', + package_type: package_protection_rule.package_type, minimum_access_level_for_push: package_protection_rule.minimum_access_level_for_push } end subject(:post_package_rule) { post(api(url, api_user), params: params) } - it_behaves_like 'rejecting project packages protection rules request', :reporter, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :developer, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :guest, :forbidden - it_behaves_like 'rejecting project packages protection rules request', nil, :not_found + context 'when not enough permissions' do + it_behaves_like 'rejecting project packages protection rules request using roles' + end context 'for maintainer' do let(:api_user) { maintainer } - it 'creates a package protection rule' do + it 'creates a package protection rule', :aggregate_failures do expect { post_package_rule }.to change { Packages::Protection::Rule.count }.by(1) - expect(response).to have_gitlab_http_status(:created) end - it 'does not create a package protection rule with invalid package_type' do - params[:package_type] = "not in enum" - expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) - expect(response).to have_gitlab_http_status(:unprocessable_entity) + context 'with invalid package_type' do + before do + params[:package_type] = "not in enum" + end + + it 'does not create a package protection rule', :aggregate_failures do + expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end end - it 'does not create a package protection rule with invalid minimum_access_level_for_push' do - params[:minimum_access_level_for_push] = "not in enum" - expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) - expect(response).to have_gitlab_http_status(:unprocessable_entity) + context 'with invalid minimum_access_level_for_push' do + before do + params[:minimum_access_level_for_push] = "not in enum" + end + + it 'does not create a package protection rule', :aggregate_failures do + expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end end context 'when the project id is invalid' do @@ -139,15 +162,14 @@ subject(:destroy_package_rule) { delete(api(url, api_user)) } - it_behaves_like 'rejecting project packages protection rules request', :reporter, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :developer, :forbidden - it_behaves_like 'rejecting project packages protection rules request', :guest, :forbidden - it_behaves_like 'rejecting project packages protection rules request', nil, :not_found + context 'when not enough permissions' do + it_behaves_like 'rejecting project packages protection rules request using roles' + end context 'for maintainer' do let(:api_user) { maintainer } - it 'deletes the package protection rule' do + it 'deletes the package protection rule', :aggregate_failures do destroy_package_rule expect do Packages::Protection::Rule.find(package_protection_rule.id) -- GitLab From 065dd242f48e70dcfbaf59bcc73423d2a2e47c8d Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Tue, 11 Jun 2024 10:36:10 -0700 Subject: [PATCH 3/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Based on review feedback, this commit uses RSpec tables to reduce repetitive specs. https://gitlab.com/gitlab-org/gitlab/-/issues/457512 Changelog: added --- doc/api/project_packages_protection_rules.md | 2 +- .../project_packages_protection_rules_spec.rb | 52 ++++++++----------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index fbe1a970f81c45..33842095044192 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -9,7 +9,7 @@ description: "Documentation for the REST API for Package Protection Rules in Git DETAILS: **Tier:** Free, Premium, Ultimate -**Offering:** GitLab.com, Self-managed +**Offering:** Self-managed **Status:** Experiment > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151741) in GitLab 17.1 [with a flag](../administration/feature_flags.md) named `packages_protected_packages`. Disabled by default. diff --git a/spec/requests/api/project_packages_protection_rules_spec.rb b/spec/requests/api/project_packages_protection_rules_spec.rb index abe628351554b3..cde97cfd9f9720 100644 --- a/spec/requests/api/project_packages_protection_rules_spec.rb +++ b/spec/requests/api/project_packages_protection_rules_spec.rb @@ -15,17 +15,7 @@ let_it_be(:invalid_token) { 'invalid-token123' } let_it_be(:headers_with_invalid_token) { { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => invalid_token } } - shared_examples 'rejecting project packages protection rules request' do |user_role, status| - context "for #{user_role}" do - before do - project.send(:"add_#{user_role}", api_user) if user_role - end - - it_behaves_like 'returning response status', status - end - end - - shared_examples 'rejecting project packages protection rules request using roles' do + shared_examples 'rejecting project packages protection rules request' do using RSpec::Parameterized::TableSyntax where(:user_role, :status) do @@ -36,7 +26,11 @@ end with_them do - it_behaves_like 'rejecting project packages protection rules request', params[:user_role], params[:status] + before do + project.send(:"add_#{user_role}", api_user) if user_role + end + + it_behaves_like 'returning response status', params[:status] end end @@ -46,7 +40,7 @@ subject(:get_package_rules) { get(api(url, api_user)) } context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request using roles' + it_behaves_like 'rejecting project packages protection rules request' end context 'for maintainer' do @@ -66,13 +60,13 @@ context 'when the project id is invalid' do let(:url) { "/projects/invalid/packages/protection/rules" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when the project id does not exist' do let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when packages_protected_packages is disabled' do @@ -80,14 +74,14 @@ stub_feature_flags(packages_protected_packages: false) end - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end end context 'with invalid token' do subject(:get_package_rules) { get(api(url), headers: headers_with_invalid_token) } - it_behaves_like 'rejecting project packages protection rules request', nil, :unauthorized + it_behaves_like 'returning response status', :unauthorized end end @@ -102,7 +96,7 @@ subject(:post_package_rule) { post(api(url, api_user), params: params) } context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request using roles' + it_behaves_like 'rejecting project packages protection rules request' end context 'for maintainer' do @@ -138,13 +132,13 @@ context 'when the project id is invalid' do let(:url) { "/projects/invalid/packages/protection/rules" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when the project id does not exist' do let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when packages_protected_packages is disabled' do @@ -152,7 +146,7 @@ stub_feature_flags(packages_protected_packages: false) end - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end end end @@ -163,7 +157,7 @@ subject(:destroy_package_rule) { delete(api(url, api_user)) } context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request using roles' + it_behaves_like 'rejecting project packages protection rules request' end context 'for maintainer' do @@ -181,31 +175,31 @@ context 'when the package protection rule does belong to another project' do let(:url) { "/projects/#{other_project.id}/packages/protection/rules/#{package_protection_rule.id}" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when the project id is invalid' do let(:url) { "/projects/invalid/packages/protection/rules/#{package_protection_rule.id}" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when the project id does not exist' do let(:url) { "/projects/#{non_existing_record_id}/packages/protection/rules/#{package_protection_rule.id}" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when the rule id is invalid' do let(:url) { "/projects/#{project.id}/packages/protection/rules/invalid" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :bad_request + it_behaves_like 'returning response status', :bad_request end context 'when the rule id does not exist' do let(:url) { "/projects/#{project.id}/packages/protection/rules/#{non_existing_record_id}" } - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'when packages_protected_packages is disabled' do @@ -213,13 +207,13 @@ stub_feature_flags(packages_protected_packages: false) end - it_behaves_like 'rejecting project packages protection rules request', :maintainer, :not_found + it_behaves_like 'returning response status', :not_found end context 'with invalid token' do subject(:delete_package_rules) { delete(api(url), headers: headers_with_invalid_token) } - it_behaves_like 'rejecting project packages protection rules request', nil, :unauthorized + it_behaves_like 'returning response status', :unauthorized end end end -- GitLab From 5a55c32f851509860935b26bd3b1d759e3582e20 Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Wed, 12 Jun 2024 01:30:05 -0700 Subject: [PATCH 4/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Based on review feedback, this commit uses the declared_params helper. https://gitlab.com/gitlab-org/gitlab/-/issues/457512 Changelog: added --- lib/api/project_packages_protection_rules.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index f5d8dcdad4cb64..dca20faa323a3a 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -53,7 +53,7 @@ class ProjectPackagesProtectionRules < ::API::Base end post ':id/packages/protection/rules' do response = ::Packages::Protection::CreateRuleService.new(project: user_project, current_user: current_user, - params: declared(params, include_parent_namespaces: false)).execute + params: declared_params(params)).execute render_api_error!({ error: response.message }, :unprocessable_entity) if response.error? -- GitLab From 2ba2782533e3525bbccbb3c01fe091d6511ed5c9 Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Thu, 13 Jun 2024 03:45:04 -0700 Subject: [PATCH 5/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Based on review feedback, this commit uses .keys for the grape entity definition and fixes a broken curl call. https://gitlab.com/gitlab-org/gitlab/-/issues/457512 Changelog: added --- doc/api/project_packages_protection_rules.md | 1 + lib/api/project_packages_protection_rules.rb | 6 +-- .../project_packages_protection_rules_spec.rb | 39 +++++++++++-------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index 33842095044192..da14edb8f86854 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -104,6 +104,7 @@ Example request: ```shell curl --request POST \ --header "PRIVATE-TOKEN: " \ + --header "Content-Type: application/json" \ --url "https://gitlab.example.com/api/v4/projects/7/packages/protection/rules" \ --data '{ "package_name_pattern": "package-name-pattern-*", diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index dca20faa323a3a..5cc19c8c69e06e 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -45,10 +45,10 @@ class ProjectPackagesProtectionRules < ::API::Base params do requires :package_name_pattern, type: String, desc: 'Package name protected by the rule. For example @my-scope/my-package-*. Wildcard character * allowed.' - requires :package_type, type: String, values: Packages::Protection::Rule.package_types, + requires :package_type, type: String, values: Packages::Protection::Rule.package_types.keys, desc: 'Package type protected by the rule. For example npm.' - requires :minimum_access_level_for_push, - values: Packages::Protection::Rule.minimum_access_level_for_pushes, type: String, + requires :minimum_access_level_for_push, type: String, + values: Packages::Protection::Rule.minimum_access_level_for_pushes.keys, desc: 'Minimum GitLab access level able to push a package. For example developer, maintainer, owner.' end post ':id/packages/protection/rules' do diff --git a/spec/requests/api/project_packages_protection_rules_spec.rb b/spec/requests/api/project_packages_protection_rules_spec.rb index cde97cfd9f9720..95e4216f86d0a3 100644 --- a/spec/requests/api/project_packages_protection_rules_spec.rb +++ b/spec/requests/api/project_packages_protection_rules_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe API::ProjectPackagesProtectionRules, feature_category: :package_registry do +RSpec.describe API::ProjectPackagesProtectionRules, :aggregate_failures, feature_category: :package_registry do include ExclusiveLeaseHelpers let_it_be(:project) { create(:project, :private) } @@ -15,7 +15,7 @@ let_it_be(:invalid_token) { 'invalid-token123' } let_it_be(:headers_with_invalid_token) { { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => invalid_token } } - shared_examples 'rejecting project packages protection rules request' do + shared_examples 'rejecting project packages protection rules request when not enough permissions' do using RSpec::Parameterized::TableSyntax where(:user_role, :status) do @@ -39,9 +39,7 @@ subject(:get_package_rules) { get(api(url, api_user)) } - context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request' - end + it_behaves_like 'rejecting project packages protection rules request when not enough permissions' context 'for maintainer' do let(:api_user) { maintainer } @@ -50,7 +48,7 @@ create(:package_protection_rule, project: project, package_name_pattern: "@my-scope/my-package-*") end - it 'gets the package protection rules', :aggregate_failures do + it 'gets the package protection rules' do get_package_rules expect(response).to have_gitlab_http_status(:ok) @@ -95,14 +93,12 @@ subject(:post_package_rule) { post(api(url, api_user), params: params) } - context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request' - end + it_behaves_like 'rejecting project packages protection rules request when not enough permissions' context 'for maintainer' do let(:api_user) { maintainer } - it 'creates a package protection rule', :aggregate_failures do + it 'creates a package protection rule' do expect { post_package_rule }.to change { Packages::Protection::Rule.count }.by(1) expect(response).to have_gitlab_http_status(:created) end @@ -112,9 +108,9 @@ params[:package_type] = "not in enum" end - it 'does not create a package protection rule', :aggregate_failures do + it 'does not create a package protection rule' do expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) - expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(response).to have_gitlab_http_status(:bad_request) end end @@ -123,7 +119,18 @@ params[:minimum_access_level_for_push] = "not in enum" end - it 'does not create a package protection rule', :aggregate_failures do + it 'does not create a package protection rule' do + expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) + expect(response).to have_gitlab_http_status(:bad_request) + end + end + + context 'with already existing package_name_pattern' do + before do + params[:package_name_pattern] = package_protection_rule.package_name_pattern + end + + it 'does not create a package protection rule' do expect { post_package_rule }.to not_change(Packages::Protection::Rule, :count) expect(response).to have_gitlab_http_status(:unprocessable_entity) end @@ -156,14 +163,12 @@ subject(:destroy_package_rule) { delete(api(url, api_user)) } - context 'when not enough permissions' do - it_behaves_like 'rejecting project packages protection rules request' - end + it_behaves_like 'rejecting project packages protection rules request when not enough permissions' context 'for maintainer' do let(:api_user) { maintainer } - it 'deletes the package protection rule', :aggregate_failures do + it 'deletes the package protection rule' do destroy_package_rule expect do Packages::Protection::Rule.find(package_protection_rule.id) -- GitLab From 874c4da1d4bde25937de6d2c3dd2227269cf6908 Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Mon, 17 Jun 2024 02:19:25 -0700 Subject: [PATCH 6/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Based on review feedback, this commit updates the docs for a 422. Changelog: added --- doc/api/project_packages_protection_rules.md | 3 ++- lib/api/project_packages_protection_rules.rb | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index da14edb8f86854..12d77f48f038eb 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -94,10 +94,11 @@ If successful, returns [`201`](rest/index.md#status-codes) and the created packa Can return the following status codes: - `201 Created`: The package protection rule has been created successfully. +- `400 Bad Request`: The package protection rule was not valid. - `401 Unauthorized`: The access token is invalid. - `403 Forbidden`: The user does not have permission to create a package protection rule. - `404 Not Found`: The project was not found. -- `422 Unprocessable Entity`: The package protection rule was not valid. +- `422 Unprocessable Entity`: The package protection rule could not be created, e.g. because the `package_name_pattern` has been already taken. Example request: diff --git a/lib/api/project_packages_protection_rules.rb b/lib/api/project_packages_protection_rules.rb index 5cc19c8c69e06e..a86ac8fb07686c 100644 --- a/lib/api/project_packages_protection_rules.rb +++ b/lib/api/project_packages_protection_rules.rb @@ -35,6 +35,7 @@ class ProjectPackagesProtectionRules < ::API::Base desc 'Create a package protection rule for a project' do success Entities::Projects::Packages::Protection::Rule failure [ + { code: 400, message: 'Bad Request' }, { code: 401, message: 'Unauthorized' }, { code: 403, message: 'Forbidden' }, { code: 404, message: 'Not Found' }, -- GitLab From b3874828e72673865896cc0b5464d41ecf384669 Mon Sep 17 00:00:00 2001 From: Nicholas Wittstruck Date: Mon, 17 Jun 2024 12:03:57 -0700 Subject: [PATCH 7/7] Protected packages: REST API POST package protection rule Adds a POST route to the REST API to create a package protection rule for a project. Based on review feedback, this commit updates the docs. Changelog: added --- doc/api/project_packages_protection_rules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/project_packages_protection_rules.md b/doc/api/project_packages_protection_rules.md index 12d77f48f038eb..f93e0cb2f60ef1 100644 --- a/doc/api/project_packages_protection_rules.md +++ b/doc/api/project_packages_protection_rules.md @@ -93,12 +93,12 @@ If successful, returns [`201`](rest/index.md#status-codes) and the created packa Can return the following status codes: -- `201 Created`: The package protection rule has been created successfully. -- `400 Bad Request`: The package protection rule was not valid. +- `201 Created`: The package protection rule was created successfully. +- `400 Bad Request`: The package protection rule is invalid. - `401 Unauthorized`: The access token is invalid. - `403 Forbidden`: The user does not have permission to create a package protection rule. - `404 Not Found`: The project was not found. -- `422 Unprocessable Entity`: The package protection rule could not be created, e.g. because the `package_name_pattern` has been already taken. +- `422 Unprocessable Entity`: The package protection rule could not be created, for example, because the `package_name_pattern` is already taken. Example request: -- GitLab