Browse Source

Merge pull request #372 from Merle-Nerger/feature/get_roles_with_attributes

feat: bulk role queries with role attributes
pull/374/head
Richard Nemeth 2 years ago
committed by GitHub
parent
commit
b1df072e54
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 77
      src/keycloak/keycloak_admin.py
  2. 57
      tests/test_keycloak_admin.py

77
src/keycloak/keycloak_admin.py

@ -578,17 +578,21 @@ class KeycloakAdmin:
data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path)) data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path))
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def get_user_groups(self, user_id):
def get_user_groups(self, user_id, brief_representation=True):
"""Get user groups. """Get user groups.
Returns a list of groups of which the user is a member Returns a list of groups of which the user is a member
:param user_id: User id :param user_id: User id
:param brief_representation: whether to omit attributes in the response
:return: user groups list :return: user groups list
""" """
params = {"briefRepresentation": brief_representation}
params_path = {"realm-name": self.realm_name, "id": user_id} params_path = {"realm-name": self.realm_name, "id": user_id}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path))
data_raw = self.raw_get(
urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def update_user(self, user_id, payload): def update_user(self, user_id, payload):
@ -1300,16 +1304,20 @@ class KeycloakAdmin:
) )
return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
def get_realm_roles(self):
def get_realm_roles(self, brief_representation=True):
"""Get all roles for the realm or client. """Get all roles for the realm or client.
RoleRepresentation RoleRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation
:return: Keycloak server response (RoleRepresentation)
:param brief_representation: whether to omit role attributes in the response
:return: Keycloak server response (array RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name} params_path = {"realm-name": self.realm_name}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path))
params = {"briefRepresentation": brief_representation}
data_raw = self.raw_get(
urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def get_realm_role_members(self, role_name, query=None): def get_realm_role_members(self, role_name, query=None):
@ -1326,18 +1334,21 @@ class KeycloakAdmin:
urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query
) )
def get_client_roles(self, client_id):
def get_client_roles(self, client_id, brief_representation=True):
"""Get all roles for the client. """Get all roles for the client.
:param client_id: id of client (not client-id)
RoleRepresentation RoleRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation
:return: Keycloak server response (RoleRepresentation)
:param client_id: id of client (not client-id)
:param brief_representation: whether to omit role attributes in the response
:return: Keycloak server response (array RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name, "id": client_id} params_path = {"realm-name": self.realm_name, "id": client_id}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path))
params = {"briefRepresentation": brief_representation}
data_raw = self.raw_get(
urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def get_client_role(self, client_id, role_name): def get_client_role(self, client_id, role_name):
@ -1345,13 +1356,12 @@ class KeycloakAdmin:
This is required for further actions with this role. This is required for further actions with this role.
:param client_id: id of client (not client-id)
:param role_name: roles name (not id!)
RoleRepresentation RoleRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation
:return: role_id
:param client_id: id of client (not client-id)
:param role_name: roles name (not id!)
:return: Keycloak server response (RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path))
@ -1517,11 +1527,11 @@ class KeycloakAdmin:
def get_realm_role(self, role_name): def get_realm_role(self, role_name):
"""Get realm role by role name. """Get realm role by role name.
:param role_name: role's name, not id!
RoleRepresentation RoleRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation
:return: role_id
:param role_name: role's name, not id!
:return: Keycloak server response (RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name, "role-name": role_name} params_path = {"realm-name": self.realm_name, "role-name": role_name}
data_raw = self.raw_get( data_raw = self.raw_get(
@ -1748,15 +1758,17 @@ class KeycloakAdmin:
) )
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def get_composite_realm_roles_of_user(self, user_id):
def get_composite_realm_roles_of_user(self, user_id, brief_representation=True):
"""Get all composite (i.e. implicit) realm roles for a user. """Get all composite (i.e. implicit) realm roles for a user.
:param user_id: id of user :param user_id: id of user
:param brief_representation: whether to omit role attributes in the response
:return: Keycloak server response (array RoleRepresentation) :return: Keycloak server response (array RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name, "id": user_id} params_path = {"realm-name": self.realm_name, "id": user_id}
params = {"briefRepresentation": brief_representation}
data_raw = self.raw_get( data_raw = self.raw_get(
urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path)
urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path), **params
) )
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
@ -1790,14 +1802,18 @@ class KeycloakAdmin:
) )
return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
def get_group_realm_roles(self, group_id):
def get_group_realm_roles(self, group_id, brief_representation=True):
"""Get all realm roles for a group. """Get all realm roles for a group.
:param user_id: id of the group :param user_id: id of the group
:param brief_representation: whether to omit role attributes in the response
:return: Keycloak server response (array RoleRepresentation) :return: Keycloak server response (array RoleRepresentation)
""" """
params_path = {"realm-name": self.realm_name, "id": group_id} params_path = {"realm-name": self.realm_name, "id": group_id}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path))
params = {"briefRepresentation": brief_representation}
data_raw = self.raw_get(
urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def assign_group_client_roles(self, group_id, client_id, roles): def assign_group_client_roles(self, group_id, client_id, roles):
@ -1865,20 +1881,24 @@ class KeycloakAdmin:
urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id
) )
def get_composite_client_roles_of_user(self, user_id, client_id):
def get_composite_client_roles_of_user(self, user_id, client_id, brief_representation=False):
"""Get composite client role-mappings for a user. """Get composite client role-mappings for a user.
:param user_id: id of user :param user_id: id of user
:param client_id: id of client (not client-id) :param client_id: id of client (not client-id)
:param brief_representation: whether to omit attributes in the response
:return: Keycloak server response (array RoleRepresentation) :return: Keycloak server response (array RoleRepresentation)
""" """
params = {"briefRepresentation": brief_representation}
return self._get_client_roles_of_user( return self._get_client_roles_of_user(
urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id
urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id, **params
) )
def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id):
def _get_client_roles_of_user(
self, client_level_role_mapping_url, user_id, client_id, **params
):
params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path))
data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path), **params)
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)
def delete_client_roles_of_user(self, user_id, client_id, roles): def delete_client_roles_of_user(self, user_id, client_id, roles):
@ -2913,19 +2933,22 @@ class KeycloakAdmin:
) )
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
def get_composite_client_roles_of_group(self, client_id, group_id):
def get_composite_client_roles_of_group(self, client_id, group_id, brief_representation=True):
"""Get the composite client roles of the given group for the given client. """Get the composite client roles of the given group for the given client.
:param client_id: id of the client. :param client_id: id of the client.
:type client_id: str :type client_id: str
:param group_id: id of the group. :param group_id: id of the group.
:type group_id: str :type group_id: str
:param brief_representation: whether to omit attributes in the response
:type brief_representation: bool
:return: the composite client roles of the group (list of RoleRepresentation). :return: the composite client roles of the group (list of RoleRepresentation).
:rtype: list :rtype: list
""" """
params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
params = {"briefRepresentation": brief_representation}
data_raw = self.raw_get( data_raw = self.raw_get(
urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path)
urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path), **params
) )
return raise_error_from_response(data_raw, KeycloakGetError) return raise_error_from_response(data_raw, KeycloakGetError)

57
tests/test_keycloak_admin.py

@ -1030,6 +1030,63 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str):
assert err.match('404: b\'{"error":"Could not find role"}\'') assert err.match('404: b\'{"error":"Could not find role"}\'')
@pytest.mark.parametrize(
"testcase, arg_brief_repr, includes_attributes",
[
("brief True", {"brief_representation": True}, False),
("brief False", {"brief_representation": False}, True),
("default", {}, False),
],
)
def test_role_attributes(
admin: KeycloakAdmin,
realm: str,
client: str,
arg_brief_repr: dict,
includes_attributes: bool,
testcase: str,
):
"""Test getting role attributes for bulk calls."""
# setup
attribute_role = "test-realm-role-w-attr"
test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
role_id = admin.create_realm_role(
payload={"name": attribute_role, "attributes": test_attrs},
skip_exists=True,
)
assert role_id, role_id
cli_role_id = admin.create_client_role(
client,
payload={"name": attribute_role, "attributes": test_attrs},
skip_exists=True,
)
assert cli_role_id, cli_role_id
if not includes_attributes:
test_attrs = None
# tests
roles = admin.get_realm_roles(**arg_brief_repr)
roles_filtered = [role for role in roles if role["name"] == role_id]
assert roles_filtered, roles_filtered
role = roles_filtered[0]
assert role.get("attributes") == test_attrs, testcase
roles = admin.get_client_roles(client, **arg_brief_repr)
roles_filtered = [role for role in roles if role["name"] == cli_role_id]
assert roles_filtered, roles_filtered
role = roles_filtered[0]
assert role.get("attributes") == test_attrs, testcase
# cleanup
res = admin.delete_realm_role(role_name=attribute_role)
assert res == dict(), res
res = admin.delete_client_role(client, role_name=attribute_role)
assert res == dict(), res
def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
"""Test client realm roles.""" """Test client realm roles."""
admin.realm_name = realm admin.realm_name = realm

Loading…
Cancel
Save