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

Lines changed: 29 additions & 3 deletions
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.
Lines changed: 19 additions & 4 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 69 additions & 21 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy