Skip to content

Commit da40e09

Browse files
Massimiliano RivaJohnVillalovos
authored andcommitted
feat(api): add support for token self-rotation
1 parent 938b0d9 commit da40e09

File tree

7 files changed

+100
-3
lines changed

7 files changed

+100
-3
lines changed

docs/gl_objects/group_access_tokens.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,9 @@ Rotate a group access token and retrieve its new value::
4646
# or directly using a token ID
4747
new_token = group.access_tokens.rotate(42)
4848
print(new_token.token)
49+
50+
Self-Rotate the group access token you are using to authenticate the request and retrieve its new value::
51+
52+
token = group.access_tokens.get(42, lazy=True)
53+
token.rotate(self_rotate=True)
54+
print(token.token)

docs/gl_objects/personal_access_tokens.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ Rotate a personal access token and retrieve its new value::
6161
new_token_dict = gl.personal_access_tokens.rotate(42)
6262
print(new_token_dict)
6363

64+
Self-Rotate the personal access token you are using to authenticate the request and retrieve its new value::
65+
66+
token = gl.personal_access_tokens.get(42, lazy=True)
67+
token.rotate(self_rotate=True)
68+
print(token.token)
69+
6470
Create a personal access token for a user (admin only)::
6571

6672
user = gl.users.get(25, lazy=True)

docs/gl_objects/project_access_tokens.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,9 @@ Rotate a project access token and retrieve its new value::
4646
# or directly using a token ID
4747
new_token = project.access_tokens.rotate(42)
4848
print(new_token.token)
49+
50+
Self-Rotate the project access token you are using to authenticate the request and retrieve its new value::
51+
52+
token = project.access_tokens.get(42, lazy=True)
53+
token.rotate(self_rotate=True)
54+
print(new_token.token)

gitlab/mixins.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,11 @@ class ObjectRotateMixin(_RestObjectBase):
660660
optional=("expires_at",),
661661
)
662662
@exc.on_http_error(exc.GitlabRotateError)
663-
def rotate(self, **kwargs: Any) -> dict[str, Any]:
663+
def rotate(self, *, self_rotate: bool = False, **kwargs: Any) -> dict[str, Any]:
664664
"""Rotate the current access token object.
665665
666666
Args:
667+
self_rotate: If True, the current access token object will be rotated.
667668
**kwargs: Extra options to send to the server (e.g. sudo)
668669
669670
Raises:
@@ -673,7 +674,8 @@ def rotate(self, **kwargs: Any) -> dict[str, Any]:
673674
if TYPE_CHECKING:
674675
assert isinstance(self.manager, RotateMixin)
675676
assert self.encoded_id is not None
676-
server_data = self.manager.rotate(self.encoded_id, **kwargs)
677+
token_id = "self" if self_rotate else self.encoded_id
678+
server_data = self.manager.rotate(token_id, **kwargs)
677679
self._update_attrs(server_data)
678680
return server_data
679681

tests/unit/objects/test_group_access_tokens.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ def resp_rotate_group_access_token(token_content):
9191
yield rsps
9292

9393

94+
@pytest.fixture
95+
def resp_self_rotate_group_access_token(token_content):
96+
with responses.RequestsMock() as rsps:
97+
rsps.add(
98+
method=responses.POST,
99+
url="http://localhost/api/v4/groups/1/access_tokens/self/rotate",
100+
json=token_content,
101+
content_type="application/json",
102+
status=200,
103+
)
104+
yield rsps
105+
106+
94107
def test_list_group_access_tokens(gl, resp_list_group_access_token):
95108
access_tokens = gl.groups.get(1, lazy=True).access_tokens.list()
96109
assert len(access_tokens) == 1
@@ -127,3 +140,15 @@ def test_rotate_group_access_token(group, resp_rotate_group_access_token):
127140
access_token.rotate()
128141
assert isinstance(access_token, GroupAccessToken)
129142
assert access_token.token == "s3cr3t"
143+
144+
145+
def test_self_rotate_group_access_token(group, resp_self_rotate_group_access_token):
146+
access_token = group.access_tokens.get(1, lazy=True)
147+
access_token.rotate(self_rotate=True)
148+
assert isinstance(access_token, GroupAccessToken)
149+
assert access_token.token == "s3cr3t"
150+
151+
# Verify that the url contains "self"
152+
rotation_calls = resp_self_rotate_group_access_token.calls
153+
assert len(rotation_calls) == 1
154+
assert "self/rotate" in rotation_calls[0].request.url

tests/unit/objects/test_personal_access_tokens.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,19 @@ def resp_rotate_personal_access_token(token_content):
102102
yield rsps
103103

104104

105+
@pytest.fixture
106+
def resp_self_rotate_personal_access_token(token_content):
107+
with responses.RequestsMock() as rsps:
108+
rsps.add(
109+
method=responses.POST,
110+
url="http://localhost/api/v4/personal_access_tokens/self/rotate",
111+
json=token_content,
112+
content_type="application/json",
113+
status=200,
114+
)
115+
yield rsps
116+
117+
105118
def test_create_personal_access_token(gl, resp_create_user_personal_access_token):
106119
user = gl.users.get(1, lazy=True)
107120
access_token = user.personal_access_tokens.create(
@@ -148,8 +161,20 @@ def test_revoke_personal_access_token_by_id(gl, resp_delete_personal_access_toke
148161
gl.personal_access_tokens.delete(token_id)
149162

150163

151-
def test_rotate_project_access_token(gl, resp_rotate_personal_access_token):
164+
def test_rotate_personal_access_token(gl, resp_rotate_personal_access_token):
152165
access_token = gl.personal_access_tokens.get(1, lazy=True)
153166
access_token.rotate()
154167
assert isinstance(access_token, PersonalAccessToken)
155168
assert access_token.token == "s3cr3t"
169+
170+
171+
def test_self_rotate_personal_access_token(gl, resp_self_rotate_personal_access_token):
172+
access_token = gl.personal_access_tokens.get(1, lazy=True)
173+
access_token.rotate(self_rotate=True)
174+
assert isinstance(access_token, PersonalAccessToken)
175+
assert access_token.token == "s3cr3t"
176+
177+
# Verify that the url contains "self"
178+
rotation_calls = resp_self_rotate_personal_access_token.calls
179+
assert len(rotation_calls) == 1
180+
assert "self/rotate" in rotation_calls[0].request.url

tests/unit/objects/test_project_access_tokens.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ def resp_rotate_project_access_token(token_content):
9191
yield rsps
9292

9393

94+
@pytest.fixture
95+
def resp_self_rotate_project_access_token(token_content):
96+
with responses.RequestsMock() as rsps:
97+
rsps.add(
98+
method=responses.POST,
99+
url="http://localhost/api/v4/projects/1/access_tokens/self/rotate",
100+
json=token_content,
101+
content_type="application/json",
102+
status=200,
103+
)
104+
yield rsps
105+
106+
94107
def test_list_project_access_tokens(gl, resp_list_project_access_token):
95108
access_tokens = gl.projects.get(1, lazy=True).access_tokens.list()
96109
assert len(access_tokens) == 1
@@ -127,3 +140,17 @@ def test_rotate_project_access_token(project, resp_rotate_project_access_token):
127140
access_token.rotate()
128141
assert isinstance(access_token, ProjectAccessToken)
129142
assert access_token.token == "s3cr3t"
143+
144+
145+
def test_self_rotate_project_access_token(
146+
project, resp_self_rotate_project_access_token
147+
):
148+
access_token = project.access_tokens.get(1, lazy=True)
149+
access_token.rotate(self_rotate=True)
150+
assert isinstance(access_token, ProjectAccessToken)
151+
assert access_token.token == "s3cr3t"
152+
153+
# Verify that the url contains "self"
154+
rotation_calls = resp_self_rotate_project_access_token.calls
155+
assert len(rotation_calls) == 1
156+
assert "self/rotate" in rotation_calls[0].request.url

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