Skip to content

Commit 2d1b749

Browse files
TimKnight-DWPnejch
andauthored
feat(job_token_scope): support Groups in job token allowlist API (#2816)
* feat(job_token_scope): support job token access allowlist API Signed-off-by: Tim Knight <tim.knight1@engineering.digital.dwp.gov.uk> l.dwp.gov.uk> Co-authored-by: Nejc Habjan <nejc.habjan@siemens.com>
1 parent c5d0404 commit 2d1b749

File tree

4 files changed

+353
-0
lines changed

4 files changed

+353
-0
lines changed

docs/gl_objects/job_token_scope.rst

+50
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,53 @@ Refresh the current state of job token scope::
4949
scope.refresh()
5050
print(scope.inbound_enabled)
5151
# False
52+
53+
Get a project's CI/CD job token inbound allowlist::
54+
55+
allowlist = scope.allowlist.list()
56+
57+
Add a project to the project's inbound allowlist::
58+
59+
allowed_project = scope.allowlist.create({"target_project_id": 42})
60+
61+
Remove a project from the project's inbound allowlist::
62+
63+
allowed_project.delete()
64+
# or directly using a project ID
65+
scope.allowlist.delete(42)
66+
67+
.. warning::
68+
69+
Similar to above, the ID attributes you receive from the create and list
70+
APIs are not consistent (in create() the id is returned as ``source_project_id`` whereas list() returns as ``id``). To safely retrieve the ID of the allowlisted project
71+
regardless of how the object was created, always use its ``.get_id()`` method.
72+
73+
Using ``.get_id()``::
74+
75+
resp = allowlist.create({"target_project_id": 2})
76+
allowlist_id = resp.get_id()
77+
78+
allowlists = project.allowlist.list()
79+
for allowlist in allowlists:
80+
allowlist_id == allowlist.get_id()
81+
82+
Get a project's CI/CD job token inbound groups allowlist::
83+
84+
allowlist = scope.groups_allowlist.list()
85+
86+
Add a project to the project's inbound groups allowlist::
87+
88+
allowed_project = scope.groups_allowlist.create({"target_project_id": 42})
89+
90+
Remove a project from the project's inbound agroups llowlist::
91+
92+
allowed_project.delete()
93+
# or directly using a Group ID
94+
scope.groups_allowlist.delete(42)
95+
96+
.. warning::
97+
98+
Similar to above, the ID attributes you receive from the create and list
99+
APIs are not consistent. To safely retrieve the ID of the allowlisted group
100+
regardless of how the object was created, always use its ``.get_id()`` method.
101+

gitlab/v4/objects/job_token_scope.py

+48
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
from gitlab.base import RESTManager, RESTObject
44
from gitlab.mixins import (
5+
CreateMixin,
6+
DeleteMixin,
57
GetWithoutIdMixin,
8+
ListMixin,
9+
ObjectDeleteMixin,
610
RefreshMixin,
711
SaveMixin,
812
UpdateMethod,
913
UpdateMixin,
1014
)
15+
from gitlab.types import RequiredOptional
1116

1217
__all__ = [
1318
"ProjectJobTokenScope",
@@ -18,6 +23,9 @@
1823
class ProjectJobTokenScope(RefreshMixin, SaveMixin, RESTObject):
1924
_id_attr = None
2025

26+
allowlist: "AllowlistProjectManager"
27+
groups_allowlist: "AllowlistGroupManager"
28+
2129

2230
class ProjectJobTokenScopeManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
2331
_path = "/projects/{project_id}/job_token_scope"
@@ -27,3 +35,43 @@ class ProjectJobTokenScopeManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
2735

2836
def get(self, **kwargs: Any) -> ProjectJobTokenScope:
2937
return cast(ProjectJobTokenScope, super().get(**kwargs))
38+
39+
40+
class AllowlistProject(ObjectDeleteMixin, RESTObject):
41+
_id_attr = "target_project_id" # note: only true for create endpoint
42+
43+
def get_id(self) -> int:
44+
"""Returns the id of the resource. This override deals with
45+
the fact that either an `id` or a `target_project_id` attribute
46+
is returned by the server depending on the endpoint called."""
47+
target_project_id = cast(int, super().get_id())
48+
if target_project_id is not None:
49+
return target_project_id
50+
return cast(int, self.id)
51+
52+
53+
class AllowlistProjectManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
54+
_path = "/projects/{project_id}/job_token_scope/allowlist"
55+
_obj_cls = AllowlistProject
56+
_from_parent_attrs = {"project_id": "project_id"}
57+
_create_attrs = RequiredOptional(required=("target_project_id",))
58+
59+
60+
class AllowlistGroup(ObjectDeleteMixin, RESTObject):
61+
_id_attr = "target_group_id" # note: only true for create endpoint
62+
63+
def get_id(self) -> int:
64+
"""Returns the id of the resource. This override deals with
65+
the fact that either an `id` or a `target_group_id` attribute
66+
is returned by the server depending on the endpoint called."""
67+
target_group_id = cast(int, super().get_id())
68+
if target_group_id is not None:
69+
return target_group_id
70+
return cast(int, self.id)
71+
72+
73+
class AllowlistGroupManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
74+
_path = "/projects/{project_id}/job_token_scope/groups_allowlist"
75+
_obj_cls = AllowlistGroup
76+
_from_parent_attrs = {"project_id": "project_id"}
77+
_create_attrs = RequiredOptional(required=("target_group_id",))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html#allow-any-project-to-access-your-project
2+
def test_enable_limit_access_to_this_project(gl, project):
3+
scope = project.job_token_scope.get()
4+
5+
scope.enabled = True
6+
scope.save()
7+
8+
scope.refresh()
9+
10+
assert scope.inbound_enabled
11+
12+
13+
def test_disable_limit_access_to_this_project(gl, project):
14+
scope = project.job_token_scope.get()
15+
16+
scope.enabled = False
17+
scope.save()
18+
19+
scope.refresh()
20+
21+
assert not scope.inbound_enabled
22+
23+
24+
def test_add_project_to_job_token_scope_allowlist(gl, project):
25+
project_to_add = gl.projects.create({"name": "Ci_Cd_token_add_proj"})
26+
27+
scope = project.job_token_scope.get()
28+
resp = scope.allowlist.create({"target_project_id": project_to_add.id})
29+
30+
assert resp.source_project_id == project.id
31+
assert resp.target_project_id == project_to_add.id
32+
33+
project_to_add.delete()
34+
35+
36+
def test_projects_job_token_scope_allowlist_contains_added_project_name(gl, project):
37+
scope = project.job_token_scope.get()
38+
project_name = "Ci_Cd_token_named_proj"
39+
project_to_add = gl.projects.create({"name": project_name})
40+
scope.allowlist.create({"target_project_id": project_to_add.id})
41+
42+
scope.refresh()
43+
assert any(allowed.name == project_name for allowed in scope.allowlist.list())
44+
45+
project_to_add.delete()
46+
47+
48+
def test_remove_project_by_id_from_projects_job_token_scope_allowlist(gl, project):
49+
scope = project.job_token_scope.get()
50+
51+
project_to_add = gl.projects.create({"name": "Ci_Cd_token_remove_proj"})
52+
53+
scope.allowlist.create({"target_project_id": project_to_add.id})
54+
55+
scope.refresh()
56+
57+
scope.allowlist.delete(project_to_add.id)
58+
59+
scope.refresh()
60+
assert not any(
61+
allowed.id == project_to_add.id for allowed in scope.allowlist.list()
62+
)
63+
64+
project_to_add.delete()
65+
66+
67+
def test_add_group_to_job_token_scope_allowlist(gl, project):
68+
group_to_add = gl.groups.create(
69+
{"name": "add_group", "path": "allowlisted-add-test"}
70+
)
71+
72+
scope = project.job_token_scope.get()
73+
resp = scope.groups_allowlist.create({"target_group_id": group_to_add.id})
74+
75+
assert resp.source_project_id == project.id
76+
assert resp.target_group_id == group_to_add.id
77+
78+
group_to_add.delete()
79+
80+
81+
def test_projects_job_token_scope_groups_allowlist_contains_added_group_name(
82+
gl, project
83+
):
84+
scope = project.job_token_scope.get()
85+
group_name = "list_group"
86+
group_to_add = gl.groups.create(
87+
{"name": group_name, "path": "allowlisted-add-and-list-test"}
88+
)
89+
90+
scope.groups_allowlist.create({"target_group_id": group_to_add.id})
91+
92+
scope.refresh()
93+
assert any(allowed.name == group_name for allowed in scope.groups_allowlist.list())
94+
95+
group_to_add.delete()
96+
97+
98+
def test_remove_group_by_id_from_projects_job_token_scope_groups_allowlist(gl, project):
99+
scope = project.job_token_scope.get()
100+
101+
group_to_add = gl.groups.create(
102+
{"name": "delete_group", "path": "allowlisted-delete-test"}
103+
)
104+
105+
scope.groups_allowlist.create({"target_group_id": group_to_add.id})
106+
107+
scope.refresh()
108+
109+
scope.groups_allowlist.delete(group_to_add.id)
110+
111+
scope.refresh()
112+
assert not any(
113+
allowed.name == group_to_add.name for allowed in scope.groups_allowlist.list()
114+
)
115+
116+
group_to_add.delete()

0 commit comments

Comments
 (0)