From aee811f7c97363cb9d7b9ccb33f4422854c40149 Mon Sep 17 00:00:00 2001 From: abime Date: Tue, 26 Aug 2025 12:09:43 +0530 Subject: [PATCH 1/3] Organize job token permission table with category sections Remove tabs - keep all endpoints in same table Resolve doc related review comments --- doc/ci/jobs/fine_grained_permissions.md | 425 ++++++++++-------- lib/tasks/ci/job_tokens_task.rb | 211 +++++++-- spec/tasks/ci/job_tokens_task_spec.rb | 48 +- .../templates/fine_grained_permissions.md.erb | 8 +- 4 files changed, 449 insertions(+), 243 deletions(-) diff --git a/doc/ci/jobs/fine_grained_permissions.md b/doc/ci/jobs/fine_grained_permissions.md index 0b1e01d06db399..691e12af4eaa1d 100644 --- a/doc/ci/jobs/fine_grained_permissions.md +++ b/doc/ci/jobs/fine_grained_permissions.md @@ -63,189 +63,242 @@ access any allowed resources in the current project. The following endpoints are available for CI/CD job tokens. -`None` means fine-grained permissions cannot control access to this endpoint. - -| Permissions | Permission Names | Path | Description | -| ----------- | ---------------- | ---- | ----------- | -| Deployments: Read and write | `ADMIN_DEPLOYMENTS` | `DELETE /projects/:id/deployments/:deployment_id` | Delete a specific deployment | -| Deployments: Read and write | `ADMIN_DEPLOYMENTS` | `POST /projects/:id/deployments/:deployment_id/approval` | Approve or reject a blocked deployment | -| Deployments: Read and write | `ADMIN_DEPLOYMENTS` | `PUT /projects/:id/deployments/:deployment_id` | Update a deployment | -| Deployments: Read and write, Environments: Read and write | `ADMIN_DEPLOYMENTS`, `ADMIN_ENVIRONMENTS` | `POST /projects/:id/deployments` | Create a deployment | -| Deployments: Read | `READ_DEPLOYMENTS` | `GET /projects/:id/deployments/:deployment_id/merge_requests` | List of merge requests associated with a deployment | -| Deployments: Read | `READ_DEPLOYMENTS` | `GET /projects/:id/deployments/:deployment_id` | Get a specific deployment | -| Deployments: Read | `READ_DEPLOYMENTS` | `GET /projects/:id/deployments` | List project deployments | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `DELETE /projects/:id/environments/:environment_id` | Delete an environment | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `DELETE /projects/:id/environments/review_apps` | Delete multiple stopped review apps | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `POST /projects/:id/environments/:environment_id/stop` | Stop an environment | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `POST /projects/:id/environments/stop_stale` | Stop stale environments | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `POST /projects/:id/environments` | Create a new environment | -| Environments: Read and write | `ADMIN_ENVIRONMENTS` | `PUT /projects/:id/environments/:environment_id` | Update an existing environment | -| Environments: Read | `READ_ENVIRONMENTS` | `GET /projects/:id/environments/:environment_id` | Get a specific environment | -| Environments: Read | `READ_ENVIRONMENTS` | `GET /projects/:id/environments` | List environments | -| Jobs: Read | `READ_JOBS` | `GET /jobs/:id/artifacts` | Download the artifacts file for job | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/jobs/:job_id/artifacts/*artifact_path` | Download a specific file from artifacts archive | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/jobs/:job_id/artifacts` | Download the artifacts archive from a job | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/jobs/artifacts/:ref_name/download` | Download the artifacts archive from a job | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path` | Download a specific file from artifacts archive from a ref | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/jobs` | Get a projects jobs | -| Jobs: Read | `READ_JOBS` | `GET /projects/:id/pipelines/:pipeline_id/jobs` | Get pipeline jobs | -| Merge requests: Read | `READ_MERGE_REQUESTS` | `GET /projects/:id/merge_requests/:merge_request_iid` | Get single merge request | -| Merge requests: Read | `READ_MERGE_REQUESTS` | `GET /projects/:id/merge_requests/:noteable_id/notes/:note_id` | Get a single merge request note | -| Merge requests: Read | `READ_MERGE_REQUESTS` | `GET /projects/:id/merge_requests/:noteable_id/notes` | Get a list of merge request notes | -| Merge requests: Read | `READ_MERGE_REQUESTS` | `GET /projects/:id/merge_requests` | List project merge requests | -| None | | `DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name` | Delete repository tag | -| None | | `DELETE /projects/:id/registry/repositories/:repository_id/tags` | Delete repository tags (in bulk) | -| None | | `DELETE /projects/:id/registry/repositories/:repository_id` | Delete repository | -| None | | `GET /group/:id/-/packages/composer/*package_name` | Composer packages endpoint at group level for package versions metadata | -| None | | `GET /group/:id/-/packages/composer/p/:sha` | Composer packages endpoint at group level for packages list | -| None | | `GET /group/:id/-/packages/composer/p2/*package_name` | Composer v2 packages p2 endpoint at group level for package versions metadata | -| None | | `GET /group/:id/-/packages/composer/packages` | Composer packages endpoint at group level | -| None | | `GET /groups/:id/-/packages/npm/*package_name` | NPM registry metadata endpoint | -| None | | `GET /groups/:id/-/packages/pypi/files/:sha256/*file_identifier` | Download a package file from a group | -| None | | `GET /groups/:id/-/packages/pypi/simple/*package_name` | The PyPi Simple Group Package Endpoint | -| None | | `GET /groups/:id/-/packages/pypi/simple` | The PyPi Simple Group Index Endpoint | -| None | | `GET /job/allowed_agents` | Get current agents | -| None | | `GET /job` | Get current job using job token | -| None | | `GET /packages/conan/v1/conans/search` | Search for packages | -| None | | `GET /packages/conan/v1/ping` | Ping the Conan API | -| None | | `GET /packages/conan/v1/users/authenticate` | Authenticate user against conan CLI | -| None | | `GET /packages/conan/v1/users/check_credentials` | Check for valid user credentials per conan CLI | -| None | | `GET /packages/npm/*package_name` | NPM registry metadata endpoint | -| None | | `GET /projects/:id/packages/conan/v1/conans/search` | Search for packages | -| None | | `GET /projects/:id/packages/conan/v1/ping` | Ping the Conan API | -| None | | `GET /projects/:id/packages/conan/v1/users/authenticate` | Authenticate user against conan CLI | -| None | | `GET /projects/:id/packages/conan/v1/users/check_credentials` | Check for valid user credentials per conan CLI | -| None | | `GET /projects/:id/packages/conan/v2/conans/search` | Search for packages | -| None | | `GET /projects/:id/packages/conan/v2/users/authenticate` | Authenticate user against conan CLI | -| None | | `GET /projects/:id/packages/conan/v2/users/check_credentials` | Check for valid user credentials per conan CLI | -| None | | `GET /projects/:id/registry/repositories/:repository_id/tags/:tag_name` | Get details about a repository tag | -| None | | `GET /projects/:id/registry/repositories/:repository_id/tags` | List tags of a repository | -| None | | `GET /projects/:id/registry/repositories` | List container repositories within a project | -| None | | `POST /internal/dast/site_validations/:id/transition` | Transitions a DAST site validation to a new state. | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | Deletes the given tag | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | Delete Package | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /packages/npm/-/package/*package_name/dist-tags/:tag` | Deletes the given tag | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/:package_id/package_files/:package_file_id` | Delete a package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/:package_id` | Delete a project package | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | Delete Package | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision` | Delete package revision | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision` | Delete recipe revision | -| Packages: Read and write | `ADMIN_PACKAGES` | `DELETE /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | Deletes the given tag | -| Packages: Read and write | `ADMIN_PACKAGES` | `POST /projects/:id/packages/composer` | Composer packages endpoint for registering packages | -| Packages: Read and write | `ADMIN_PACKAGES` | `POST /projects/:id/packages/pypi/authorize` | Authorize the PyPi package upload from workhorse | -| Packages: Read and write | `ADMIN_PACKAGES` | `POST /projects/:id/packages/pypi` | The PyPi Package upload endpoint | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | Create or Update the given tag for the given NPM package and version | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | Workhorse authorize the conan recipe file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | Upload recipe package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | Workhorse authorize the conan package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | Upload package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /packages/npm/-/package/*package_name/dist-tags/:tag` | Create or Update the given tag for the given NPM package and version | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | Workhorse authorize the conan recipe file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | Upload recipe package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | Workhorse authorize the conan package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | Upload package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name/authorize` | Workhorse authorize the conan recipe file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | Upload recipe package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name/authorize` | Workhorse authorize the conan package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | Upload package files | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name/authorize` | Workhorse authorize generic package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | Upload package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/maven/*path/:file_name/authorize` | Workhorse authorize the maven package file upload | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/maven/*path/:file_name` | Upload the maven package file | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | Create or Update the given tag for the given NPM package and version | -| Packages: Read and write | `ADMIN_PACKAGES` | `PUT /projects/:id/packages/npm/:package_name` | Create or deprecate NPM package | -| Packages: Read | `READ_PACKAGES` | `GET /groups/:id/-/packages/maven/*path/:file_name` | Download the maven package file at a group level | -| Packages: Read | `READ_PACKAGES` | `GET /groups/:id/-/packages/npm/-/package/*package_name/dist-tags` | Get all tags for a given an NPM package | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | Recipe Digest | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | Recipe Download Urls | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | Package Digest | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | Package Download Urls | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference` | Package Snapshot | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | Get package references metadata | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | Recipe Snapshot | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | Download recipe files | -| Packages: Read | `READ_PACKAGES` | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | Download package files | -| Packages: Read | `READ_PACKAGES` | `GET /packages/maven/*path/:file_name` | Download the maven package file at instance level | -| Packages: Read | `READ_PACKAGES` | `GET /packages/npm/-/package/*package_name/dist-tags` | Get all tags for a given an NPM package | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/:package_id/package_files` | List package files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/:package_id` | Get a single project package | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/composer/archives/*package_name` | Composer package endpoint to download a package archive | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | Recipe Digest | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | Recipe Download Urls | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | Package Digest | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | Package Download Urls | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference` | Package Snapshot | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | Get package references metadata | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | Recipe Snapshot | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | Download recipe files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | Download package files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/latest` | Get the latest recipe revision | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | Download recipe files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files` | List recipe files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/latest` | Get the latest package revision | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | Download package files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files` | List package files | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions` | Get the list of package revisions | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/search` | Get package references metadata | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions` | Get the list of revisions | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/search` | Get package references metadata | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | Download package file | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/go/*module_name/@v/:module_version.info` | Version metadata | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/go/*module_name/@v/:module_version.mod` | Download module file | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/go/*module_name/@v/:module_version.zip` | Download module source | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/go/*module_name/@v/list` | List | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/maven/*path/:file_name` | Download the maven package file at a project level | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/npm/*package_name/-/*file_name` | Download the NPM tarball | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/npm/*package_name` | NPM registry metadata endpoint | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/npm/-/package/*package_name/dist-tags` | Get all tags for a given an NPM package | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/pypi/files/:sha256/*file_identifier` | The PyPi package download endpoint | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/pypi/simple/*package_name` | The PyPi Simple Project Package Endpoint | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/pypi/simple` | The PyPi Simple Project Index Endpoint | -| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages` | Get a list of project packages | -| Packages: Read | `READ_PACKAGES` | `POST /groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk` | NPM registry bulk advisory endpoint | -| Packages: Read | `READ_PACKAGES` | `POST /groups/:id/-/packages/npm/-/npm/v1/security/audits/quick` | NPM registry quick audit endpoint | -| Packages: Read | `READ_PACKAGES` | `POST /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls` | Package Upload Urls | -| Packages: Read | `READ_PACKAGES` | `POST /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | Recipe Upload Urls | -| Packages: Read | `READ_PACKAGES` | `POST /packages/npm/-/npm/v1/security/advisories/bulk` | NPM registry bulk advisory endpoint | -| Packages: Read | `READ_PACKAGES` | `POST /packages/npm/-/npm/v1/security/audits/quick` | NPM registry quick audit endpoint | -| Packages: Read | `READ_PACKAGES` | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls` | Package Upload Urls | -| Packages: Read | `READ_PACKAGES` | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | Recipe Upload Urls | -| Packages: Read | `READ_PACKAGES` | `POST /projects/:id/packages/npm/-/npm/v1/security/advisories/bulk` | NPM registry bulk advisory endpoint | -| Packages: Read | `READ_PACKAGES` | `POST /projects/:id/packages/npm/-/npm/v1/security/audits/quick` | NPM registry quick audit endpoint | -| Pipelines: Read and write | `ADMIN_PIPELINES` | `PUT /projects/:id/pipelines/:pipeline_id/metadata` | Updates pipeline metadata | -| Pipelines: Read | `READ_PIPELINES` | `GET /projects/:id/packages/:package_id/pipelines` | Get the pipelines for a single project package | -| Pipelines: Read | `READ_PIPELINES` | `GET /projects/:id/pipelines/:pipeline_id/bridges` | Get pipeline bridge jobs | -| Pipelines: Read | `READ_PIPELINES` | `GET /projects/:id/pipelines/:pipeline_id` | Gets a specific pipeline for the project | -| Pipelines: Read | `READ_PIPELINES` | `GET /projects/:id/pipelines` | Get all Pipelines of the project | -| Releases: Read and write | `ADMIN_RELEASES` | `DELETE /projects/:id/releases/:tag_name/assets/links/:link_id` | Delete a release link | -| Releases: Read and write | `ADMIN_RELEASES` | `DELETE /projects/:id/releases/:tag_name` | Delete a release | -| Releases: Read and write | `ADMIN_RELEASES` | `POST /projects/:id/catalog/publish` | Publish a new component project release as version to the CI/CD catalog | -| Releases: Read and write | `ADMIN_RELEASES` | `POST /projects/:id/releases/:tag_name/assets/links` | Create a release link | -| Releases: Read and write | `ADMIN_RELEASES` | `POST /projects/:id/releases/:tag_name/evidence` | Collect release evidence | -| Releases: Read and write | `ADMIN_RELEASES` | `POST /projects/:id/releases` | Create a release | -| Releases: Read and write | `ADMIN_RELEASES` | `PUT /projects/:id/releases/:tag_name/assets/links/:link_id` | Update a release link | -| Releases: Read and write | `ADMIN_RELEASES` | `PUT /projects/:id/releases/:tag_name` | Update a release | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases/:tag_name/assets/links/:link_id` | Get a release link | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases/:tag_name/assets/links` | List links of a release | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases/:tag_name/downloads/*direct_asset_path` | Download a project release asset file | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases/:tag_name` | Get a release by a tag name | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases/permalink/latest(/)(*suffix_path)` | Get the latest project release | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/releases` | List Releases | -| Releases: Read | `READ_RELEASES` | `GET /projects/:id/repository/changelog` | Generates a changelog section for a release and returns it | -| Repositories: Read | `READ_REPOSITORIES` | `GET /projects/:id/repository/branches` | Get a project repository branches | -| Repositories: Read | `READ_REPOSITORIES` | `GET /projects/:id/repository/commits/:sha/merge_requests` | Get Merge Requests associated with a commit | -| Repositories: Read | `READ_REPOSITORIES` | `GET /projects/:id/repository/commits/:sha` | Get a specific commit of a project | -| Repositories: Read | `READ_REPOSITORIES` | `GET /projects/:id/repository/files/:file_path/raw` | Get raw file contents from the repository | -| Repositories: Read | `READ_REPOSITORIES` | `GET /projects/:id/repository/tags` | Get a project repository tags | -| Secure files: Read and write | `ADMIN_SECURE_FILES` | `DELETE /projects/:id/secure_files/:secure_file_id` | Remove a secure file | -| Secure files: Read and write | `ADMIN_SECURE_FILES` | `POST /projects/:id/secure_files` | Create a secure file | -| Secure files: Read | `READ_SECURE_FILES` | `GET /projects/:id/secure_files/:secure_file_id/download` | Download secure file | -| Secure files: Read | `READ_SECURE_FILES` | `GET /projects/:id/secure_files/:secure_file_id` | Get the details of a specific secure file in a project | -| Secure files: Read | `READ_SECURE_FILES` | `GET /projects/:id/secure_files` | Get list of secure files in a project | -| Terraform state: Read and write | `ADMIN_TERRAFORM_STATE` | `DELETE /projects/:id/terraform/state/:name/lock` | Unlock a Terraform state of a certain name | -| Terraform state: Read and write | `ADMIN_TERRAFORM_STATE` | `DELETE /projects/:id/terraform/state/:name/versions/:serial` | Delete a Terraform state version | -| Terraform state: Read and write | `ADMIN_TERRAFORM_STATE` | `DELETE /projects/:id/terraform/state/:name` | Delete a Terraform state of a certain name | -| Terraform state: Read and write | `ADMIN_TERRAFORM_STATE` | `POST /projects/:id/terraform/state/:name/lock` | Lock a Terraform state of a certain name | -| Terraform state: Read and write | `ADMIN_TERRAFORM_STATE` | `POST /projects/:id/terraform/state/:name` | Add a new Terraform state or update an existing one | -| Terraform state: Read | `READ_TERRAFORM_STATE` | `GET /projects/:id/terraform/state/:name/versions/:serial` | Get a Terraform state version | -| Terraform state: Read | `READ_TERRAFORM_STATE` | `GET /projects/:id/terraform/state/:name` | Get a Terraform state by its name | +## Deployments endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get a specific deployment | `GET /projects/:id/deployments/:deployment_id` | `READ_DEPLOYMENTS` | Read | +| List of merge requests associated with a deployment | `GET /projects/:id/deployments/:deployment_id/merge_requests` | `READ_DEPLOYMENTS` | Read | +| List project deployments | `GET /projects/:id/deployments` | `READ_DEPLOYMENTS` | Read | +| Approve or reject a blocked deployment | `POST /projects/:id/deployments/:deployment_id/approval` | `ADMIN_DEPLOYMENTS` | Read and write | +| Create a deployment | `POST /projects/:id/deployments` | `ADMIN_DEPLOYMENTS` | Read and write | +| Delete a specific deployment | `DELETE /projects/:id/deployments/:deployment_id` | `ADMIN_DEPLOYMENTS` | Read and write | +| Update a deployment | `PUT /projects/:id/deployments/:deployment_id` | `ADMIN_DEPLOYMENTS` | Read and write | + +## Environments endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get a specific environment | `GET /projects/:id/environments/:environment_id` | `READ_ENVIRONMENTS` | Read | +| List environments | `GET /projects/:id/environments` | `READ_ENVIRONMENTS` | Read | +| Create a deployment | `POST /projects/:id/deployments` | `ADMIN_ENVIRONMENTS` | Read and write | +| Create a new environment | `POST /projects/:id/environments` | `ADMIN_ENVIRONMENTS` | Read and write | +| Delete an environment | `DELETE /projects/:id/environments/:environment_id` | `ADMIN_ENVIRONMENTS` | Read and write | +| Delete multiple stopped review apps | `DELETE /projects/:id/environments/review_apps` | `ADMIN_ENVIRONMENTS` | Read and write | +| Stop an environment | `POST /projects/:id/environments/:environment_id/stop` | `ADMIN_ENVIRONMENTS` | Read and write | +| Stop stale environments | `POST /projects/:id/environments/stop_stale` | `ADMIN_ENVIRONMENTS` | Read and write | +| Update an existing environment | `PUT /projects/:id/environments/:environment_id` | `ADMIN_ENVIRONMENTS` | Read and write | + +## Jobs endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Download a specific file from artifacts archive | `GET /projects/:id/jobs/:job_id/artifacts/*artifact_path` | `READ_JOBS` | Read | +| Download a specific file from artifacts archive from a ref | `GET /projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path` | `READ_JOBS` | Read | +| Download the artifacts archive from a job | `GET /projects/:id/jobs/:job_id/artifacts` | `READ_JOBS` | Read | +| Download the artifacts archive from a job | `GET /projects/:id/jobs/artifacts/:ref_name/download` | `READ_JOBS` | Read | +| Download the artifacts file for job | `GET /jobs/:id/artifacts` | `READ_JOBS` | Read | +| Get a projects jobs | `GET /projects/:id/jobs` | `READ_JOBS` | Read | +| Get pipeline jobs | `GET /projects/:id/pipelines/:pipeline_id/jobs` | `READ_JOBS` | Read | + +## Merge requests endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get a list of merge request notes | `GET /projects/:id/merge_requests/:noteable_id/notes` | `READ_MERGE_REQUESTS` | Read | +| Get a single merge request note | `GET /projects/:id/merge_requests/:noteable_id/notes/:note_id` | `READ_MERGE_REQUESTS` | Read | +| Get single merge request | `GET /projects/:id/merge_requests/:merge_request_iid` | `READ_MERGE_REQUESTS` | Read | +| List project merge requests | `GET /projects/:id/merge_requests` | `READ_MERGE_REQUESTS` | Read | + +## Packages endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Composer package endpoint to download a package archive | `GET /projects/:id/packages/composer/archives/*package_name` | `READ_PACKAGES` | Read | +| Download module file | `GET /projects/:id/packages/go/*module_name/@v/:module_version.mod` | `READ_PACKAGES` | Read | +| Download module source | `GET /projects/:id/packages/go/*module_name/@v/:module_version.zip` | `READ_PACKAGES` | Read | +| Download package file | `GET /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | `READ_PACKAGES` | Read | +| Download package files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | `READ_PACKAGES` | Read | +| Download package files | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `READ_PACKAGES` | Read | +| Download package files | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `READ_PACKAGES` | Read | +| Download recipe files | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `READ_PACKAGES` | Read | +| Download recipe files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | `READ_PACKAGES` | Read | +| Download recipe files | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `READ_PACKAGES` | Read | +| Download the NPM tarball | `GET /projects/:id/packages/npm/*package_name/-/*file_name` | `READ_PACKAGES` | Read | +| Download the maven package file at a group level | `GET /groups/:id/-/packages/maven/*path/:file_name` | `READ_PACKAGES` | Read | +| Download the maven package file at a project level | `GET /projects/:id/packages/maven/*path/:file_name` | `READ_PACKAGES` | Read | +| Download the maven package file at instance level | `GET /packages/maven/*path/:file_name` | `READ_PACKAGES` | Read | +| Get a list of project packages | `GET /projects/:id/packages` | `READ_PACKAGES` | Read | +| Get a single project package | `GET /projects/:id/packages/:package_id` | `READ_PACKAGES` | Read | +| Get all tags for a given an NPM package | `GET /groups/:id/-/packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | +| Get all tags for a given an NPM package | `GET /projects/:id/packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | +| Get all tags for a given an NPM package | `GET /packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | +| Get package references metadata | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | +| Get package references metadata | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | +| Get package references metadata | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | +| Get package references metadata | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/search` | `READ_PACKAGES` | Read | +| Get the latest package revision | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/latest` | `READ_PACKAGES` | Read | +| Get the latest recipe revision | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/latest` | `READ_PACKAGES` | Read | +| Get the list of package revisions | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions` | `READ_PACKAGES` | Read | +| Get the list of revisions | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions` | `READ_PACKAGES` | Read | +| List | `GET /projects/:id/packages/go/*module_name/@v/list` | `READ_PACKAGES` | Read | +| List package files | `GET /projects/:id/packages/:package_id/package_files` | `READ_PACKAGES` | Read | +| List package files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files` | `READ_PACKAGES` | Read | +| List recipe files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files` | `READ_PACKAGES` | Read | +| NPM registry bulk advisory endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | +| NPM registry bulk advisory endpoint | `POST /groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | +| NPM registry bulk advisory endpoint | `POST /packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | +| NPM registry metadata endpoint | `GET /projects/:id/packages/npm/*package_name` | `READ_PACKAGES` | Read | +| NPM registry quick audit endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | +| NPM registry quick audit endpoint | `POST /packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | +| NPM registry quick audit endpoint | `POST /groups/:id/-/packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | +| Package Digest | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | `READ_PACKAGES` | Read | +| Package Digest | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | `READ_PACKAGES` | Read | +| Package Download Urls | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | `READ_PACKAGES` | Read | +| Package Download Urls | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | `READ_PACKAGES` | Read | +| Package Snapshot | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference` | `READ_PACKAGES` | Read | +| Package Snapshot | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference` | `READ_PACKAGES` | Read | +| Package Upload Urls | `POST /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls` | `READ_PACKAGES` | Read | +| Package Upload Urls | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls` | `READ_PACKAGES` | Read | +| Recipe Digest | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | `READ_PACKAGES` | Read | +| Recipe Digest | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | `READ_PACKAGES` | Read | +| Recipe Download Urls | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | `READ_PACKAGES` | Read | +| Recipe Download Urls | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | `READ_PACKAGES` | Read | +| Recipe Snapshot | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `READ_PACKAGES` | Read | +| Recipe Snapshot | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `READ_PACKAGES` | Read | +| Recipe Upload Urls | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | `READ_PACKAGES` | Read | +| Recipe Upload Urls | `POST /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | `READ_PACKAGES` | Read | +| The PyPi Simple Project Index Endpoint | `GET /projects/:id/packages/pypi/simple` | `READ_PACKAGES` | Read | +| The PyPi Simple Project Package Endpoint | `GET /projects/:id/packages/pypi/simple/*package_name` | `READ_PACKAGES` | Read | +| The PyPi package download endpoint | `GET /projects/:id/packages/pypi/files/:sha256/*file_identifier` | `READ_PACKAGES` | Read | +| Version metadata | `GET /projects/:id/packages/go/*module_name/@v/:module_version.info` | `READ_PACKAGES` | Read | +| Authorize the PyPi package upload from workhorse | `POST /projects/:id/packages/pypi/authorize` | `ADMIN_PACKAGES` | Read and write | +| Composer packages endpoint for registering packages | `POST /projects/:id/packages/composer` | `ADMIN_PACKAGES` | Read and write | +| Create or Update the given tag for the given NPM package and version | `PUT /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Create or Update the given tag for the given NPM package and version | `PUT /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Create or Update the given tag for the given NPM package and version | `PUT /packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Create or deprecate NPM package | `PUT /projects/:id/packages/npm/:package_name` | `ADMIN_PACKAGES` | Read and write | +| Delete Package | `DELETE /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `ADMIN_PACKAGES` | Read and write | +| Delete Package | `DELETE /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `ADMIN_PACKAGES` | Read and write | +| Delete a package file | `DELETE /projects/:id/packages/:package_id/package_files/:package_file_id` | `ADMIN_PACKAGES` | Read and write | +| Delete a project package | `DELETE /projects/:id/packages/:package_id` | `ADMIN_PACKAGES` | Read and write | +| Delete package revision | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision` | `ADMIN_PACKAGES` | Read and write | +| Delete recipe revision | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision` | `ADMIN_PACKAGES` | Read and write | +| Deletes the given tag | `DELETE /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Deletes the given tag | `DELETE /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Deletes the given tag | `DELETE /packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| The PyPi Package upload endpoint | `POST /projects/:id/packages/pypi` | `ADMIN_PACKAGES` | Read and write | +| Upload package file | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload package files | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload package files | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload recipe package files | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload recipe package files | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload recipe package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload the maven package file | `PUT /projects/:id/packages/maven/*path/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize generic package file | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan package file | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan recipe file | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan recipe file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan recipe file | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the maven package file upload | `PUT /projects/:id/packages/maven/*path/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | + +## Pipelines endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get all Pipelines of the project | `GET /projects/:id/pipelines` | `READ_PIPELINES` | Read | +| Get pipeline bridge jobs | `GET /projects/:id/pipelines/:pipeline_id/bridges` | `READ_PIPELINES` | Read | +| Get the pipelines for a single project package | `GET /projects/:id/packages/:package_id/pipelines` | `READ_PIPELINES` | Read | +| Gets a specific pipeline for the project | `GET /projects/:id/pipelines/:pipeline_id` | `READ_PIPELINES` | Read | +| Updates pipeline metadata | `PUT /projects/:id/pipelines/:pipeline_id/metadata` | `ADMIN_PIPELINES` | Read and write | + +## Releases endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Download a project release asset file | `GET /projects/:id/releases/:tag_name/downloads/*direct_asset_path` | `READ_RELEASES` | Read | +| Generates a changelog section for a release and returns it | `GET /projects/:id/repository/changelog` | `READ_RELEASES` | Read | +| Get a release by a tag name | `GET /projects/:id/releases/:tag_name` | `READ_RELEASES` | Read | +| Get a release link | `GET /projects/:id/releases/:tag_name/assets/links/:link_id` | `READ_RELEASES` | Read | +| Get the latest project release | `GET /projects/:id/releases/permalink/latest(/)(*suffix_path)` | `READ_RELEASES` | Read | +| List Releases | `GET /projects/:id/releases` | `READ_RELEASES` | Read | +| List links of a release | `GET /projects/:id/releases/:tag_name/assets/links` | `READ_RELEASES` | Read | +| Collect release evidence | `POST /projects/:id/releases/:tag_name/evidence` | `ADMIN_RELEASES` | Read and write | +| Create a release | `POST /projects/:id/releases` | `ADMIN_RELEASES` | Read and write | +| Create a release link | `POST /projects/:id/releases/:tag_name/assets/links` | `ADMIN_RELEASES` | Read and write | +| Delete a release | `DELETE /projects/:id/releases/:tag_name` | `ADMIN_RELEASES` | Read and write | +| Delete a release link | `DELETE /projects/:id/releases/:tag_name/assets/links/:link_id` | `ADMIN_RELEASES` | Read and write | +| Publish a new component project release as version to the CI/CD catalog | `POST /projects/:id/catalog/publish` | `ADMIN_RELEASES` | Read and write | +| Update a release | `PUT /projects/:id/releases/:tag_name` | `ADMIN_RELEASES` | Read and write | +| Update a release link | `PUT /projects/:id/releases/:tag_name/assets/links/:link_id` | `ADMIN_RELEASES` | Read and write | + +## Repositories endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get Merge Requests associated with a commit | `GET /projects/:id/repository/commits/:sha/merge_requests` | `READ_REPOSITORIES` | Read | +| Get a project repository branches | `GET /projects/:id/repository/branches` | `READ_REPOSITORIES` | Read | +| Get a project repository tags | `GET /projects/:id/repository/tags` | `READ_REPOSITORIES` | Read | +| Get a specific commit of a project | `GET /projects/:id/repository/commits/:sha` | `READ_REPOSITORIES` | Read | +| Get raw file contents from the repository | `GET /projects/:id/repository/files/:file_path/raw` | `READ_REPOSITORIES` | Read | + +## Secure files endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Download secure file | `GET /projects/:id/secure_files/:secure_file_id/download` | `READ_SECURE_FILES` | Read | +| Get list of secure files in a project | `GET /projects/:id/secure_files` | `READ_SECURE_FILES` | Read | +| Get the details of a specific secure file in a project | `GET /projects/:id/secure_files/:secure_file_id` | `READ_SECURE_FILES` | Read | +| Create a secure file | `POST /projects/:id/secure_files` | `ADMIN_SECURE_FILES` | Read and write | +| Remove a secure file | `DELETE /projects/:id/secure_files/:secure_file_id` | `ADMIN_SECURE_FILES` | Read and write | + +## Terraform state endpoints + +| Permission | API endpoint | Permission name | Scope | +| ---------- | ------------ | --------------- | ----- | +| Get a Terraform state by its name | `GET /projects/:id/terraform/state/:name` | `READ_TERRAFORM_STATE` | Read | +| Get a Terraform state version | `GET /projects/:id/terraform/state/:name/versions/:serial` | `READ_TERRAFORM_STATE` | Read | +| Add a new Terraform state or update an existing one | `POST /projects/:id/terraform/state/:name` | `ADMIN_TERRAFORM_STATE` | Read and write | +| Delete a Terraform state of a certain name | `DELETE /projects/:id/terraform/state/:name` | `ADMIN_TERRAFORM_STATE` | Read and write | +| Delete a Terraform state version | `DELETE /projects/:id/terraform/state/:name/versions/:serial` | `ADMIN_TERRAFORM_STATE` | Read and write | +| Lock a Terraform state of a certain name | `POST /projects/:id/terraform/state/:name/lock` | `ADMIN_TERRAFORM_STATE` | Read and write | +| Unlock a Terraform state of a certain name | `DELETE /projects/:id/terraform/state/:name/lock` | `ADMIN_TERRAFORM_STATE` | Read and write | + +## Unavailable endpoints + +These API endpoints cannot be assigned as fine-grained permissions for job tokens. + +| Permission | API endpoint | +| ---------- | ------------ | +| Delete repository | `DELETE /projects/:id/registry/repositories/:repository_id` | +| Delete repository tags (in bulk) | `DELETE /projects/:id/registry/repositories/:repository_id/tags` | +| Delete repository tag | `DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name` | +| Composer packages endpoint at group level for package versions metadata | `GET /group/:id/-/packages/composer/*package_name` | +| Composer packages endpoint at group level for packages list | `GET /group/:id/-/packages/composer/p/:sha` | +| Composer v2 packages p2 endpoint at group level for package versions metadata | `GET /group/:id/-/packages/composer/p2/*package_name` | +| Composer packages endpoint at group level | `GET /group/:id/-/packages/composer/packages` | +| NPM registry metadata endpoint | `GET /groups/:id/-/packages/npm/*package_name` | +| Download a package file from a group | `GET /groups/:id/-/packages/pypi/files/:sha256/*file_identifier` | +| The PyPi Simple Group Index Endpoint | `GET /groups/:id/-/packages/pypi/simple` | +| The PyPi Simple Group Package Endpoint | `GET /groups/:id/-/packages/pypi/simple/*package_name` | +| Get current job using job token | `GET /job` | +| Get current agents | `GET /job/allowed_agents` | +| Search for packages | `GET /packages/conan/v1/conans/search` | +| Ping the Conan API | `GET /packages/conan/v1/ping` | +| Authenticate user against conan CLI | `GET /packages/conan/v1/users/authenticate` | +| Check for valid user credentials per conan CLI | `GET /packages/conan/v1/users/check_credentials` | +| NPM registry metadata endpoint | `GET /packages/npm/*package_name` | +| Search for packages | `GET /projects/:id/packages/conan/v1/conans/search` | +| Ping the Conan API | `GET /projects/:id/packages/conan/v1/ping` | +| Authenticate user against conan CLI | `GET /projects/:id/packages/conan/v1/users/authenticate` | +| Check for valid user credentials per conan CLI | `GET /projects/:id/packages/conan/v1/users/check_credentials` | +| Search for packages | `GET /projects/:id/packages/conan/v2/conans/search` | +| Authenticate user against conan CLI | `GET /projects/:id/packages/conan/v2/users/authenticate` | +| Check for valid user credentials per conan CLI | `GET /projects/:id/packages/conan/v2/users/check_credentials` | +| List container repositories within a project | `GET /projects/:id/registry/repositories` | +| List tags of a repository | `GET /projects/:id/registry/repositories/:repository_id/tags` | +| Get details about a repository tag | `GET /projects/:id/registry/repositories/:repository_id/tags/:tag_name` | +| Transitions a DAST site validation to a new state. | `POST /internal/dast/site_validations/:id/transition` | diff --git a/lib/tasks/ci/job_tokens_task.rb b/lib/tasks/ci/job_tokens_task.rb index 34c0fb07d9911b..7d0eafee13203f 100644 --- a/lib/tasks/ci/job_tokens_task.rb +++ b/lib/tasks/ci/job_tokens_task.rb @@ -3,6 +3,11 @@ module Tasks module Ci class JobTokensTask + ADMIN_PERMISSION = 'admin' + READ_LABEL = 'Read' + READ_AND_WRITE_LABEL = 'Read and write' + POLICY_PATTERN = /^(read|admin)_(.+)/ + def initialize @routes = API::API.endpoints.flat_map(&:routes) @doc_path = Rails.root.join('doc/ci/jobs/fine_grained_permissions.md') @@ -69,8 +74,14 @@ def compile_docs end def allowed_endpoints - routes_for_table = routes.select { |route| allowed_route?(route) } - table_for_routes(routes_for_table, user_docs: true) + routes_with_policies, routes_without_policies = routes + .select { |route| allowed_route?(route) } + .partition { |route| policies_for(route).present? } + + { + categorized: generate_categorized_routes_table(routes_with_policies), + unavailable: unavailable_table(routes_without_policies) + } end private @@ -89,81 +100,191 @@ def valid_policies @valid_policies ||= ::Ci::JobToken::Policies::POLICIES end - def table_for_routes(routes, include_policies: false, user_docs: false) - header = [] + def table_for_routes(routes, include_policies: false) + header = [] header << 'Policies' if include_policies - - if user_docs - header << 'Permissions' - header << 'Permission Names' - end - header += %w[Path Description] - table = [] + table = [] table << markdown_row(header) table << markdown_row(header.map { |item| '-' * item.length }) - formatted_routes = routes.map { |route| format_route(route, include_policies, user_docs) } + formatted_routes = routes.map do |route| + row = [] + row << policies_for(route).join(', ') if include_policies + row << "`#{route_path(route)}`" + row << route.description + markdown_row(row) + end + table += formatted_routes.uniq.sort table.join("\n") end - def format_route(route, include_policies, user_docs) - row = [] - row << policies_for(route).join(', ') if include_policies + def format_route(route, category = nil) + row = [ + route.description, + "`#{route_path(route)}`", + permission_names(route, category), + scope_for(route, category) + ] + markdown_row(row) + end + + def scope_for(route, category = nil) + policies = category ? filtered_policies_for_category(route, category) : policies_for(route) + return '' unless policies.present? + + permissions = policies.map do |policy| + permission = extract_permission(policy) + permission == ADMIN_PERMISSION ? READ_AND_WRITE_LABEL : READ_LABEL + end.uniq + + permissions.join(', ') + end + + def allowed_route?(route) + route.settings.dig(:authentication, :job_token_allowed) + end + + def skip_route?(route) + route.settings.dig(:authorization, :skip_job_token_policies) + end + + def policies_for(route) + Array(route.settings.dig(:authorization, :job_token_policies)) + end + + def route_path(route) + [route.request_method, route.origin.delete_prefix('/api/:version')].join(' ') + end + + def extract_permission(policy) + POLICY_PATTERN.match(policy)&.captures&.first + end + + def extract_category(policy) + POLICY_PATTERN.match(policy)&.captures&.last + end + + def extract_permission_categories + @permission_categories ||= allowed_routes_with_policies + .flat_map { |route| policies_for(route) } + .filter_map { |policy| extract_category(policy)&.humanize } + .uniq + .sort + end - if user_docs - row << resource_and_permissions_for(route) - row << permission_names(route) + def allowed_routes_with_policies + @allowed_routes_with_policies ||= routes.select do |route| + allowed_route?(route) && policies_for(route).present? end + end - row << [ - "`#{route_path(route)}`", - route.description - ] + def filtered_policies_for_category(route, target_category) + return policies_for(route) unless target_category - markdown_row(row) + policies_for(route).select do |policy| + category = extract_category(policy) + category&.humanize == target_category + end end - def markdown_row(row) - "| #{row.join(' | ')} |" + def group_routes_by_category + @grouped_routes ||= build_category_groups + end + + def build_category_groups + category_groups = Hash.new { |hash, key| hash[key] = [] } + + allowed_routes_with_policies.each do |route| + add_route_to_categories(route, category_groups) + end + + category_groups + end + + def add_route_to_categories(route, category_groups) + policies_for(route).each do |policy| + category = extract_category(policy)&.humanize + category_groups[category] |= [route] if category + end end - def resource_and_permissions_for(route) - policies = policies_for(route) - return 'None' unless policies.present? + def sort_routes_by_permission_level(routes, category) + routes.sort_by do |route| + permission_priority = has_admin_permission_for_category?(route, category) ? 1 : 0 + [permission_priority, route.description] + end + end + + def has_admin_permission_for_category?(route, category) + filtered_policies_for_category(route, category).any? do |policy| + extract_permission(policy) == ADMIN_PERMISSION + end + end - policies.map do |policy| - _, permission, category = policy.match(/^(read|admin)_(.+)/).to_a - next policy unless permission && category + def unavailable_table(routes) + header = ['Permission', 'API endpoint'] - permission = 'read_and_write' if permission == 'admin' - "#{category.humanize}: #{permission.humanize}" - end.join(', ') + build_table(header) do + routes + .sort_by { |route| route_path(route) } + .map { |route| format_unavailable_route(route) } + end end - def permission_names(route) - policies = policies_for(route) - return unless policies.present? + def format_unavailable_route(route) + row = [route.description, "`#{route_path(route)}`"] + markdown_row(row) + end + + def markdown_row(row) + "| #{row.join(' | ')} |" + end + + def permission_names(route, category = nil) + policies = category ? filtered_policies_for_category(route, category) : policies_for(route) + return '' unless policies.present? policies.map { |policy| "`#{policy.upcase}`" }.join(', ') end - def route_path(route) - [route.request_method, route.origin.delete_prefix('/api/:version')].join(' ') + def generate_categorized_routes_table(routes) + grouped_routes = group_routes_by_category + routes_set = routes.to_set + + extract_permission_categories.filter_map.with_index do |category, index| + category_routes = grouped_routes[category] & routes_set.to_a + next if category_routes.empty? + + section = build_category_section(category, category_routes) + section += "\n" unless index == extract_permission_categories.size - 1 + section + end.join("\n") end - def allowed_route?(route) - route.settings.dig(:authentication, :job_token_allowed) + def build_category_section(category, category_routes) + section_title = "#{category} endpoints" + "## #{section_title}\n\n#{generate_category_table(category_routes, category)}" end - def skip_route?(route) - route.settings.dig(:authorization, :skip_job_token_policies) + def generate_category_table(routes, category) + header = ['Permission', 'API endpoint', 'Permission name', 'Scope'] + + build_table(header) do + sort_routes_by_permission_level(routes, category) + .map { |route| format_route(route, category) } + .uniq + end end - def policies_for(route) - Array(route.settings.dig(:authorization, :job_token_policies)) + def build_table(header) + table = [] + table << markdown_row(header) + table << markdown_row(header.map { |item| '-' * item.length }) + table += yield + table.join("\n") end end end diff --git a/spec/tasks/ci/job_tokens_task_spec.rb b/spec/tasks/ci/job_tokens_task_spec.rb index 939459b1bed44c..b522416328505d 100644 --- a/spec/tasks/ci/job_tokens_task_spec.rb +++ b/spec/tasks/ci/job_tokens_task_spec.rb @@ -13,6 +13,7 @@ allowed_route_without_policies, allowed_route_with_invalid_policies, allowed_route_with_valid_policies, + allowed_route_with_admin_policy, allowed_route_with_skipped_policies, not_allowed_route ] @@ -163,7 +164,6 @@ it 'creates fine_grained_permissions.md', :aggregate_failures do FileUtils.rm_f(doc_path) expect { File.read(doc_path) }.to raise_error(Errno::ENOENT) - expect(task).to receive(:allowed_endpoints) compile_docs @@ -172,19 +172,35 @@ end describe '#allowed_endpoints' do - let(:table) do + let(:categorized_table) do <<~TABLE.chomp - | Permissions | Permission Names | Path | Description | - | ----------- | ---------------- | ---- | ----------- | - | None | | `GET path/to/allowed_route_with_skipped_policies` | route description | - | None | | `GET path/to/allowed_route_without_policies` | route description | - | Packages: Read | `READ_PACKAGES` | `GET path/to/allowed_route_with_valid_policies` | route description | - | invalid_policy | `INVALID_POLICY` | `GET path/to/allowed_route_with_invalid_policies` | route description | + ## Packages endpoints + + | Permission | API endpoint | Permission name | Scope | + | ---------- | ------------ | --------------- | ----- | + | route description | `GET path/to/allowed_route_with_valid_policies` | `READ_PACKAGES` | Read | + | admin policy route | `GET path/to/allowed_route_with_admin_policy` | `ADMIN_PACKAGES` | Read and write | + TABLE + end + + let(:unavailable_table) do + <<~TABLE.chomp + | Permission | API endpoint | + | ---------- | ------------ | + | route description | `GET path/to/allowed_route_with_skipped_policies` | + | route description | `GET path/to/allowed_route_without_policies` | TABLE end - it 'returns a sorted table for the docs that includes allowed routes only' do - expect(task.allowed_endpoints).to eq(table) + let(:expected_hash) do + { + categorized: categorized_table, + unavailable: unavailable_table + } + end + + it 'returns the expected tables in a hash' do + expect(task.allowed_endpoints).to eq(expected_hash) end end @@ -223,6 +239,18 @@ def allowed_route_with_valid_policies ) end + def allowed_route_with_admin_policy + instance_double(Grape::Router::Route, + settings: { + authentication: { job_token_allowed: true }, + authorization: { job_token_policies: :admin_packages } + }, + request_method: 'GET', + description: 'admin policy route', + origin: 'path/to/allowed_route_with_admin_policy' + ) + end + def allowed_route_with_skipped_policies instance_double(Grape::Router::Route, settings: { diff --git a/tooling/ci/job_tokens/docs/templates/fine_grained_permissions.md.erb b/tooling/ci/job_tokens/docs/templates/fine_grained_permissions.md.erb index b0224447d2afc8..0e31a702472716 100644 --- a/tooling/ci/job_tokens/docs/templates/fine_grained_permissions.md.erb +++ b/tooling/ci/job_tokens/docs/templates/fine_grained_permissions.md.erb @@ -63,6 +63,10 @@ access any allowed resources in the current project. The following endpoints are available for CI/CD job tokens. -`None` means fine-grained permissions cannot control access to this endpoint. +<%= allowed_endpoints[:categorized] %> -<%= allowed_endpoints %> +## Unavailable endpoints + +These API endpoints cannot be assigned as fine-grained permissions for job tokens. + +<%= allowed_endpoints[:unavailable] %> -- GitLab From 640b1dd0b924b5b93eb5a21b0a6355a5abd70bc6 Mon Sep 17 00:00:00 2001 From: abime Date: Thu, 4 Sep 2025 18:57:24 +0530 Subject: [PATCH 2/3] Organize job token permission table with category sections --- doc/ci/jobs/fine_grained_permissions.md | 3 +- lib/tasks/ci/job_tokens_task.rb | 52 +++++++++++-------------- spec/tasks/ci/job_tokens_task_spec.rb | 33 ++++++++++++++++ 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/doc/ci/jobs/fine_grained_permissions.md b/doc/ci/jobs/fine_grained_permissions.md index 691e12af4eaa1d..6f9fbfaa85d88a 100644 --- a/doc/ci/jobs/fine_grained_permissions.md +++ b/doc/ci/jobs/fine_grained_permissions.md @@ -71,7 +71,7 @@ The following endpoints are available for CI/CD job tokens. | List of merge requests associated with a deployment | `GET /projects/:id/deployments/:deployment_id/merge_requests` | `READ_DEPLOYMENTS` | Read | | List project deployments | `GET /projects/:id/deployments` | `READ_DEPLOYMENTS` | Read | | Approve or reject a blocked deployment | `POST /projects/:id/deployments/:deployment_id/approval` | `ADMIN_DEPLOYMENTS` | Read and write | -| Create a deployment | `POST /projects/:id/deployments` | `ADMIN_DEPLOYMENTS` | Read and write | +| Create a deployment | `POST /projects/:id/deployments` | `ADMIN_DEPLOYMENTS`, `ADMIN_ENVIRONMENTS` | Read and write | | Delete a specific deployment | `DELETE /projects/:id/deployments/:deployment_id` | `ADMIN_DEPLOYMENTS` | Read and write | | Update a deployment | `PUT /projects/:id/deployments/:deployment_id` | `ADMIN_DEPLOYMENTS` | Read and write | @@ -81,7 +81,6 @@ The following endpoints are available for CI/CD job tokens. | ---------- | ------------ | --------------- | ----- | | Get a specific environment | `GET /projects/:id/environments/:environment_id` | `READ_ENVIRONMENTS` | Read | | List environments | `GET /projects/:id/environments` | `READ_ENVIRONMENTS` | Read | -| Create a deployment | `POST /projects/:id/deployments` | `ADMIN_ENVIRONMENTS` | Read and write | | Create a new environment | `POST /projects/:id/environments` | `ADMIN_ENVIRONMENTS` | Read and write | | Delete an environment | `DELETE /projects/:id/environments/:environment_id` | `ADMIN_ENVIRONMENTS` | Read and write | | Delete multiple stopped review apps | `DELETE /projects/:id/environments/review_apps` | `ADMIN_ENVIRONMENTS` | Read and write | diff --git a/lib/tasks/ci/job_tokens_task.rb b/lib/tasks/ci/job_tokens_task.rb index 7d0eafee13203f..7ea562dc2091e9 100644 --- a/lib/tasks/ci/job_tokens_task.rb +++ b/lib/tasks/ci/job_tokens_task.rb @@ -121,19 +121,19 @@ def table_for_routes(routes, include_policies: false) table.join("\n") end - def format_route(route, category = nil) + def format_route(route) row = [ route.description, "`#{route_path(route)}`", - permission_names(route, category), - scope_for(route, category) + permission_names(route), + scope_for(route) ] markdown_row(row) end - def scope_for(route, category = nil) - policies = category ? filtered_policies_for_category(route, category) : policies_for(route) - return '' unless policies.present? + def scope_for(route) + policies = policies_for(route) + return unless policies.present? permissions = policies.map do |policy| permission = extract_permission(policy) @@ -167,6 +167,11 @@ def extract_category(policy) POLICY_PATTERN.match(policy)&.captures&.last end + def primary_category_for_route(route) + policies = policies_for(route) + policies.first&.then { |policy| extract_category(policy)&.humanize } + end + def extract_permission_categories @permission_categories ||= allowed_routes_with_policies .flat_map { |route| policies_for(route) } @@ -181,15 +186,6 @@ def allowed_routes_with_policies end end - def filtered_policies_for_category(route, target_category) - return policies_for(route) unless target_category - - policies_for(route).select do |policy| - category = extract_category(policy) - category&.humanize == target_category - end - end - def group_routes_by_category @grouped_routes ||= build_category_groups end @@ -205,21 +201,19 @@ def build_category_groups end def add_route_to_categories(route, category_groups) - policies_for(route).each do |policy| - category = extract_category(policy)&.humanize - category_groups[category] |= [route] if category - end + primary_cat = primary_category_for_route(route) + category_groups[primary_cat] |= [route] if primary_cat end - def sort_routes_by_permission_level(routes, category) + def sort_routes_by_permission_level(routes) routes.sort_by do |route| - permission_priority = has_admin_permission_for_category?(route, category) ? 1 : 0 + permission_priority = has_admin_permission?(route) ? 1 : 0 [permission_priority, route.description] end end - def has_admin_permission_for_category?(route, category) - filtered_policies_for_category(route, category).any? do |policy| + def has_admin_permission?(route) + policies_for(route).any? do |policy| extract_permission(policy) == ADMIN_PERMISSION end end @@ -243,8 +237,8 @@ def markdown_row(row) "| #{row.join(' | ')} |" end - def permission_names(route, category = nil) - policies = category ? filtered_policies_for_category(route, category) : policies_for(route) + def permission_names(route) + policies = policies_for(route) return '' unless policies.present? policies.map { |policy| "`#{policy.upcase}`" }.join(', ') @@ -266,15 +260,15 @@ def generate_categorized_routes_table(routes) def build_category_section(category, category_routes) section_title = "#{category} endpoints" - "## #{section_title}\n\n#{generate_category_table(category_routes, category)}" + "## #{section_title}\n\n#{generate_category_table(category_routes)}" end - def generate_category_table(routes, category) + def generate_category_table(routes) header = ['Permission', 'API endpoint', 'Permission name', 'Scope'] build_table(header) do - sort_routes_by_permission_level(routes, category) - .map { |route| format_route(route, category) } + sort_routes_by_permission_level(routes) + .map { |route| format_route(route) } .uniq end end diff --git a/spec/tasks/ci/job_tokens_task_spec.rb b/spec/tasks/ci/job_tokens_task_spec.rb index b522416328505d..01974a22f91db6 100644 --- a/spec/tasks/ci/job_tokens_task_spec.rb +++ b/spec/tasks/ci/job_tokens_task_spec.rb @@ -15,6 +15,8 @@ allowed_route_with_valid_policies, allowed_route_with_admin_policy, allowed_route_with_skipped_policies, + allowed_route_with_multi_policies, + allowed_route_with_mixed_policy_types, not_allowed_route ] end @@ -174,6 +176,13 @@ describe '#allowed_endpoints' do let(:categorized_table) do <<~TABLE.chomp + ## Deployments endpoints + + | Permission | API endpoint | Permission name | Scope | + | ---------- | ------------ | --------------- | ----- | + | mixed permission route | `PUT /projects/:id/deployments/:id` | `ADMIN_DEPLOYMENTS`, `READ_PACKAGES` | Read and write, Read | + | multi permission route | `POST /projects/:id/deployments` | `ADMIN_DEPLOYMENTS`, `ADMIN_ENVIRONMENTS` | Read and write | + ## Packages endpoints | Permission | API endpoint | Permission name | Scope | @@ -263,6 +272,30 @@ def allowed_route_with_skipped_policies ) end + def allowed_route_with_multi_policies + instance_double(Grape::Router::Route, + settings: { + authentication: { job_token_allowed: true }, + authorization: { job_token_policies: [:admin_deployments, :admin_environments] } + }, + request_method: 'POST', + description: 'multi permission route', + origin: '/projects/:id/deployments' + ) + end + + def allowed_route_with_mixed_policy_types + instance_double(Grape::Router::Route, + settings: { + authentication: { job_token_allowed: true }, + authorization: { job_token_policies: [:admin_deployments, :read_packages] } + }, + request_method: 'PUT', + description: 'mixed permission route', + origin: '/projects/:id/deployments/:id' + ) + end + def not_allowed_route instance_double(Grape::Router::Route, settings: { -- GitLab From 6ca0ce30150e398a7805b3eb14d0da627641763a Mon Sep 17 00:00:00 2001 From: abime Date: Fri, 5 Sep 2025 11:20:18 +0530 Subject: [PATCH 3/3] Debug endpoints --- doc/ci/jobs/fine_grained_permissions.md | 38 ++++++++-------- lib/tasks/ci/job_tokens_task.rb | 59 +++++++++++++++++++++---- 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/doc/ci/jobs/fine_grained_permissions.md b/doc/ci/jobs/fine_grained_permissions.md index 6f9fbfaa85d88a..8ce79d7855820b 100644 --- a/doc/ci/jobs/fine_grained_permissions.md +++ b/doc/ci/jobs/fine_grained_permissions.md @@ -117,12 +117,12 @@ The following endpoints are available for CI/CD job tokens. | Download module file | `GET /projects/:id/packages/go/*module_name/@v/:module_version.mod` | `READ_PACKAGES` | Read | | Download module source | `GET /projects/:id/packages/go/*module_name/@v/:module_version.zip` | `READ_PACKAGES` | Read | | Download package file | `GET /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | `READ_PACKAGES` | Read | -| Download package files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | `READ_PACKAGES` | Read | | Download package files | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `READ_PACKAGES` | Read | | Download package files | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `READ_PACKAGES` | Read | +| Download package files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | `READ_PACKAGES` | Read | +| Download recipe files | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `READ_PACKAGES` | Read | | Download recipe files | `GET /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `READ_PACKAGES` | Read | | Download recipe files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | `READ_PACKAGES` | Read | -| Download recipe files | `GET /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `READ_PACKAGES` | Read | | Download the NPM tarball | `GET /projects/:id/packages/npm/*package_name/-/*file_name` | `READ_PACKAGES` | Read | | Download the maven package file at a group level | `GET /groups/:id/-/packages/maven/*path/:file_name` | `READ_PACKAGES` | Read | | Download the maven package file at a project level | `GET /projects/:id/packages/maven/*path/:file_name` | `READ_PACKAGES` | Read | @@ -130,12 +130,12 @@ The following endpoints are available for CI/CD job tokens. | Get a list of project packages | `GET /projects/:id/packages` | `READ_PACKAGES` | Read | | Get a single project package | `GET /projects/:id/packages/:package_id` | `READ_PACKAGES` | Read | | Get all tags for a given an NPM package | `GET /groups/:id/-/packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | -| Get all tags for a given an NPM package | `GET /projects/:id/packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | | Get all tags for a given an NPM package | `GET /packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | -| Get package references metadata | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | +| Get all tags for a given an NPM package | `GET /projects/:id/packages/npm/-/package/*package_name/dist-tags` | `READ_PACKAGES` | Read | | Get package references metadata | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | | Get package references metadata | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | | Get package references metadata | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/search` | `READ_PACKAGES` | Read | +| Get package references metadata | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/search` | `READ_PACKAGES` | Read | | Get the latest package revision | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/latest` | `READ_PACKAGES` | Read | | Get the latest recipe revision | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/latest` | `READ_PACKAGES` | Read | | Get the list of package revisions | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions` | `READ_PACKAGES` | Read | @@ -144,15 +144,15 @@ The following endpoints are available for CI/CD job tokens. | List package files | `GET /projects/:id/packages/:package_id/package_files` | `READ_PACKAGES` | Read | | List package files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files` | `READ_PACKAGES` | Read | | List recipe files | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files` | `READ_PACKAGES` | Read | -| NPM registry bulk advisory endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | | NPM registry bulk advisory endpoint | `POST /groups/:id/-/packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | | NPM registry bulk advisory endpoint | `POST /packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | +| NPM registry bulk advisory endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/advisories/bulk` | `READ_PACKAGES` | Read | | NPM registry metadata endpoint | `GET /projects/:id/packages/npm/*package_name` | `READ_PACKAGES` | Read | -| NPM registry quick audit endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | -| NPM registry quick audit endpoint | `POST /packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | | NPM registry quick audit endpoint | `POST /groups/:id/-/packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | -| Package Digest | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | `READ_PACKAGES` | Read | +| NPM registry quick audit endpoint | `POST /packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | +| NPM registry quick audit endpoint | `POST /projects/:id/packages/npm/-/npm/v1/security/audits/quick` | `READ_PACKAGES` | Read | | Package Digest | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | `READ_PACKAGES` | Read | +| Package Digest | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/digest` | `READ_PACKAGES` | Read | | Package Download Urls | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | `READ_PACKAGES` | Read | | Package Download Urls | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls` | `READ_PACKAGES` | Read | | Package Snapshot | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference` | `READ_PACKAGES` | Read | @@ -161,12 +161,12 @@ The following endpoints are available for CI/CD job tokens. | Package Upload Urls | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls` | `READ_PACKAGES` | Read | | Recipe Digest | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | `READ_PACKAGES` | Read | | Recipe Digest | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/digest` | `READ_PACKAGES` | Read | -| Recipe Download Urls | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | `READ_PACKAGES` | Read | | Recipe Download Urls | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | `READ_PACKAGES` | Read | -| Recipe Snapshot | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `READ_PACKAGES` | Read | +| Recipe Download Urls | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/download_urls` | `READ_PACKAGES` | Read | | Recipe Snapshot | `GET /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `READ_PACKAGES` | Read | -| Recipe Upload Urls | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | `READ_PACKAGES` | Read | +| Recipe Snapshot | `GET /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `READ_PACKAGES` | Read | | Recipe Upload Urls | `POST /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | `READ_PACKAGES` | Read | +| Recipe Upload Urls | `POST /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel/upload_urls` | `READ_PACKAGES` | Read | | The PyPi Simple Project Index Endpoint | `GET /projects/:id/packages/pypi/simple` | `READ_PACKAGES` | Read | | The PyPi Simple Project Package Endpoint | `GET /projects/:id/packages/pypi/simple/*package_name` | `READ_PACKAGES` | Read | | The PyPi package download endpoint | `GET /projects/:id/packages/pypi/files/:sha256/*file_identifier` | `READ_PACKAGES` | Read | @@ -174,34 +174,34 @@ The following endpoints are available for CI/CD job tokens. | Authorize the PyPi package upload from workhorse | `POST /projects/:id/packages/pypi/authorize` | `ADMIN_PACKAGES` | Read and write | | Composer packages endpoint for registering packages | `POST /projects/:id/packages/composer` | `ADMIN_PACKAGES` | Read and write | | Create or Update the given tag for the given NPM package and version | `PUT /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | -| Create or Update the given tag for the given NPM package and version | `PUT /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | | Create or Update the given tag for the given NPM package and version | `PUT /packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Create or Update the given tag for the given NPM package and version | `PUT /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | | Create or deprecate NPM package | `PUT /projects/:id/packages/npm/:package_name` | `ADMIN_PACKAGES` | Read and write | -| Delete Package | `DELETE /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `ADMIN_PACKAGES` | Read and write | | Delete Package | `DELETE /packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `ADMIN_PACKAGES` | Read and write | +| Delete Package | `DELETE /projects/:id/packages/conan/v1/conans/:package_name/:package_version/:package_username/:package_channel` | `ADMIN_PACKAGES` | Read and write | | Delete a package file | `DELETE /projects/:id/packages/:package_id/package_files/:package_file_id` | `ADMIN_PACKAGES` | Read and write | | Delete a project package | `DELETE /projects/:id/packages/:package_id` | `ADMIN_PACKAGES` | Read and write | | Delete package revision | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision` | `ADMIN_PACKAGES` | Read and write | | Delete recipe revision | `DELETE /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision` | `ADMIN_PACKAGES` | Read and write | -| Deletes the given tag | `DELETE /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | | Deletes the given tag | `DELETE /groups/:id/-/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | | Deletes the given tag | `DELETE /packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | +| Deletes the given tag | `DELETE /projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag` | `ADMIN_PACKAGES` | Read and write | | The PyPi Package upload endpoint | `POST /projects/:id/packages/pypi` | `ADMIN_PACKAGES` | Read and write | | Upload package file | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `ADMIN_PACKAGES` | Read and write | | Upload package files | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `ADMIN_PACKAGES` | Read and write | | Upload package files | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | `ADMIN_PACKAGES` | Read and write | -| Upload package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name` | `ADMIN_PACKAGES` | Read and write | +| Upload recipe package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `ADMIN_PACKAGES` | Read and write | | Upload recipe package files | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `ADMIN_PACKAGES` | Read and write | | Upload recipe package files | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name` | `ADMIN_PACKAGES` | Read and write | -| Upload recipe package files | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name` | `ADMIN_PACKAGES` | Read and write | | Upload the maven package file | `PUT /projects/:id/packages/maven/*path/:file_name` | `ADMIN_PACKAGES` | Read and write | | Workhorse authorize generic package file | `PUT /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name/authorize` | `ADMIN_PACKAGES` | Read and write | -| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | -| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | | Workhorse authorize the conan package file | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan package file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | | Workhorse authorize the conan recipe file | `PUT /packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | -| Workhorse authorize the conan recipe file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | | Workhorse authorize the conan recipe file | `PUT /projects/:id/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | +| Workhorse authorize the conan recipe file | `PUT /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | | Workhorse authorize the maven package file upload | `PUT /projects/:id/packages/maven/*path/:file_name/authorize` | `ADMIN_PACKAGES` | Read and write | ## Pipelines endpoints diff --git a/lib/tasks/ci/job_tokens_task.rb b/lib/tasks/ci/job_tokens_task.rb index 7ea562dc2091e9..dafba9e901cf27 100644 --- a/lib/tasks/ci/job_tokens_task.rb +++ b/lib/tasks/ci/job_tokens_task.rb @@ -49,18 +49,44 @@ def check_policies_correctness end def check_docs + debug_routes_info doc = File.read(doc_path) - template = ERB.new(File.read(template_path)) - if doc == template.result(binding) + generated = template.result(binding) + + if doc == generated puts 'CI/CD job token allowed endpoints documentation is up to date.' else - puts '##########' - puts '#' - puts '# CI/CD job token allowed endpoints documentation is outdated! Please update it by running ' \ - '`bundle exec rake ci:job_tokens:compile_docs`.' - puts '#' - puts '##########' + puts "Documentation mismatch found:" + puts "Current doc length: #{doc.length}" + puts "Generated doc length: #{generated.length}" + + # Show all differences + doc_lines = doc.lines + generated_lines = generated.lines + diff_count = 0 + + puts "\n=== ALL DIFFERENCES ===" + [doc_lines.length, generated_lines.length].max.times do |i| + current_line = doc_lines[i] + generated_line = generated_lines[i] + + next if current_line == generated_line + + diff_count += 1 + puts "Diff ##{diff_count} at line #{i + 1}:" + puts "Current: #{current_line&.inspect || 'nil (missing line)'}" + puts "Generated: #{generated_line&.inspect || 'nil (missing line)'}" + puts "" + end + + puts "=== TOTAL DIFFERENCES: #{diff_count} ===" + + puts "\n=== CURRENT DOCUMENT ===" + puts doc + puts "\n=== GENERATED DOCUMENT ===" + puts generated + puts "\n=== END DOCUMENTS ===" abort end @@ -92,6 +118,21 @@ def find_allowed_routes_without_policies routes.select { |route| allowed_route?(route) && policies_for(route).empty? && !skip_route?(route) } end + def debug_routes_info + puts "=== ROUTE DEBUG INFO ===" + puts "Total routes: #{routes.count}" + puts "Job token allowed routes: #{routes.count { |r| allowed_route?(r) }}" + puts "Routes with policies: #{routes.count { |r| allowed_route?(r) && policies_for(r).present? }}" + count = routes.count { |r| allowed_route?(r) && policies_for(r).empty? && !skip_route?(r) } + puts "Routes without policies: #{count}" + + # Show a sample of routes for comparison + sample_routes = routes.select { |r| allowed_route?(r) && policies_for(r).present? }.first(3) + puts "Sample routes with policies:" + sample_routes.each { |r| puts " #{route_path(r)} - #{policies_for(r).join(', ')}" } + puts "========================" + end + def find_routes_with_invalid_policies routes.select { |route| (policies_for(route) - valid_policies).present? } end @@ -208,7 +249,7 @@ def add_route_to_categories(route, category_groups) def sort_routes_by_permission_level(routes) routes.sort_by do |route| permission_priority = has_admin_permission?(route) ? 1 : 0 - [permission_priority, route.description] + [permission_priority, route.description, route_path(route)] end end -- GitLab