Commit 0765ffdc authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

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

API endpoint for creating project deploy tokens

See merge request gitlab-org/gitlab!25270
parents 92e160e6 6a7f693a
...@@ -313,6 +313,7 @@ class ProjectPolicy < BasePolicy ...@@ -313,6 +313,7 @@ class ProjectPolicy < BasePolicy
enable :daily_statistics enable :daily_statistics
enable :admin_operations enable :admin_operations
enable :read_deploy_token enable :read_deploy_token
enable :create_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 api endpoint to create deploy tokens
merge_request: 25270
author:
type: added
...@@ -72,6 +72,43 @@ Example response: ...@@ -72,6 +72,43 @@ Example response:
] ]
``` ```
### Create a project deploy token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21811) in GitLab 12.9.
Creates a new deploy token for a project.
```
POST /projects/:id/deploy_tokens
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | New deploy token's name |
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository` or `read_registry`. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "My deploy token", "expires_at": "2021-01-01", "username": "custom-user", "scopes": ["read_repository"]}' "https://gitlab.example.com/api/v4/projects/5/deploy_tokens/"
```
Example response:
```json
{
"id": 1,
"name": "My deploy token",
"username": "custom-user",
"expires_at": "2021-01-01T00:00:00.000Z",
"token": "jMRvtPNxrn3crTAGukpZ",
"scopes": [
"read_repository"
]
}
```
## Group deploy tokens ## Group deploy tokens
These endpoints require group maintainer access or higher. These endpoints require group maintainer access or higher.
......
...@@ -4,6 +4,17 @@ module API ...@@ -4,6 +4,17 @@ module API
class DeployTokens < Grape::API class DeployTokens < Grape::API
include PaginationParams include PaginationParams
helpers do
def scope_params
scopes = params.delete(:scopes)
result_hash = {}
result_hash[:read_registry] = scopes.include?('read_registry')
result_hash[:read_repository] = scopes.include?('read_repository')
result_hash
end
end
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
...@@ -33,6 +44,27 @@ module API ...@@ -33,6 +44,27 @@ module API
present paginate(user_project.deploy_tokens), with: Entities::DeployToken present paginate(user_project.deploy_tokens), with: Entities::DeployToken
end end
params do
requires :name, type: String, desc: "New deploy token's name"
requires :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
requires :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository" or "read_registry".'
end
desc 'Create a project deploy token' do
detail 'This feature was introduced in GitLab 12.9'
success Entities::DeployTokenWithToken
end
post ':id/deploy_tokens' do
authorize!(:create_deploy_token, user_project)
deploy_token = ::Projects::DeployTokens::CreateService.new(
user_project, current_user, scope_params.merge(declared(params, include_missing: false, include_parent_namespaces: false))
).execute
present deploy_token, with: Entities::DeployTokenWithToken
end
end end
params do params do
......
# frozen_string_literal: true
module API
module Entities
class DeployTokenWithToken < Entities::DeployToken
expose :token
end
end
end
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
"items": { "items": {
"type": "string" "type": "string"
} }
}
}, },
"additionalProperties": false "token": {
"type": "string"
}
}
} }
\ 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 read_deploy_token daily_statistics read_deploy_token create_deploy_token
] ]
end end
......
...@@ -133,4 +133,57 @@ describe API::DeployTokens do ...@@ -133,4 +133,57 @@ describe API::DeployTokens do
end end
end end
end end
describe 'POST /projects/:id/deploy_tokens' do
let(:params) do
{
name: 'Foo',
expires_at: 1.year.from_now,
scopes: [
'read_repository'
],
username: 'Bar'
}
end
subject do
post api("/projects/#{project.id}/deploy_tokens", user), params: params
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
before do
project.add_maintainer(user)
end
it 'creates the deploy token' do
expect { subject }.to change { DeployToken.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
end
context 'with an invalid scope' do
before do
params[:scopes] = %w[read_repository all_access]
end
it { is_expected.to have_gitlab_http_status(:bad_request) }
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