From 7c486ccb4fce28ff7e9fb5a5d4b6b95e94343926 Mon Sep 17 00:00:00 2001 From: Antonio Lucas Neres Date: Fri, 19 Aug 2022 14:13:27 -0300 Subject: [PATCH] feat: add client scope-mappings client roles operations --- README.md | 11 +++++++ src/keycloak/keycloak_admin.py | 57 ++++++++++++++++++++++++++++++++++ src/keycloak/urls_patterns.py | 3 ++ tests/test_keycloak_admin.py | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) diff --git a/README.md b/README.md index 9666ba9..3558d30 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,17 @@ keycloak_admin.get_realm_roles_of_client_scope(client_id=client_id) # Remove realm roles assigned to client's scope keycloak_admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=realm_roles) +another_client_id = keycloak_admin.get_client_id("my-client-2") + +# Assign client roles to client's scope +keycloak_admin.assign_client_roles_to_client_scope(client_id=another_client_id, client_roles_owner_id=client_id, roles=client_roles) + +# Get client roles assigned to client's scope +keycloak_admin.get_client_roles_of_client_scope(client_id=another_client_id, client_roles_owner_id=client_id) + +# Remove client roles assigned to client's scope +keycloak_admin.delete_client_roles_of_client_scope(client_id=another_client_id, client_roles_owner_id=client_id, roles=client_roles) + # Get all ID Providers idps = keycloak_admin.get_idps() diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 50299b6..15423a1 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -1639,6 +1639,63 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) + def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles): + """Assign client roles to a client's scope. + + :param client_id: id of client (not client-id) who is assigned the roles + :param client_roles_owner_id: id of client (not client-id) who has the roles + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) + + def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles): + """Delete client roles of a client's scope. + + :param client_id: id of client (not client-id) who is assigned the roles + :param client_roles_owner_id: id of client (not client-id) who has the roles + :param roles: roles list or role (use RoleRepresentation) + :return: Keycloak server response + """ + payload = roles if isinstance(roles, list) else [roles] + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_delete( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) + + def get_client_roles_of_client_scope(self, client_id, client_roles_owner_id): + """Get all client roles for a client's scope. + + :param client_id: id of client (not client-id) + :param client_roles_owner_id: id of client (not client-id) who has the roles + :return: Keycloak server response (array RoleRepresentation) + """ + params_path = { + "realm-name": self.realm_name, + "id": client_id, + "client": client_roles_owner_id, + } + data_raw = self.raw_get( + urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path) + ) + return raise_error_from_response(data_raw, KeycloakGetError) + def assign_realm_roles(self, user_id, roles): """Assign realm roles to a user. diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index f7d4f65..f2a2188 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -92,6 +92,9 @@ URL_ADMIN_CLIENT_ROLE_MEMBERS = URL_ADMIN_CLIENT + "/roles/{role-name}/users" URL_ADMIN_CLIENT_ROLE_GROUPS = URL_ADMIN_CLIENT + "/roles/{role-name}/groups" URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS = URL_ADMIN_CLIENT + "/management/permissions" URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES = URL_ADMIN_CLIENT + "/scope-mappings/realm" +URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES = ( + URL_ADMIN_CLIENT + "/scope-mappings/clients/{client}" +) URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource?max=-1" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index e9b093c..0f5af95 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1088,6 +1088,62 @@ def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): assert len(roles) == 0 +def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str): + """Test client assignment of other client roles.""" + admin.realm_name = realm + + client_id = admin.create_client( + payload={"name": "role-testing-client", "clientId": "role-testing-client"} + ) + + # Test get client roles + roles = admin.get_client_roles_of_client_scope(client_id, client) + assert len(roles) == 0, roles + + # create client role for test + client_role_id = admin.create_client_role( + client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True + ) + assert client_role_id, client_role_id + + # Test client role assignment to other client + with pytest.raises(KeycloakPostError) as err: + admin.assign_client_roles_to_client_scope( + client_id=client_id, client_roles_owner_id=client, roles=["bad"] + ) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.assign_client_roles_to_client_scope( + client_id=client_id, + client_roles_owner_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test")], + ) + assert res == dict(), res + + roles = admin.get_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client + ) + assert len(roles) == 1 + client_role_names = [x["name"] for x in roles] + assert "client-role-test" in client_role_names, client_role_names + + # Test remove realm role of client + with pytest.raises(KeycloakDeleteError) as err: + admin.delete_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client, roles=["bad"] + ) + assert err.match('500: b\'{"error":"unknown_error"}\'') + res = admin.delete_client_roles_of_client_scope( + client_id=client_id, + client_roles_owner_id=client, + roles=[admin.get_client_role(client_id=client, role_name="client-role-test")], + ) + assert res == dict(), res + roles = admin.get_client_roles_of_client_scope( + client_id=client_id, client_roles_owner_id=client + ) + assert len(roles) == 0 + + def test_client_roles(admin: KeycloakAdmin, client: str): """Test client roles.""" # Test get client roles