Skip to content

Commit 3ea1a0a

Browse files
authored
feat: add get_role_composites_by_id method (#680)
1 parent c5a8309 commit 3ea1a0a

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

src/keycloak/keycloak_admin.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3481,6 +3481,26 @@ def delete_role_by_id(self, role_id: str) -> bytes:
34813481
expected_codes=[HTTP_NO_CONTENT],
34823482
)
34833483

3484+
def get_role_composites_by_id(self, role_id: str, query: dict | None = None) -> list:
3485+
"""
3486+
Get all composite roles by role id.
3487+
3488+
:param role_id: id of role
3489+
:type role_id: str
3490+
:param query: Query parameters (optional). Supported keys: 'first', 'max', 'search'
3491+
:type query: dict
3492+
:return: Keycloak server response (RoleRepresentation)
3493+
:rtype: list
3494+
"""
3495+
query = query or {}
3496+
params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
3497+
url = urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path)
3498+
3499+
if "first" in query or "max" in query:
3500+
return self.__fetch_paginated(url, query)
3501+
3502+
return self.__fetch_all(url, query)
3503+
34843504
def create_realm_role(self, payload: dict, skip_exists: bool = False) -> str:
34853505
"""
34863506
Create a new role for the realm or client.
@@ -8779,6 +8799,26 @@ async def a_delete_role_by_id(self, role_id: str) -> bytes:
87798799
expected_codes=[HTTP_NO_CONTENT],
87808800
)
87818801

8802+
async def a_get_role_composites_by_id(self, role_id: str, query: dict | None = None) -> list:
8803+
"""
8804+
Get all composite roles by role id asynchronously.
8805+
8806+
:param role_id: id of role
8807+
:type role_id: str
8808+
:param query: Query parameters (optional). Supported keys: 'first', 'max', 'search'
8809+
:type query: dict
8810+
:return: Keycloak server response (RoleRepresentation)
8811+
:rtype: list
8812+
"""
8813+
query = query or {}
8814+
params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
8815+
url = urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path)
8816+
8817+
if "first" in query or "max" in query:
8818+
return await self.a___fetch_paginated(url, query)
8819+
8820+
return await self.a___fetch_all(url, query)
8821+
87828822
async def a_create_realm_role(self, payload: dict, skip_exists: bool = False) -> str:
87838823
"""
87848824
Create a new role for the realm or client asynchronously.

tests/test_keycloak_admin.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3287,6 +3287,48 @@ def test_get_role_client_level_children(
32873287
assert child["id"] in [x["id"] for x in res]
32883288

32893289

3290+
def test_get_role_composites_by_id(
3291+
admin: KeycloakAdmin,
3292+
realm: str,
3293+
client: str,
3294+
composite_client_role: str,
3295+
client_role: str,
3296+
) -> None:
3297+
"""
3298+
Test get role's children by role ID.
3299+
3300+
:param admin: Keycloak Admin client
3301+
:type admin: KeycloakAdmin
3302+
:param realm: Keycloak realm
3303+
:type realm: str
3304+
:param client: Keycloak client
3305+
:type client: str
3306+
:param composite_client_role: Composite client role
3307+
:type composite_client_role: str
3308+
:param client_role: Client role
3309+
:type client_role: str
3310+
"""
3311+
admin.change_current_realm(realm)
3312+
3313+
parent_role = admin.get_client_role(client, composite_client_role)
3314+
child_role = admin.get_client_role(client, client_role)
3315+
3316+
composites = admin.get_role_composites_by_id(parent_role["id"])
3317+
assert len(composites) > 0
3318+
assert child_role["id"] in [x["id"] for x in composites]
3319+
3320+
composites_paginated = admin.get_role_composites_by_id(
3321+
parent_role["id"], query={"first": 0, "max": 10}
3322+
)
3323+
assert len(composites_paginated) > 0
3324+
assert child_role["id"] in [x["id"] for x in composites_paginated]
3325+
3326+
composites_searched = admin.get_role_composites_by_id(
3327+
parent_role["id"], query={"search": client_role[:3]}
3328+
)
3329+
assert len(composites_searched) > 0
3330+
3331+
32903332
def test_upload_certificate(
32913333
admin: KeycloakAdmin,
32923334
realm: str,
@@ -7019,6 +7061,49 @@ async def test_a_get_role_client_level_children(
70197061
assert child["id"] in [x["id"] for x in res]
70207062

70217063

7064+
@pytest.mark.asyncio
7065+
async def test_a_get_role_composites_by_id(
7066+
admin: KeycloakAdmin,
7067+
realm: str,
7068+
client: str,
7069+
composite_client_role: str,
7070+
client_role: str,
7071+
) -> None:
7072+
"""
7073+
Test get all composite roles by role id asynchronously.
7074+
7075+
:param admin: Keycloak Admin client
7076+
:type admin: KeycloakAdmin
7077+
:param realm: Keycloak realm
7078+
:type realm: str
7079+
:param client: Keycloak client
7080+
:type client: str
7081+
:param composite_client_role: Composite client role
7082+
:type composite_client_role: str
7083+
:param client_role: Client role
7084+
:type client_role: str
7085+
"""
7086+
await admin.a_change_current_realm(realm)
7087+
7088+
parent_role = await admin.a_get_client_role(client, composite_client_role)
7089+
child_role = await admin.a_get_client_role(client, client_role)
7090+
7091+
composites = await admin.a_get_role_composites_by_id(parent_role["id"])
7092+
assert len(composites) > 0
7093+
assert child_role["id"] in [x["id"] for x in composites]
7094+
7095+
composites_paginated = await admin.a_get_role_composites_by_id(
7096+
parent_role["id"], query={"first": 0, "max": 10}
7097+
)
7098+
assert len(composites_paginated) > 0
7099+
assert child_role["id"] in [x["id"] for x in composites_paginated]
7100+
7101+
composites_searched = await admin.a_get_role_composites_by_id(
7102+
parent_role["id"], query={"search": client_role[:3]}
7103+
)
7104+
assert len(composites_searched) > 0
7105+
7106+
70227107
@pytest.mark.asyncio
70237108
async def test_a_upload_certificate(
70247109
admin: KeycloakAdmin,

0 commit comments

Comments
 (0)