Commit b18ad174 authored by Shinya Maeda's avatar Shinya Maeda

Merge branch '21811-project-list-deploy-tokens' into 'master'

API endpoint to list deploy tokens in a project

Closes #207380

See merge request gitlab-org/gitlab!25186
parents 212710c8 524506e9
...@@ -312,6 +312,7 @@ class ProjectPolicy < BasePolicy ...@@ -312,6 +312,7 @@ class ProjectPolicy < BasePolicy
enable :destroy_artifacts enable :destroy_artifacts
enable :daily_statistics enable :daily_statistics
enable :admin_operations enable :admin_operations
enable :read_deploy_token
end end
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
......
---
title: Add endpoint for listing all deploy tokens for a project
merge_request: 25186
author:
type: added
...@@ -2,15 +2,14 @@ ...@@ -2,15 +2,14 @@
## List all deploy tokens ## List all deploy tokens
Get a list of all deploy tokens across all projects of the GitLab instance. Get a list of all deploy tokens across the GitLab instance. This endpoint requires admin access.
>**Note:** ```plaintext
> This endpoint requires admin access.
```
GET /deploy_tokens GET /deploy_tokens
``` ```
Example request:
```shell ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/deploy_tokens" curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/deploy_tokens"
``` ```
...@@ -24,7 +23,47 @@ Example response: ...@@ -24,7 +23,47 @@ Example response:
"name": "MyToken", "name": "MyToken",
"username": "gitlab+deploy-token-1", "username": "gitlab+deploy-token-1",
"expires_at": "2020-02-14T00:00:00.000Z", "expires_at": "2020-02-14T00:00:00.000Z",
"token": "jMRvtPNxrn3crTAGukpZ", "scopes": [
"read_repository",
"read_registry"
]
}
]
```
## Project deploy tokens
Project deploy token API endpoints require project maintainer access or higher.
### List project deploy tokens
Get a list of a project's deploy tokens.
```plaintext
GET /projects/:id/deploy_tokens
```
Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deploy_tokens"
```
Example response:
```json
[
{
"id": 1,
"name": "MyToken",
"username": "gitlab+deploy-token-1",
"expires_at": "2020-02-14T00:00:00.000Z",
"scopes": [ "scopes": [
"read_repository", "read_repository",
"read_registry" "read_registry"
......
...@@ -4,8 +4,6 @@ module API ...@@ -4,8 +4,6 @@ module API
class DeployTokens < Grape::API class DeployTokens < Grape::API
include PaginationParams include PaginationParams
before { authenticated_as_admin! }
desc 'Return all deploy tokens' do desc 'Return all deploy tokens' do
detail 'This feature was introduced in GitLab 12.9.' detail 'This feature was introduced in GitLab 12.9.'
success Entities::DeployToken success Entities::DeployToken
...@@ -14,7 +12,27 @@ module API ...@@ -14,7 +12,27 @@ module API
use :pagination use :pagination
end end
get 'deploy_tokens' do get 'deploy_tokens' do
authenticated_as_admin!
present paginate(DeployToken.all), with: Entities::DeployToken present paginate(DeployToken.all), with: Entities::DeployToken
end end
params do
requires :id, type: Integer, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
use :pagination
end
desc 'List deploy tokens for a project' do
detail 'This feature was introduced in GitLab 12.9'
success Entities::DeployToken
end
get ':id/deploy_tokens' do
authorize!(:read_deploy_token, user_project)
present paginate(user_project.deploy_tokens), with: Entities::DeployToken
end
end
end end
end end
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
module API module API
module Entities module Entities
class DeployToken < Grape::Entity class DeployToken < Grape::Entity
expose :id, :name, :username, :expires_at, :token, :scopes # exposing :token is a security risk and should be avoided
expose :id, :name, :username, :expires_at, :scopes
end end
end end
end end
{
"type": "object",
"required": [
"id",
"name",
"username",
"expires_at",
"scopes"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"username": {
"type": "string"
},
"expires_at": {
"type": "date"
},
"scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
\ No newline at end of file
{
"type": "array",
"items": {
"$ref": "deploy_token.json"
}
}
\ No newline at end of file
...@@ -52,7 +52,7 @@ describe ProjectPolicy do ...@@ -52,7 +52,7 @@ describe ProjectPolicy do
admin_snippet admin_project_member admin_note admin_wiki admin_project admin_snippet admin_project_member admin_note admin_wiki admin_project
admin_commit_status admin_build admin_container_image admin_commit_status admin_build admin_container_image
admin_pipeline admin_environment admin_deployment destroy_release add_cluster admin_pipeline admin_environment admin_deployment destroy_release add_cluster
daily_statistics daily_statistics read_deploy_token
] ]
end end
......
...@@ -3,43 +3,84 @@ ...@@ -3,43 +3,84 @@
require 'spec_helper' require 'spec_helper'
describe API::DeployTokens do describe API::DeployTokens do
let(:user) { create(:user) }
let(:creator) { create(:user) } let(:creator) { create(:user) }
let(:project) { create(:project, creator_id: creator.id) } let(:project) { create(:project, creator_id: creator.id) }
let!(:deploy_token) { create(:deploy_token, projects: [project]) } let!(:deploy_token) { create(:deploy_token, projects: [project]) }
describe 'GET /deploy_tokens' do describe 'GET /deploy_tokens' do
subject { get api('/deploy_tokens', user) } subject do
get api('/deploy_tokens', user)
response
end
context 'when unauthenticated' do context 'when unauthenticated' do
let(:user) { nil } let(:user) { nil }
it 'rejects the response as unauthorized' do it { is_expected.to have_gitlab_http_status(:unauthorized) }
subject
expect(response).to have_gitlab_http_status(:unauthorized)
end
end end
context 'when authenticated as non-admin user' do context 'when authenticated as non-admin user' do
let(:user) { creator } let(:user) { creator }
it 'rejects the response as forbidden' do it { is_expected.to have_gitlab_http_status(:forbidden) }
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end end
context 'when authenticated as admin' do context 'when authenticated as admin' do
let(:user) { create(:admin) } let(:user) { create(:admin) }
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns all deploy tokens' do it 'returns all deploy tokens' do
subject subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(response).to match_response_schema('public_api/v4/deploy_tokens')
expect(json_response.first['id']).to eq(deploy_token.id) end
end
end
describe 'GET /projects/:id/deploy_tokens' do
subject do
get api("/projects/#{project.id}/deploy_tokens", user)
response
end
context 'when unauthenticated' do
let(:user) { nil }
it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'when authenticated as non-admin user' do
before do
project.add_developer(user)
end
it { is_expected.to have_gitlab_http_status(:forbidden) }
end
context 'when authenticated as maintainer' do
let!(:other_deploy_token) { create(:deploy_token) }
before do
project.add_maintainer(user)
end
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns all deploy tokens for the project' do
subject
expect(response).to include_pagination_headers
expect(response).to match_response_schema('public_api/v4/deploy_tokens')
end
it 'does not return deploy tokens for other projects' do
subject
token_ids = json_response.map { |token| token['id'] }
expect(token_ids).not_to include(other_deploy_token.id)
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment