Skip to content

Commit e19314d

Browse files
nejchJohnVillalovos
authored andcommitted
feat(objects): support Create and Revoke personal access token API
1 parent a5d8b7f commit e19314d

File tree

4 files changed

+119
-28
lines changed

4 files changed

+119
-28
lines changed

docs/gl_objects/personal_access_tokens.rst

+29-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
Personal Access Tokens
33
######################
44

5-
Get a list of personal access tokens
6-
75
References
86
----------
97

@@ -12,8 +10,14 @@ References
1210
+ :class:`gitlab.v4.objects.PersonalAccessToken`
1311
+ :class:`gitlab.v4.objects.PersonalAcessTokenManager`
1412
+ :attr:`gitlab.Gitlab.personal_access_tokens`
13+
+ :class:`gitlab.v4.objects.UserPersonalAccessToken`
14+
+ :class:`gitlab.v4.objects.UserPersonalAcessTokenManager`
15+
+ :attr:`gitlab.Gitlab.User.personal_access_tokens`
16+
17+
* GitLab API:
1518

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

1822
Examples
1923
--------
@@ -26,3 +30,25 @@ List personal access tokens::
2630
List personal access tokens from other user_id (admin only)::
2731

2832
access_tokens = gl.personal_access_tokens.list(user_id=25)
33+
34+
Revoke a personal access token fetched via list::
35+
36+
access_token = access_tokens[0]
37+
access_token.delete()
38+
39+
Revoke a personal access token by id::
40+
41+
gl.personal_access_tokens.delete(123)
42+
43+
Create a personal access token for a user (admin only)::
44+
45+
user = gl.users.get(25, lazy=True)
46+
access_token = user.personal_access_tokens.create({"name": "test", "scopes": "api"})
47+
48+
.. note:: As you can see above, you can only create personal access tokens
49+
via the Users API, but you cannot revoke these objects directly.
50+
This is because the create API uses a different endpoint than the list and revoke APIs.
51+
You need to fetch the token via the list API first to revoke it.
52+
53+
As of 14.2, GitLab does not provide a GET API for single personal access tokens.
54+
You must use the list method to retrieve single tokens.
+19-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1-
from gitlab.base import RESTManager, RESTObject
2-
from gitlab.mixins import ListMixin
1+
from gitlab.base import RequiredOptional, RESTManager, RESTObject
2+
from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
33

44
__all__ = [
55
"PersonalAccessToken",
66
"PersonalAccessTokenManager",
7+
"UserPersonalAccessToken",
8+
"UserPersonalAccessTokenManager",
79
]
810

911

10-
class PersonalAccessToken(RESTObject):
12+
class PersonalAccessToken(ObjectDeleteMixin, RESTObject):
1113
pass
1214

1315

14-
class PersonalAccessTokenManager(ListMixin, RESTManager):
16+
class PersonalAccessTokenManager(DeleteMixin, ListMixin, RESTManager):
1517
_path = "/personal_access_tokens"
1618
_obj_cls = PersonalAccessToken
1719
_list_filters = ("user_id",)
20+
21+
22+
class UserPersonalAccessToken(RESTObject):
23+
pass
24+
25+
26+
class UserPersonalAccessTokenManager(CreateMixin, RESTManager):
27+
_path = "/users/%(user_id)s/personal_access_tokens"
28+
_obj_cls = UserPersonalAccessToken
29+
_from_parent_attrs = {"user_id": "id"}
30+
_create_attrs = RequiredOptional(
31+
required=("name", "scopes"), optional=("expires_at",)
32+
)

gitlab/v4/objects/users.py

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from .custom_attributes import UserCustomAttributeManager # noqa: F401
1919
from .events import UserEventManager # noqa: F401
20+
from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401
2021

2122
__all__ = [
2223
"CurrentUserEmail",
@@ -122,6 +123,7 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject):
122123
impersonationtokens: "UserImpersonationTokenManager"
123124
keys: "UserKeyManager"
124125
memberships: "UserMembershipManager"
126+
personal_access_tokens: UserPersonalAccessTokenManager
125127
projects: "UserProjectManager"
126128
status: "UserStatusManager"
127129

Original file line numberDiff line numberDiff line change
@@ -1,46 +1,94 @@
11
"""
2-
GitLab API: https://docs.gitlab.com/ee/api/personal_access_tokens.html
2+
GitLab API:
3+
https://docs.gitlab.com/ee/api/personal_access_tokens.html
4+
https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token
35
"""
46

57
import pytest
68
import responses
79

10+
user_id = 1
11+
token_id = 1
12+
token_name = "Test Token"
13+
14+
token_url = "http://localhost/api/v4/personal_access_tokens"
15+
single_token_url = f"{token_url}/{token_id}"
16+
user_token_url = f"http://localhost/api/v4/users/{user_id}/personal_access_tokens"
17+
18+
content = {
19+
"id": token_id,
20+
"name": token_name,
21+
"revoked": False,
22+
"created_at": "2020-07-23T14:31:47.729Z",
23+
"scopes": ["api"],
24+
"active": True,
25+
"user_id": user_id,
26+
"expires_at": None,
27+
}
28+
829

930
@pytest.fixture
10-
def resp_list_personal_access_token():
11-
content = [
12-
{
13-
"id": 4,
14-
"name": "Test Token",
15-
"revoked": False,
16-
"created_at": "2020-07-23T14:31:47.729Z",
17-
"scopes": ["api"],
18-
"active": True,
19-
"user_id": 24,
20-
"expires_at": None,
21-
}
22-
]
31+
def resp_create_user_personal_access_token():
32+
with responses.RequestsMock() as rsps:
33+
rsps.add(
34+
method=responses.POST,
35+
url=user_token_url,
36+
json=content,
37+
content_type="application/json",
38+
status=200,
39+
)
40+
yield rsps
2341

42+
43+
@pytest.fixture
44+
def resp_personal_access_token(no_content):
2445
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
2546
rsps.add(
2647
method=responses.GET,
27-
url="http://localhost/api/v4/personal_access_tokens",
28-
json=content,
48+
url=token_url,
49+
json=[content],
2950
content_type="application/json",
3051
status=200,
3152
)
53+
rsps.add(
54+
method=responses.DELETE,
55+
url=single_token_url,
56+
json=no_content,
57+
content_type="application/json",
58+
status=204,
59+
)
3260
yield rsps
3361

3462

35-
def test_list_personal_access_tokens(gl, resp_list_personal_access_token):
63+
def test_create_personal_access_token(gl, resp_create_user_personal_access_token):
64+
user = gl.users.get(1, lazy=True)
65+
access_token = user.personal_access_tokens.create(
66+
{"name": token_name, "scopes": "api"}
67+
)
68+
assert access_token.revoked is False
69+
assert access_token.name == token_name
70+
71+
72+
def test_list_personal_access_tokens(gl, resp_personal_access_token):
3673
access_tokens = gl.personal_access_tokens.list()
3774
assert len(access_tokens) == 1
3875
assert access_tokens[0].revoked is False
39-
assert access_tokens[0].name == "Test Token"
76+
assert access_tokens[0].name == token_name
4077

4178

42-
def test_list_personal_access_tokens_filter(gl, resp_list_personal_access_token):
43-
access_tokens = gl.personal_access_tokens.list(user_id=24)
79+
def test_list_personal_access_tokens_filter(gl, resp_personal_access_token):
80+
access_tokens = gl.personal_access_tokens.list(user_id=user_id)
4481
assert len(access_tokens) == 1
4582
assert access_tokens[0].revoked is False
46-
assert access_tokens[0].user_id == 24
83+
assert access_tokens[0].user_id == user_id
84+
85+
86+
def test_revoke_personal_access_token(gl, resp_personal_access_token):
87+
access_token = gl.personal_access_tokens.list(user_id=user_id)[0]
88+
access_token.delete()
89+
assert resp_personal_access_token.assert_call_count(single_token_url, 1)
90+
91+
92+
def test_revoke_personal_access_token_by_id(gl, resp_personal_access_token):
93+
gl.personal_access_tokens.delete(token_id)
94+
assert resp_personal_access_token.assert_call_count(single_token_url, 1)

0 commit comments

Comments
 (0)