Commit 5be27d66 authored by Timo Furrer's avatar Timo Furrer

Implement GET API for GATs and PATs

This change set implements the `GET` API for a single Group and Project
Access Token.

Changelog: added
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82714
parent 91a42f8a
......@@ -44,6 +44,41 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
]
```
## Get a group access token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82714) in GitLab 14.10.
Get a [group access token](../user/group/settings/group_access_tokens.md) by ID.
```plaintext
GET groups/:id/access_tokens/:token_id
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer or string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `token_id` | integer or string | yes | ID of the group access token |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/<group_id>/access_tokens/<token_id>"
```
```json
{
"user_id" : 141,
"scopes" : [
"api"
],
"name" : "token",
"expires_at" : "2021-01-31",
"id" : 42,
"active" : true,
"created_at" : "2021-01-20T22:11:48.151Z",
"revoked" : false,
"access_level": 40
}
```
## Create a group access token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77236) in GitLab 14.7.
......
......@@ -44,6 +44,42 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
]
```
## Get a project access token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82714) in GitLab 14.10.
Get a [project access token](../user/project/settings/project_access_tokens.md) by ID.
```plaintext
GET projects/:id/access_tokens/:token_id
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer or string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
| `token_id` | integer or string | yes | ID of the project access token |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/access_tokens/<token_id>"
```
```json
{
"user_id" : 141,
"scopes" : [
"api"
],
"name" : "token",
"expires_at" : "2021-01-31",
"id" : 42,
"active" : true,
"created_at" : "2021-01-20T22:11:48.151Z",
"revoked" : false,
"access_level": 40,
"last_used_at": "2022-03-15T11:05:42.437Z"
}
```
## Create a project access token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55408) in GitLab 13.10.
......
......@@ -27,6 +27,28 @@ module API
present paginate(tokens), with: Entities::ResourceAccessToken, resource: resource
end
desc 'Get an access token for the specified resource by ID' do
detail 'This feature was introduced in GitLab 14.10.'
end
params do
requires :id, type: String, desc: "The #{source_type} ID"
requires :token_id, type: String, desc: "The ID of the token"
end
get ":id/access_tokens/:token_id" do
resource = find_source(source_type, params[:id])
next unauthorized! unless current_user.can?(:read_resource_access_tokens, resource)
token = find_token(resource, params[:token_id])
if token.nil?
next not_found!("Could not find #{source_type} access token with token_id: #{params[:token_id]}")
end
resource.members.load
present token, with: Entities::ResourceAccessToken, resource: resource
end
desc 'Revoke a resource access token' do
detail 'This feature was introduced in GitLab 13.9.'
end
......
{
"type": "object",
"required": [
"id",
"name",
"user_id",
"active",
"created_at",
"expires_at",
"revoked",
"access_level",
"scopes",
"last_used_at"
],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"user_id": { "type": "integer" },
"active": { "type": "boolean" },
"created_at": { "type": "string", "format": "date-time" },
"expires_at": { "type": ["string", "null"], "format": "date" },
"revoked": { "type": "boolean" },
"access_level": { "type": "integer" },
"scopes": {
"type": "array",
"items": { "type": "string" }
},
"last_used_at": { "type": ["string", "null"], "format": "date-time" }
},
"additionalProperties": false
}
......@@ -131,6 +131,103 @@ RSpec.describe API::ResourceAccessTokens do
end
end
context "GET #{source_type}s/:id/access_tokens/:token_id" do
subject(:get_token) { get api("/#{source_type}s/#{resource_id}/access_tokens/#{token_id}", user) }
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:token) { create(:personal_access_token, user: project_bot) }
let_it_be(:resource_id) { resource.id }
let_it_be(:token_id) { token.id }
before do
if source_type == 'project'
resource.add_maintainer(project_bot)
else
resource.add_owner(project_bot)
end
end
context "when the user has valid permissions" do
it "gets the #{source_type} access token from the #{source_type}" do
get_token
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/resource_access_token')
expect(json_response["name"]).to eq(token.name)
expect(json_response["scopes"]).to eq(token.scopes)
if source_type == 'project'
expect(json_response["access_level"]).to eq(resource.team.max_member_access(token.user.id))
else
expect(json_response["access_level"]).to eq(resource.max_member_access_for_user(token.user))
end
expect(json_response["expires_at"]).to eq(token.expires_at.to_date.iso8601)
end
context "when using #{source_type} access token to GET other #{source_type} access token" do
let_it_be(:other_project_bot) { create(:user, :project_bot) }
let_it_be(:other_token) { create(:personal_access_token, user: other_project_bot) }
let_it_be(:token_id) { other_token.id }
before do
resource.add_maintainer(other_project_bot)
end
it "gets the #{source_type} access token from the #{source_type}" do
get_token
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/resource_access_token')
expect(json_response["name"]).to eq(other_token.name)
expect(json_response["scopes"]).to eq(other_token.scopes)
if source_type == 'project'
expect(json_response["access_level"]).to eq(resource.team.max_member_access(other_token.user.id))
else
expect(json_response["access_level"]).to eq(resource.max_member_access_for_user(other_token.user))
end
expect(json_response["expires_at"]).to eq(other_token.expires_at.to_date.iso8601)
end
end
context "when attempting to get a non-existent #{source_type} access token" do
let_it_be(:token_id) { non_existing_record_id }
it "does not get the token, and returns 404" do
get_token
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to include("Could not find #{source_type} access token with token_id: #{token_id}")
end
end
context "when attempting to get a token that does not belong to the specified #{source_type}" do
let_it_be(:resource_id) { other_resource.id }
it "does not get the token, and returns 404" do
get_token
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to include("Could not find #{source_type} access token with token_id: #{token_id}")
end
end
end
context "when the user does not have valid permissions" do
let_it_be(:user) { user_non_priviledged }
it "returns 401" do
get_token
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
context "DELETE #{source_type}s/:id/access_tokens/:token_id", :sidekiq_inline do
subject(:delete_token) { delete api("/#{source_type}s/#{resource_id}/access_tokens/#{token_id}", user) }
......
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