Skip to content

Commit

Permalink
feat(objects): support Create and Revoke personal access token API
Browse files Browse the repository at this point in the history
  • Loading branch information
nejch authored and JohnVillalovos committed Sep 13, 2021
1 parent a5d8b7f commit e19314d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 28 deletions.
32 changes: 29 additions & 3 deletions docs/gl_objects/personal_access_tokens.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Personal Access Tokens
######################

Get a list of personal access tokens

References
----------

Expand All @@ -12,8 +10,14 @@ References
+ :class:`gitlab.v4.objects.PersonalAccessToken`
+ :class:`gitlab.v4.objects.PersonalAcessTokenManager`
+ :attr:`gitlab.Gitlab.personal_access_tokens`
+ :class:`gitlab.v4.objects.UserPersonalAccessToken`
+ :class:`gitlab.v4.objects.UserPersonalAcessTokenManager`
+ :attr:`gitlab.Gitlab.User.personal_access_tokens`

* GitLab API:

* GitLab API: https://docs.gitlab.com/ee/api/personal_access_tokens.html
+ https://docs.gitlab.com/ee/api/personal_access_tokens.html
+ https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token

Examples
--------
Expand All @@ -26,3 +30,25 @@ List personal access tokens::
List personal access tokens from other user_id (admin only)::

access_tokens = gl.personal_access_tokens.list(user_id=25)

Revoke a personal access token fetched via list::

access_token = access_tokens[0]
access_token.delete()

Revoke a personal access token by id::

gl.personal_access_tokens.delete(123)

Create a personal access token for a user (admin only)::

user = gl.users.get(25, lazy=True)
access_token = user.personal_access_tokens.create({"name": "test", "scopes": "api"})

.. note:: As you can see above, you can only create personal access tokens
via the Users API, but you cannot revoke these objects directly.
This is because the create API uses a different endpoint than the list and revoke APIs.
You need to fetch the token via the list API first to revoke it.

As of 14.2, GitLab does not provide a GET API for single personal access tokens.
You must use the list method to retrieve single tokens.
23 changes: 19 additions & 4 deletions gitlab/v4/objects/personal_access_tokens.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import ListMixin
from gitlab.base import RequiredOptional, RESTManager, RESTObject
from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin

__all__ = [
"PersonalAccessToken",
"PersonalAccessTokenManager",
"UserPersonalAccessToken",
"UserPersonalAccessTokenManager",
]


class PersonalAccessToken(RESTObject):
class PersonalAccessToken(ObjectDeleteMixin, RESTObject):
pass


class PersonalAccessTokenManager(ListMixin, RESTManager):
class PersonalAccessTokenManager(DeleteMixin, ListMixin, RESTManager):
_path = "/personal_access_tokens"
_obj_cls = PersonalAccessToken
_list_filters = ("user_id",)


class UserPersonalAccessToken(RESTObject):
pass


class UserPersonalAccessTokenManager(CreateMixin, RESTManager):
_path = "/users/%(user_id)s/personal_access_tokens"
_obj_cls = UserPersonalAccessToken
_from_parent_attrs = {"user_id": "id"}
_create_attrs = RequiredOptional(
required=("name", "scopes"), optional=("expires_at",)
)
2 changes: 2 additions & 0 deletions gitlab/v4/objects/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from .custom_attributes import UserCustomAttributeManager # noqa: F401
from .events import UserEventManager # noqa: F401
from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401

__all__ = [
"CurrentUserEmail",
Expand Down Expand Up @@ -122,6 +123,7 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject):
impersonationtokens: "UserImpersonationTokenManager"
keys: "UserKeyManager"
memberships: "UserMembershipManager"
personal_access_tokens: UserPersonalAccessTokenManager
projects: "UserProjectManager"
status: "UserStatusManager"

Expand Down
90 changes: 69 additions & 21 deletions tests/unit/objects/test_personal_access_tokens.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,94 @@
"""
GitLab API: https://docs.gitlab.com/ee/api/personal_access_tokens.html
GitLab API:
https://docs.gitlab.com/ee/api/personal_access_tokens.html
https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token
"""

import pytest
import responses

user_id = 1
token_id = 1
token_name = "Test Token"

token_url = "http://localhost/api/v4/personal_access_tokens"
single_token_url = f"{token_url}/{token_id}"
user_token_url = f"http://localhost/api/v4/users/{user_id}/personal_access_tokens"

content = {
"id": token_id,
"name": token_name,
"revoked": False,
"created_at": "2020-07-23T14:31:47.729Z",
"scopes": ["api"],
"active": True,
"user_id": user_id,
"expires_at": None,
}


@pytest.fixture
def resp_list_personal_access_token():
content = [
{
"id": 4,
"name": "Test Token",
"revoked": False,
"created_at": "2020-07-23T14:31:47.729Z",
"scopes": ["api"],
"active": True,
"user_id": 24,
"expires_at": None,
}
]
def resp_create_user_personal_access_token():
with responses.RequestsMock() as rsps:
rsps.add(
method=responses.POST,
url=user_token_url,
json=content,
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_personal_access_token(no_content):
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/personal_access_tokens",
json=content,
url=token_url,
json=[content],
content_type="application/json",
status=200,
)
rsps.add(
method=responses.DELETE,
url=single_token_url,
json=no_content,
content_type="application/json",
status=204,
)
yield rsps


def test_list_personal_access_tokens(gl, resp_list_personal_access_token):
def test_create_personal_access_token(gl, resp_create_user_personal_access_token):
user = gl.users.get(1, lazy=True)
access_token = user.personal_access_tokens.create(
{"name": token_name, "scopes": "api"}
)
assert access_token.revoked is False
assert access_token.name == token_name


def test_list_personal_access_tokens(gl, resp_personal_access_token):
access_tokens = gl.personal_access_tokens.list()
assert len(access_tokens) == 1
assert access_tokens[0].revoked is False
assert access_tokens[0].name == "Test Token"
assert access_tokens[0].name == token_name


def test_list_personal_access_tokens_filter(gl, resp_list_personal_access_token):
access_tokens = gl.personal_access_tokens.list(user_id=24)
def test_list_personal_access_tokens_filter(gl, resp_personal_access_token):
access_tokens = gl.personal_access_tokens.list(user_id=user_id)
assert len(access_tokens) == 1
assert access_tokens[0].revoked is False
assert access_tokens[0].user_id == 24
assert access_tokens[0].user_id == user_id


def test_revoke_personal_access_token(gl, resp_personal_access_token):
access_token = gl.personal_access_tokens.list(user_id=user_id)[0]
access_token.delete()
assert resp_personal_access_token.assert_call_count(single_token_url, 1)


def test_revoke_personal_access_token_by_id(gl, resp_personal_access_token):
gl.personal_access_tokens.delete(token_id)
assert resp_personal_access_token.assert_call_count(single_token_url, 1)

0 comments on commit e19314d

Please sign in to comment.