From 808d137e4cbda167ee5b89e4be7e63acfb5f4483 Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Mon, 8 Jun 2026 11:53:44 +0000 Subject: [PATCH 1/8] s/clientscope/client_scope/ --- README.md | 2 +- meta/runtime.yml | 20 +- molecule/keycloak_modules/verify.yml | 64 +-- .../identity/keycloak/keycloak.py | 438 +++++++++--------- plugins/modules/keycloak_client.py | 16 +- plugins/modules/keycloak_client_scope.py | 20 +- .../keycloak_clientscope_rolemappings.py | 68 +-- plugins/modules/keycloak_clientscope_type.py | 152 +++--- 8 files changed, 398 insertions(+), 382 deletions(-) diff --git a/README.md b/README.md index 7ea01bb..55474ed 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ All Keycloak administration modules from `community.general` are provided in thi * `keycloak_client_rolemapping`: manage client role mappings for users and groups. * `keycloak_client_rolescope`: manage client role scope mappings. * `keycloak_client_scope`: manage client scopes and protocol mappers (replaces `community.general.keycloak_clientscope`). -* `keycloak_clientscope_type`: manage default and optional client scope assignments. +* `keycloak_client_scopeee_type`: manage default and optional client scope assignments. * `keycloak_clientsecret_info`: retrieve client secret information. * `keycloak_clientsecret_regenerate`: regenerate a client secret. * `keycloak_clienttemplate`: manage legacy client templates. diff --git a/meta/runtime.yml b/meta/runtime.yml index e2907d9..2e2fc5d 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -14,8 +14,8 @@ action_groups: - keycloak_client_rolemapping - keycloak_client_rolescope - keycloak_client_scope - - keycloak_clientscope_type - - keycloak_clientscope_rolemappings + - keycloak_client_scope_type + - keycloak_client_scope_rolemappings - keycloak_clientsecret_info - keycloak_clientsecret_regenerate - keycloak_clienttemplate @@ -44,3 +44,19 @@ plugin_routing: warning_text: >- The module has been renamed to keycloak_client_scope for Keycloak 17+ (Quarkus). Update playbooks to use middleware_automation.keycloak.keycloak_client_scope. + + keycloak_clientscope_type: + redirect: middleware_automation.keycloak.keycloak_client_scope_type + deprecation: + removal_version: 5.0.0 + warning_text: >- + The module has been renamed to keycloak_client_scope_type for Keycloak 17+ (Quarkus). + Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_type. + + keycloak_clientscope_rolemappings: + redirect: middleware_automation.keycloak.keycloak_client_scope_rolemappings + deprecation: + removal_version: 5.0.0 + warning_text: >- + The module has been renamed to keycloak_client_scope_rolemappings for Keycloak 17+ (Quarkus). + Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_rolemappings. diff --git a/molecule/keycloak_modules/verify.yml b/molecule/keycloak_modules/verify.yml index 867f687..a313b24 100644 --- a/molecule/keycloak_modules/verify.yml +++ b/molecule/keycloak_modules/verify.yml @@ -37,8 +37,8 @@ - keycloak_client_rolemapping - keycloak_client_rolescope - keycloak_client_scope - - keycloak_clientscope_type - - keycloak_clientscope_rolemappings + - keycloak_client_scope_type + - keycloak_client_scope_rolemappings - keycloak_clientsecret_info - keycloak_clientsecret_regenerate - keycloak_clienttemplate @@ -265,10 +265,10 @@ - "'404' not in (clienttemplate_result.msg | default(''))" - "'Not Found' not in (clienttemplate_result.msg | default(''))" - - name: keycloak_clientscope_type — attach scope as optional on realm - middleware_automation.keycloak.keycloak_clientscope_type: + - name: keycloak_client_scope_type — attach scope as optional on realm + middleware_automation.keycloak.keycloak_client_scope_type: realm: "{{ target_realm }}" - optional_clientscopes: + optional_client_scopes: - "{{ scope }}" - name: keycloak_user_rolemapping — assign realm role to user @@ -304,49 +304,49 @@ - "{{ role }}" state: present - - name: keycloak_clientscope_rolemappings — map client roles to clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: + - name: keycloak_client_scope_rolemappings — map client roles to client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: realm: "{{ target_realm }}" client_id: "{{ client }}" - clientscope_id: "{{ scope }}" + client_scope_id: "{{ scope }}" role_names: - "{{ client_role }}" - register: clientscope_rolemappings_result + register: client_scope_rolemappings_result - - name: Assert clientscope role mappings were created + - name: Assert client scope role mappings were created ansible.builtin.assert: that: - - clientscope_rolemappings_result is changed - - clientscope_rolemappings_result.end_state | length == 1 + - client_scope_rolemappings_result is changed + - client_scope_rolemappings_result.end_state | length == 1 - - name: keycloak_clientscope_rolemappings — remap client role (idempotency) - middleware_automation.keycloak.keycloak_clientscope_rolemappings: + - name: keycloak_client_scope_rolemappings — remap client role (idempotency) + middleware_automation.keycloak.keycloak_client_scope_rolemappings: realm: "{{ target_realm }}" client_id: "{{ client }}" - clientscope_id: "{{ scope }}" + client_scope_id: "{{ scope }}" role_names: - "{{ client_role }}" - register: clientscope_rolemappings_idempotent_result + register: client_scope_rolemappings_idempotent_result - - name: Assert clientscope role mappings are idempotent + - name: Assert client scope role mappings are idempotent ansible.builtin.assert: that: - - clientscope_rolemappings_idempotent_result is not changed - - clientscope_rolemappings_idempotent_result.end_state | length == 1 + - client_scope_rolemappings_idempotent_result is not changed + - client_scope_rolemappings_idempotent_result.end_state | length == 1 - - name: keycloak_clientscope_rolemappings — map realm role to clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: + - name: keycloak_client_scope_rolemappings — map realm role to client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: realm: "{{ target_realm }}" - clientscope_id: "{{ scope }}" + client_scope_id: "{{ scope }}" role_names: - "{{ role }}" - register: clientscope_realm_rolemappings_result + register: client_scope_realm_rolemappings_result - - name: Assert realm role was mapped to clientscope + - name: Assert realm role was mapped to client_scope ansible.builtin.assert: that: - - clientscope_realm_rolemappings_result is changed - - clientscope_realm_rolemappings_result.end_state | length == 1 + - client_scope_realm_rolemappings_result is changed + - client_scope_realm_rolemappings_result.end_state | length == 1 - name: keycloak_user — set email_verified explicitly middleware_automation.keycloak.keycloak_user: @@ -517,19 +517,19 @@ name: "{{ authz_scope }}" state: absent - - name: keycloak_clientscope_rolemappings — remove realm role from clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: + - name: keycloak_client_scope_rolemappings — remove realm role from client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: realm: "{{ target_realm }}" - clientscope_id: "{{ scope }}" + client_scope_id: "{{ scope }}" role_names: - "{{ role }}" state: absent - - name: keycloak_clientscope_rolemappings — remove client role from clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: + - name: keycloak_client_scope_rolemappings — remove client role from client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: realm: "{{ target_realm }}" client_id: "{{ client }}" - clientscope_id: "{{ scope }}" + client_scope_id: "{{ scope }}" role_names: - "{{ client_role }}" state: absent diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index b0be79e..e2e9da3 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -57,23 +57,23 @@ URL_GROUPS = "{url}/admin/realms/{realm}/groups" URL_GROUP = "{url}/admin/realms/{realm}/groups/{groupid}" URL_GROUP_CHILDREN = "{url}/admin/realms/{realm}/groups/{groupid}/children" -URL_CLIENTSCOPES = "{url}/admin/realms/{realm}/client-scopes" -URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}" -URL_CLIENTSCOPE_SCOPE_MAPPINGS = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings" -URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/realm" -URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/clients/{client}" -URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models" -URL_CLIENTSCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}" +URL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/client-scopes" +URL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}" +URL_CLIENT_SCOPE_SCOPE_MAPPINGS = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings" +URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/realm" +URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/clients/{client}" +URL_CLIENT_SCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models" +URL_CLIENT_SCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}" -URL_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes" -URL_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}" -URL_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes" -URL_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}" +URL_DEFAULT_CLIENT_SCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes" +URL_DEFAULT_CLIENT_SCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}" +URL_OPTIONAL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes" +URL_OPTIONAL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}" -URL_CLIENT_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes" -URL_CLIENT_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}" -URL_CLIENT_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes" -URL_CLIENT_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}" +URL_CLIENT_DEFAULT_CLIENT_SCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes" +URL_CLIENT_DEFAULT_CLIENT_SCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}" +URL_CLIENT_OPTIONAL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes" +URL_CLIENT_OPTIONAL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}" URL_CLIENT_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}" URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = ( @@ -1289,99 +1289,99 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not delete client template {id} in realm {realm}: {e}") - def get_clientscopes(self, realm: str = "master"): - """Fetch the name and ID of all clientscopes on the Keycloak server. + def get_client_scopes(self, realm: str = "master"): + """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the group, make a subsequent call to - get_clientscope_by_clientscopeid, passing in the ID of the group you wish to return. + get_client_scope_by_client_scopeid, passing in the ID of the group you wish to return. - :param realm: Realm in which the clientscope resides; default 'master'. - :return The clientscopes of this realm (default "master") + :param realm: Realm in which the client scope resides; default 'master'. + :return The client scopes of this realm (default "master") """ - clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm) + client_scopes_url = URL_CLIENT_SCOPES.format(url=self.baseurl, realm=realm) try: - return self._request_and_deserialize(clientscopes_url, method="GET") + return self._request_and_deserialize(client_scopes_url, method="GET") except Exception as e: - self.fail_request(e, msg=f"Could not fetch list of clientscopes in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch list of client scopes in realm {realm}: {e}") - def get_clientscope_by_clientscopeid(self, cid, realm: str = "master"): - """Fetch a keycloak clientscope from the provided realm using the clientscope's unique ID. + def get_client_scope_by_client_scopeid(self, cid, realm: str = "master"): + """Fetch a keycloak client scope from the provided realm using the client scope's unique ID. - If the clientscope does not exist, None is returned. + If the client scope does not exist, None is returned. gid is a UUID provided by the Keycloak API - :param cid: UUID of the clientscope to be returned - :param realm: Realm in which the clientscope resides; default 'master'. + :param cid: UUID of the client scope to be returned + :param realm: Realm in which the client scope resides; default 'master'. """ - clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=cid) + client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=cid) try: - return self._request_and_deserialize(clientscope_url, method="GET") + return self._request_and_deserialize(client_scope_url, method="GET") except HTTPError as e: if e.code == HTTPStatus.NOT_FOUND: return None else: - self.fail_request(e, msg=f"Could not fetch clientscope {cid} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch client scope {cid} in realm {realm}: {e}") except Exception as e: - self.module.fail_json(msg=f"Could not clientscope group {cid} in realm {realm}: {e}") + self.module.fail_json(msg=f"Could not client scope group {cid} in realm {realm}: {e}") - def get_clientscope_by_name(self, name, realm: str = "master"): - """Fetch a keycloak clientscope within a realm based on its name. + def get_client_scope_by_name(self, name, realm: str = "master"): + """Fetch a keycloak client scope within a realm based on its name. - The Keycloak API does not allow filtering of the clientscopes resource by name. - As a result, this method first retrieves the entire list of clientscopes - name and ID - + The Keycloak API does not allow filtering of the client scopes resource by name. + As a result, this method first retrieves the entire list of client scopes - name and ID - then performs a second query to fetch the group. - If the clientscope does not exist, None is returned. - :param name: Name of the clientscope to fetch. - :param realm: Realm in which the clientscope resides; default 'master' + If the client scope does not exist, None is returned. + :param name: Name of the client scope to fetch. + :param realm: Realm in which the client scope resides; default 'master' """ try: - all_clientscopes = self.get_clientscopes(realm=realm) + all_client_scopes = self.get_client_scopes(realm=realm) - for clientscope in all_clientscopes: - if clientscope["name"] == name: - return self.get_clientscope_by_clientscopeid(clientscope["id"], realm=realm) + for client_scope in all_client_scopes: + if client_scope["name"] == name: + return self.get_client_scope_by_client_scopeid(client_scope["id"], realm=realm) return None except Exception as e: - self.module.fail_json(msg=f"Could not fetch clientscope {name} in realm {realm}: {e}") + self.module.fail_json(msg=f"Could not fetch client scope {name} in realm {realm}: {e}") - def create_clientscope(self, clientscoperep, realm: str = "master"): - """Create a Keycloak clientscope. + def create_client_scope(self, client_scoperep, realm: str = "master"): + """Create a Keycloak client scope. - :param clientscoperep: a ClientScopeRepresentation of the clientscope to be created. Must contain at minimum the field name. + :param client_scoperep: a ClientScopeRepresentation of the client scope to be created. Must contain at minimum the field name. :return: HTTPResponse object on success """ - clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm) + client_scopes_url = URL_CLIENT_SCOPES.format(url=self.baseurl, realm=realm) try: - return self._request(clientscopes_url, method="POST", data=json.dumps(clientscoperep)) + return self._request(client_scopes_url, method="POST", data=json.dumps(client_scoperep)) except Exception as e: - self.fail_request(e, msg=f"Could not create clientscope {clientscoperep['name']} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not create client scope {client_scoperep['name']} in realm {realm}: {e}") - def update_clientscope(self, clientscoperep, realm: str = "master"): - """Update an existing clientscope. + def update_client_scope(self, client_scoperep, realm: str = "master"): + """Update an existing client scope. :param grouprep: A GroupRepresentation of the updated group. :return HTTPResponse object on success """ - clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=clientscoperep["id"]) + client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=client_scoperep["id"]) try: - return self._request(clientscope_url, method="PUT", data=json.dumps(clientscoperep)) + return self._request(client_scope_url, method="PUT", data=json.dumps(client_scoperep)) except Exception as e: - self.fail_request(e, msg=f"Could not update clientscope {clientscoperep['name']} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not update client scope {client_scoperep['name']} in realm {realm}: {e}") - def delete_clientscope(self, name=None, cid=None, realm: str = "master"): - """Delete a clientscope. One of name or cid must be provided. + def delete_client_scope(self, name=None, cid=None, realm: str = "master"): + """Delete a client scope. One of name or cid must be provided. - Providing the clientscope ID is preferred as it avoids a second lookup to - convert a clientscope name to an ID. + Providing the client scope ID is preferred as it avoids a second lookup to + convert a client scope name to an ID. - :param name: The name of the clientscope. A lookup will be performed to retrieve the clientscope ID. - :param cid: The ID of the clientscope (preferred to name). + :param name: The name of the client scope. A lookup will be performed to retrieve the client scope ID. + :param cid: The ID of the client scope (preferred to name). :param realm: The realm in which this group resides, default "master". """ @@ -1393,9 +1393,9 @@ class KeycloakAPI: # in the case that both are provided, prefer the ID, since it is one # less lookup. if cid is None and name is not None: - for clientscope in self.get_clientscopes(realm=realm): - if clientscope["name"] == name: - cid = clientscope["id"] + for client_scope in self.get_client_scopes(realm=realm): + if client_scope["name"] == name: + cid = client_scope["id"] break # if the group doesn't exist - no problem, nothing to delete. @@ -1403,41 +1403,41 @@ class KeycloakAPI: return None # should have a good cid by here. - clientscope_url = URL_CLIENTSCOPE.format(realm=realm, id=cid, url=self.baseurl) + client_scope_url = URL_CLIENT_SCOPE.format(realm=realm, id=cid, url=self.baseurl) try: - return self._request(clientscope_url, method="DELETE") + return self._request(client_scope_url, method="DELETE") except Exception as e: - self.fail_request(e, msg=f"Unable to delete clientscope {cid}: {e}") + self.fail_request(e, msg=f"Unable to delete client scope {cid}: {e}") - def get_clientscope_protocolmappers(self, cid, realm: str = "master"): - """Fetch the name and ID of all clientscopes on the Keycloak server. + def get_client_scope_protocolmappers(self, cid, realm: str = "master"): + """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the group, make a subsequent call to - get_clientscope_by_clientscopeid, passing in the ID of the group you wish to return. + get_client_scope_by_client_scopeid, passing in the ID of the group you wish to return. - :param cid: id of clientscope (not name). - :param realm: Realm in which the clientscope resides; default 'master'. + :param cid: id of client scope (not name). + :param realm: Realm in which the client_scope resides; default 'master'. :return The protocolmappers of this realm (default "master") """ - protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm) + protocolmappers_url = URL_CLIENT_SCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm) try: return self._request_and_deserialize(protocolmappers_url, method="GET") except Exception as e: self.fail_request(e, msg=f"Could not fetch list of protocolmappers in realm {realm}: {e}") - def get_clientscope_protocolmapper_by_protocolmapperid(self, pid, cid, realm: str = "master"): - """Fetch a keycloak clientscope from the provided realm using the clientscope's unique ID. + def get_client_scope_protocolmapper_by_protocolmapperid(self, pid, cid, realm: str = "master"): + """Fetch a keycloak client scope from the provided realm using the client scope's unique ID. - If the clientscope does not exist, None is returned. + If the client scope does not exist, None is returned. gid is a UUID provided by the Keycloak API :param cid: UUID of the protocolmapper to be returned - :param cid: UUID of the clientscope to be returned - :param realm: Realm in which the clientscope resides; default 'master'. + :param cid: UUID of the client scope to be returned + :param realm: Realm in which the client scope resides; default 'master'. """ - protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid) + protocolmapper_url = URL_CLIENT_SCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid) try: return self._request_and_deserialize(protocolmapper_url, method="GET") @@ -1449,24 +1449,24 @@ class KeycloakAPI: except Exception as e: self.module.fail_json(msg=f"Could not fetch protocolmapper {cid} in realm {realm}: {e}") - def get_clientscope_protocolmapper_by_name(self, cid, name, realm: str = "master"): - """Fetch a keycloak clientscope within a realm based on its name. + def get_client_scope_protocolmapper_by_name(self, cid, name, realm: str = "master"): + """Fetch a keycloak client scope within a realm based on its name. - The Keycloak API does not allow filtering of the clientscopes resource by name. - As a result, this method first retrieves the entire list of clientscopes - name and ID - + The Keycloak API does not allow filtering of the client scopes resource by name. + As a result, this method first retrieves the entire list of client scopes - name and ID - then performs a second query to fetch the group. - If the clientscope does not exist, None is returned. - :param cid: Id of the clientscope (not name). + If the client scope does not exist, None is returned. + :param cid: Id of the client scope (not name). :param name: Name of the protocolmapper to fetch. - :param realm: Realm in which the clientscope resides; default 'master' + :param realm: Realm in which the client scope resides; default 'master' """ try: - all_protocolmappers = self.get_clientscope_protocolmappers(cid, realm=realm) + all_protocolmappers = self.get_client_scope_protocolmappers(cid, realm=realm) for protocolmapper in all_protocolmappers: if protocolmapper["name"] == name: - return self.get_clientscope_protocolmapper_by_protocolmapperid( + return self.get_client_scope_protocolmapper_by_protocolmapperid( protocolmapper["id"], cid, realm=realm ) @@ -1475,27 +1475,27 @@ class KeycloakAPI: except Exception as e: self.module.fail_json(msg=f"Could not fetch protocolmapper {name} in realm {realm}: {e}") - def create_clientscope_protocolmapper(self, cid, mapper_rep, realm: str = "master"): - """Create a Keycloak clientscope protocolmapper. + def create_client_scope_protocolmapper(self, cid, mapper_rep, realm: str = "master"): + """Create a Keycloak client scope protocolmapper. - :param cid: Id of the clientscope. + :param cid: Id of the client scope. :param mapper_rep: a ProtocolMapperRepresentation of the protocolmapper to be created. Must contain at minimum the field name. :return: HTTPResponse object on success """ - protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm) + protocolmappers_url = URL_CLIENT_SCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm) try: return self._request(protocolmappers_url, method="POST", data=json.dumps(mapper_rep)) except Exception as e: self.fail_request(e, msg=f"Could not create protocolmapper {mapper_rep['name']} in realm {realm}: {e}") - def update_clientscope_protocolmappers(self, cid, mapper_rep, realm: str = "master"): - """Update an existing clientscope. + def update_client_scope_protocolmappers(self, cid, mapper_rep, realm: str = "master"): + """Update an existing client scope. - :param cid: Id of the clientscope. + :param cid: Id of the client scope. :param mapper_rep: A ProtocolMapperRepresentation of the updated protocolmapper. :return HTTPResponse object on success """ - protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format( + protocolmapper_url = URL_CLIENT_SCOPE_PROTOCOLMAPPER.format( url=self.baseurl, realm=realm, id=cid, mapper_id=mapper_rep["id"] ) @@ -1504,137 +1504,137 @@ class KeycloakAPI: except Exception as e: self.fail_request( - e, msg=f"Could not update protocolmappers for clientscope {mapper_rep} in realm {realm}: {e}" + e, msg=f"Could not update protocolmappers for client scope {mapper_rep} in realm {realm}: {e}" ) - def get_default_clientscopes(self, realm, client_id=None): - """Fetch the name and ID of all clientscopes on the Keycloak server. + def get_default_client_scopes(self, realm, client_id=None): + """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. - :return The default clientscopes of this realm or client + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. + :return The default client scopes of this realm or client """ - url = URL_DEFAULT_CLIENTSCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENTSCOPES - return self._get_clientscopes_of_type(realm, url, "default", client_id) + url = URL_DEFAULT_CLIENT_SCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENT_SCOPES + return self._get_client_scopes_of_type(realm, url, "default", client_id) - def get_optional_clientscopes(self, realm, client_id=None): - """Fetch the name and ID of all clientscopes on the Keycloak server. + def get_optional_client_scopes(self, realm, client_id=None): + """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. - :return The optional clientscopes of this realm or client + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. + :return The optional client scopes of this realm or client """ - url = URL_OPTIONAL_CLIENTSCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENTSCOPES - return self._get_clientscopes_of_type(realm, url, "optional", client_id) + url = URL_OPTIONAL_CLIENT_SCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENT_SCOPES + return self._get_client_scopes_of_type(realm, url, "optional", client_id) - def _get_clientscopes_of_type(self, realm, url_template, scope_type, client_id=None): - """Fetch the name and ID of all clientscopes on the Keycloak server. + def _get_client_scopes_of_type(self, realm, url_template, scope_type, client_id=None): + """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. - :param realm: Realm in which the clientscope resides. + :param realm: Realm in which the client scope resides. :param url_template the template for the right type :param scope_type this can be either optional or default - :param client_id: The client in which the clientscope resides. - :return The clientscopes of the specified type of this realm + :param client_id: The client in which the client scope resides. + :return The client scopes of the specified type of this realm """ if client_id is None: - clientscopes_url = url_template.format(url=self.baseurl, realm=realm) + client_scopes_url = url_template.format(url=self.baseurl, realm=realm) try: - return self._request_and_deserialize(clientscopes_url, method="GET") + return self._request_and_deserialize(client_scopes_url, method="GET") except Exception as e: - self.fail_request(e, msg=f"Could not fetch list of {scope_type} clientscopes in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch list of {scope_type} client scopes in realm {realm}: {e}") else: cid = self.get_client_id(client_id=client_id, realm=realm) - clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid) + client_scopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid) try: - return self._request_and_deserialize(clientscopes_url, method="GET") + return self._request_and_deserialize(client_scopes_url, method="GET") except Exception as e: self.fail_request( e, - msg=f"Could not fetch list of {scope_type} clientscopes in client {client_id}: {clientscopes_url}", + msg=f"Could not fetch list of {scope_type} client scopes in client {client_id}: {client_scopes_url}", ) - def _decide_url_type_clientscope(self, client_id=None, scope_type="default"): + def _decide_url_type_client_scope(self, client_id=None, scope_type="default"): """Decides which url to use. :param scope_type this can be either optional or default - :param client_id: The client in which the clientscope resides. + :param client_id: The client in which the client scope resides. """ if client_id is None: if scope_type == "default": - return URL_DEFAULT_CLIENTSCOPE + return URL_DEFAULT_CLIENT_SCOPE if scope_type == "optional": - return URL_OPTIONAL_CLIENTSCOPE + return URL_OPTIONAL_CLIENT_SCOPE else: if scope_type == "default": - return URL_CLIENT_DEFAULT_CLIENTSCOPE + return URL_CLIENT_DEFAULT_CLIENT_SCOPE if scope_type == "optional": - return URL_CLIENT_OPTIONAL_CLIENTSCOPE + return URL_CLIENT_OPTIONAL_CLIENT_SCOPE - def add_default_clientscope(self, id, realm: str = "master", client_id=None): + def add_default_client_scope(self, id, realm: str = "master", client_id=None): """Add a client scope as default either on realm or client level. :param id: Client scope Id. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. """ - self._action_type_clientscope(id, client_id, "default", realm, "add") + self._action_type_client_scope(id, client_id, "default", realm, "add") - def add_optional_clientscope(self, id, realm: str = "master", client_id=None): + def add_optional_client_scope(self, id, realm: str = "master", client_id=None): """Add a client scope as optional either on realm or client level. :param id: Client scope Id. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. """ - self._action_type_clientscope(id, client_id, "optional", realm, "add") + self._action_type_client_scope(id, client_id, "optional", realm, "add") - def delete_default_clientscope(self, id, realm: str = "master", client_id=None): + def delete_default_client_scope(self, id, realm: str = "master", client_id=None): """Remove a client scope as default either on realm or client level. :param id: Client scope Id. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. """ - self._action_type_clientscope(id, client_id, "default", realm, "delete") + self._action_type_client_scope(id, client_id, "default", realm, "delete") - def delete_optional_clientscope(self, id, realm: str = "master", client_id=None): + def delete_optional_client_scope(self, id, realm: str = "master", client_id=None): """Remove a client scope as optional either on realm or client level. :param id: Client scope Id. - :param realm: Realm in which the clientscope resides. - :param client_id: The client in which the clientscope resides. + :param realm: Realm in which the client scope resides. + :param client_id: The client in which the client scope resides. """ - self._action_type_clientscope(id, client_id, "optional", realm, "delete") + self._action_type_client_scope(id, client_id, "optional", realm, "delete") - def _action_type_clientscope( + def _action_type_client_scope( self, id=None, client_id=None, scope_type="default", realm: str = "master", action="add" ): - """Delete or add a clientscope of type. - :param name: The name of the clientscope. A lookup will be performed to retrieve the clientscope ID. - :param client_id: The ID of the clientscope (preferred to name). + """Delete or add a client scope of type. + :param name: The name of the client scope. A lookup will be performed to retrieve the client scope ID. + :param client_id: The ID of the client scope (preferred to name). :param scope_type 'default' or 'optional' :param realm: The realm in which this group resides, default "master". """ cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm) # should have a good cid by here. - clientscope_type_url = self._decide_url_type_clientscope(client_id, scope_type).format( + client_scope_type_url = self._decide_url_type_client_scope(client_id, scope_type).format( realm=realm, id=id, cid=cid, url=self.baseurl ) try: method = "PUT" if action == "add" else "DELETE" - return self._request(clientscope_type_url, method=method) + return self._request(client_scope_type_url, method=method) except Exception as e: place = "realm" if client_id is None else f"client {client_id}" - self.fail_request(e, msg=f"Unable to {action} {scope_type} clientscope {id} @ {place} : {e}") + self.fail_request(e, msg=f"Unable to {action} {scope_type} client_scope {id} @ {place} : {e}") def create_clientsecret(self, id, realm: str = "master"): """Generate a new client secret by id @@ -3259,77 +3259,77 @@ class KeycloakAPI: except Exception: return False - def get_all_clientscope_scope_mappings(self, clientscope_id, realm: str = "master"): - """Fetch all (realm and client) roles (scope-mappings) associated with the clientscope for a specific clientscope on the Keycloak server. - :param clientscope_id: ID of the clientscope from which to obtain the associated roles. + def get_all_client_scope_scope_mappings(self, client_scope_id, realm: str = "master"): + """Fetch all (realm and client) roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server. + :param client_scope_id: ID of the client scope from which to obtain the associated roles. :param realm: Realm from which to obtain the scope. - :return: The clientscope scope-mappings. + :return: The client scope scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS.format(url=self.baseurl, realm=realm, id=clientscope_id) + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS.format(url=self.baseurl, realm=realm, id=client_scope_id) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: - self.fail_request(e, msg=f"Could not fetch roles for client-scope {clientscope_id} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch roles for client-scope {client_scope_id} in realm {realm}: {e}") - def get_clientscope_scope_mappings_realm(self, clientscope_id, realm: str = "master"): - """Fetch the realm roles (scope-mappings) associated with the clientscope for a specific clientscope on the Keycloak server. - :param clientscope_id: ID of the clientscope from which to obtain the associated roles. + def get_client_scope_scope_mappings_realm(self, client_scope_id, realm: str = "master"): + """Fetch the realm roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server. + :param client_scope_id: ID of the client scope from which to obtain the associated roles. :param realm: Realm from which to obtain the scope. - :return: The clientscope realm scope-mappings. + :return: The client scope realm scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format( - url=self.baseurl, realm=realm, id=clientscope_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format( + url=self.baseurl, realm=realm, id=client_scope_id ) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: self.fail_request( - e, msg=f"Could not fetch realm roles for client-scope {clientscope_id} in realm {realm}: {e}" + e, msg=f"Could not fetch realm roles for client-scope {client_scope_id} in realm {realm}: {e}" ) - def get_clientscope_scope_mappings_client(self, clientscope_id, client_id, realm: str = "master"): - """Fetch the client roles (scope-mappings) associated with the clientscope for a specific clientscope and client on the Keycloak server. - :param clientscope_id: ID of the clientscope from which to obtain the associated roles. + def get_client_scope_scope_mappings_client(self, client_scope_id, client_id, realm: str = "master"): + """Fetch the client roles (scope-mappings) associated with the client scope for a specific client scope and client on the Keycloak server. + :param client_scope_id: ID of the client scope from which to obtain the associated roles. :param clientid: ID of the client from which to obtain the associated roles. :param realm: Realm from which to obtain the scope. - :return: The clientscope client scope-mappings. + :return: The client scope client scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format( - url=self.baseurl, realm=realm, id=clientscope_id, client=client_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format( + url=self.baseurl, realm=realm, id=client_scope_id, client=client_id ) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: self.fail_request( e, - msg=f"Could not fetch client roles from client {client_id} for client-scope {clientscope_id} in realm {realm}: {e}", + msg=f"Could not fetch client roles from client {client_id} for client-scope {client_scope_id} in realm {realm}: {e}", ) - def get_client_role_scope_from_client(self, clientid, clientscopeid, realm: str = "master"): + def get_client_role_scope_from_client(self, clientid, client_scopeid, realm: str = "master"): """Fetch the roles associated with the client's scope for a specific client on the Keycloak server. :param clientid: ID of the client from which to obtain the associated roles. - :param clientscopeid: ID of the client who owns the roles. + :param client_scopeid: ID of the client who owns the roles. :param realm: Realm from which to obtain the scope. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid ) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}") - def update_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm: str = "master"): + def update_client_role_scope_from_client(self, payload, clientid, client_scopeid, realm: str = "master"): """Update and fetch the roles associated with the client's scope on the Keycloak server. :param payload: List of roles to be added to the scope. :param clientid: ID of the client to update scope. - :param clientscopeid: ID of the client who owns the roles. + :param client_scopeid: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid ) try: self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) @@ -3337,18 +3337,18 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, clientscopeid, realm) + return self.get_client_role_scope_from_client(clientid, client_scopeid, realm) - def delete_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm: str = "master"): + def delete_client_role_scope_from_client(self, payload, clientid, client_scopeid, realm: str = "master"): """Delete the roles contains in the payload from the client's scope on the Keycloak server. :param payload: List of roles to be deleted. :param clientid: ID of the client to delete roles from scope. - :param clientscopeid: ID of the client who owns the roles. + :param client_scopeid: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid ) try: self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) @@ -3356,20 +3356,20 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, clientscopeid, realm) + return self.get_client_role_scope_from_client(clientid, client_scopeid, realm) - def update_clientscope_scope_mappings_client( - self, payload: list[dict], clientscope_id: str, client_id: str, realm: str = "master" + def update_client_scope_scope_mappings_client( + self, payload: list[dict], client_scope_id: str, client_id: str, realm: str = "master" ): - """Update and fetch the client roles (scope-mappings) associated with the clientscope on the Keycloak server. + """Update and fetch the client roles (scope-mappings) associated with the client scope on the Keycloak server. :param payload: List of client roles to be added to the scope. - :param clientscope_id: ID of the clientscope to update scope-mappings. + :param client_scope_id: ID of the client scope to update scope-mappings. :param clientid: ID of the client from which to obtain the associated roles. :param realm: Realm from which to obtain the client. - :return: The clientscope client scope-mappings. + :return: The client scope client scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format( - url=self.baseurl, realm=realm, id=clientscope_id, client=client_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format( + url=self.baseurl, realm=realm, id=client_scope_id, client=client_id ) try: self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) @@ -3377,43 +3377,43 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not update scope mappings for client-scope {client_id}.{clientscope_id} in realm {realm}: {e}", + msg=f"Could not update scope mappings for client-scope {client_id}.{client_scope_id} in realm {realm}: {e}", ) - return self.get_clientscope_scope_mappings_client(clientscope_id, client_id, realm) + return self.get_client_scope_scope_mappings_client(client_scope_id, client_id, realm) - def update_clientscope_scope_mappings_realm(self, payload: list[dict], clientscope_id: str, realm: str = "master"): - """Update and fetch the realm roles (scope-mappings) associated with the clientscope on the Keycloak server. + def update_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): + """Update and fetch the realm roles (scope-mappings) associated with the client scope on the Keycloak server. :param payload: List of realm roles to be added to the scope. - :param clientscope_id: ID of the clientscope to update scope-mappings. + :param client_scope_id: ID of the client scope to update scope-mappings. :param realm: Realm from which to obtain the roles. - :return: The clientscope realm scope-mappings. + :return: The client scope realm scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format( - url=self.baseurl, realm=realm, id=clientscope_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format( + url=self.baseurl, realm=realm, id=client_scope_id ) try: self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) except Exception as e: self.fail_request( - e, msg=f"Could not update scope mappings for client-scope {clientscope_id} in realm {realm}: {e}" + e, msg=f"Could not update scope mappings for client scope {client_scope_id} in realm {realm}: {e}" ) - return self.get_clientscope_scope_mappings_realm(clientscope_id, realm) + return self.get_client_scope_scope_mappings_realm(client_scope_id, realm) - def delete_clientscope_scope_mappings_client( - self, payload: list[dict], clientscope_id: str, client_id: str, realm: str = "master" + def delete_client_scope_scope_mappings_client( + self, payload: list[dict], client_scope_id: str, client_id: str, realm: str = "master" ): - """Delete the client roles (scope_mappings) contained in the payload from the clientscope on the Keycloak server. + """Delete the client roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. :param payload: List of roles to be deleted. - :param clientscope_id: ID of the clientscope to delete roles from scope-mappings. + :param client_scope_id: ID of the client scope to delete roles from scope-mappings. :param clientid: ID of the client who owns the roles. :param realm: Realm from which to obtain the client. - :return: The clientscope client scope-mappings. + :return: The client scope client scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format( - url=self.baseurl, realm=realm, id=clientscope_id, client=client_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format( + url=self.baseurl, realm=realm, id=client_scope_id, client=client_id ) try: self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) @@ -3421,30 +3421,30 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not delete scope mappings for client-scope {client_id}.{clientscope_id} in realm {realm}: {e}", + msg=f"Could not delete scope mappings for client scope {client_id}.{client_scope_id} in realm {realm}: {e}", ) - return self.get_clientscope_scope_mappings_client(clientscope_id, client_id, realm) + return self.get_client_scope_scope_mappings_client(client_scope_id, client_id, realm) - def delete_clientscope_scope_mappings_realm(self, payload: list[dict], clientscope_id: str, realm: str = "master"): - """Delete the realm roles (scope_mappings) contained in the payload from the clientscope on the Keycloak server. + def delete_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): + """Delete the realm roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. :param payload: List of roles to be deleted. - :param clientscope_id: ID of the clientscope to delete roles from scope-mappings. + :param client_scope_id: ID of the client scope to delete roles from scope-mappings. :param realm: Realm from which to obtain the roles. - :return: The clientscope realm scope-mappings. + :return: The client scope realm scope-mappings. """ - client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format( - url=self.baseurl, realm=realm, id=clientscope_id + client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format( + url=self.baseurl, realm=realm, id=client_scope_id ) try: self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) except Exception as e: self.fail_request( - e, msg=f"Could not delete scope mappings for client-scope {clientscope_id} in realm {realm}: {e}" + e, msg=f"Could not delete scope mappings for client-scope {client_scope_id} in realm {realm}: {e}" ) - return self.get_clientscope_scope_mappings_realm(clientscope_id, realm) + return self.get_client_scope_scope_mappings_realm(client_scope_id, realm) def get_client_role_scope_from_realm(self, clientid, realm: str = "master"): """Fetch the realm roles from the client's scope on the Keycloak server. diff --git a/plugins/modules/keycloak_client.py b/plugins/modules/keycloak_client.py index 9a15881..0407a13 100644 --- a/plugins/modules/keycloak_client.py +++ b/plugins/modules/keycloak_client.py @@ -1100,11 +1100,11 @@ def add_default_client_scopes(desired_client, before_client, realm, kc): missing_scopes = [item for item in desired_default_scope if item not in before_client["defaultClientScopes"]] if not missing_scopes: return - client_scopes = kc.get_clientscopes(realm) + client_scopes = kc.get_client_scopes(realm) for name in missing_scopes: scope = find_match(client_scopes, "name", name) if scope: - kc.add_default_clientscope(scope["id"], realm, desired_client["clientId"]) + kc.add_default_client_scope(scope["id"], realm, desired_client["clientId"]) def add_optional_client_scopes(desired_client, before_client, realm, kc): @@ -1139,11 +1139,11 @@ def add_optional_client_scopes(desired_client, before_client, realm, kc): missing_scopes = [item for item in desired_optional_scope if item not in before_client["optionalClientScopes"]] if not missing_scopes: return - client_scopes = kc.get_clientscopes(realm) + client_scopes = kc.get_client_scopes(realm) for name in missing_scopes: scope = find_match(client_scopes, "name", name) if scope: - kc.add_optional_clientscope(scope["id"], realm, desired_client["clientId"]) + kc.add_optional_client_scope(scope["id"], realm, desired_client["clientId"]) def remove_default_client_scopes(desired_client, before_client, realm, kc): @@ -1178,11 +1178,11 @@ def remove_default_client_scopes(desired_client, before_client, realm, kc): missing_scopes = [item for item in before_default_scope if item not in desired_client["defaultClientScopes"]] if not missing_scopes: return - client_scopes = kc.get_default_clientscopes(realm, desired_client["clientId"]) + client_scopes = kc.get_default_client_scopes(realm, desired_client["clientId"]) for name in missing_scopes: scope = find_match(client_scopes, "name", name) if scope: - kc.delete_default_clientscope(scope["id"], realm, desired_client["clientId"]) + kc.delete_default_client_scope(scope["id"], realm, desired_client["clientId"]) def remove_optional_client_scopes(desired_client, before_client, realm, kc): @@ -1217,11 +1217,11 @@ def remove_optional_client_scopes(desired_client, before_client, realm, kc): missing_scopes = [item for item in before_optional_scope if item not in desired_client["optionalClientScopes"]] if not missing_scopes: return - client_scopes = kc.get_optional_clientscopes(realm, desired_client["clientId"]) + client_scopes = kc.get_optional_client_scopes(realm, desired_client["clientId"]) for name in missing_scopes: scope = find_match(client_scopes, "name", name) if scope: - kc.delete_optional_clientscope(scope["id"], realm, desired_client["clientId"]) + kc.delete_optional_client_scope(scope["id"], realm, desired_client["clientId"]) def main(): diff --git a/plugins/modules/keycloak_client_scope.py b/plugins/modules/keycloak_client_scope.py index 35723c9..b2dfccd 100644 --- a/plugins/modules/keycloak_client_scope.py +++ b/plugins/modules/keycloak_client_scope.py @@ -230,7 +230,7 @@ def main(): attributes = module.params.get('attributes') protocol_mappers = module.params.get('protocol_mappers') - before_scope = kc.get_clientscope_by_name(name, realm=realm) + before_scope = kc.get_client_scope_by_name(name, realm=realm) if state == 'absent': if before_scope: @@ -239,7 +239,7 @@ def main(): result['diff'] = dict(before=before_scope, after='') if module.check_mode: module.exit_json(**result) - kc.delete_clientscope(cid=before_scope['id'], realm=realm) + kc.delete_client_scope(cid=before_scope['id'], realm=realm) result['msg'] = "Client scope {name} has been deleted".format(name=name) else: result['msg'] = "Client scope {name} does not exist, doing nothing".format(name=name) @@ -261,8 +261,8 @@ def main(): if module.check_mode: module.exit_json(**result) - kc.create_clientscope(scope_rep, realm=realm) - after_scope = kc.get_clientscope_by_name(name, realm=realm) + kc.create_client_scope(scope_rep, realm=realm) + after_scope = kc.get_client_scope_by_name(name, realm=realm) if protocol_mappers: for mapper in protocol_mappers: @@ -272,8 +272,8 @@ def main(): 'protocolMapper': mapper['protocolMapper'], 'config': mapper['config'], } - kc.create_clientscope_protocolmapper(after_scope['id'], mapper_rep, realm=realm) - after_scope = kc.get_clientscope_by_name(name, realm=realm) + kc.create_client_scope_protocolmapper(after_scope['id'], mapper_rep, realm=realm) + after_scope = kc.get_client_scope_by_name(name, realm=realm) result['end_state'] = after_scope result['msg'] = "Client scope {name} has been created".format(name=name) @@ -296,10 +296,10 @@ def main(): result['diff'] = dict(before=before_scope, after=scope_rep) if module.check_mode: module.exit_json(**result) - kc.update_clientscope(scope_rep, realm=realm) + kc.update_client_scope(scope_rep, realm=realm) if protocol_mappers: - existing_mappers = kc.get_clientscope_protocolmappers(before_scope['id'], realm=realm) + existing_mappers = kc.get_client_scope_protocolmappers(before_scope['id'], realm=realm) existing_mapper_names = {m['name'] for m in existing_mappers} for mapper in protocol_mappers: @@ -312,9 +312,9 @@ def main(): 'protocolMapper': mapper['protocolMapper'], 'config': mapper['config'], } - kc.create_clientscope_protocolmapper(before_scope['id'], mapper_rep, realm=realm) + kc.create_client_scope_protocolmapper(before_scope['id'], mapper_rep, realm=realm) - after_scope = kc.get_clientscope_by_name(name, realm=realm) + after_scope = kc.get_client_scope_by_name(name, realm=realm) result['end_state'] = after_scope if result['changed']: diff --git a/plugins/modules/keycloak_clientscope_rolemappings.py b/plugins/modules/keycloak_clientscope_rolemappings.py index be89a07..a89241d 100644 --- a/plugins/modules/keycloak_clientscope_rolemappings.py +++ b/plugins/modules/keycloak_clientscope_rolemappings.py @@ -6,16 +6,16 @@ from __future__ import annotations DOCUMENTATION = r""" -module: keycloak_clientscope_rolemappings +module: keycloak_client_scope_rolemappings -short_description: Allows administration of Keycloak clientscope scope mappings to restrict the usage of certain roles to - specific clientscopes +short_description: Allows administration of Keycloak client scope scope mappings to restrict the usage of certain roles to + specific client scopes # Originally added in community.general 13.1.0 version_added: "3.0.0" description: - - This module allows you to add or remove Keycloak roles from clientscopes using the Keycloak REST API. It requires access + - This module allows you to add or remove Keycloak roles from client scopes using the Keycloak REST API. It requires access to the REST API using OpenID Connect; the user connecting and the client being used must have the requisite access rights. In a default Keycloak installation, C(admin-cli) and an admin user would work, as would a separate client definition with the scope tailored to your needs and a user having the expected roles. @@ -49,11 +49,11 @@ options: - The Keycloak realm under which clients resides. default: 'master' - clientscope_id: + client_scope_id: required: true type: str description: - - Roles provided in O(role_names) will be added to this clientscope. + - Roles provided in O(role_names) will be added to this client scope. client_id: type: str @@ -81,40 +81,40 @@ author: """ EXAMPLES = r""" -- name: Add roles to clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: +- name: Add roles to client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: auth_keycloak_url: https://auth.example.com auth_realm: master auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm client_id: frontend-client-public - clientscope_id: frontend-clientscope + client_scope_id: frontend-client-scope role_names: - backend-role-admin - backend-role-user -- name: Remove roles from clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: +- name: Remove roles from client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: auth_keycloak_url: https://auth.example.com auth_realm: master auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm client_id: frontend-client-public - clientscope_id: frontend-clientscope + client_scope_id: frontend-client-scope role_names: - backend-role-admin state: absent -- name: Add realm roles to clientscope - middleware_automation.keycloak.keycloak_clientscope_rolemappings: +- name: Add realm roles to client scope + middleware_automation.keycloak.keycloak_client_scope_rolemappings: auth_keycloak_url: https://auth.example.com auth_realm: master auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm - clientscope_id: frontend-clientscope + client_scope_id: frontend-client-scope role_names: - realm-role-admin - realm-role-user @@ -122,7 +122,7 @@ EXAMPLES = r""" RETURN = r""" end_state: - description: Representation of clientscope scope mappings after module execution. + description: Representation of client scope scope mappings after module execution. returned: on success type: list elements: dict @@ -164,7 +164,7 @@ def main(): meta_args = dict( client_id=dict(type="str"), - clientscope_id=dict(type="str", required=True), + client_scope_id=dict(type="str", required=True), realm=dict(type="str", default="master"), role_names=dict(type="list", elements="str", required=True), state=dict(type="str", default="present", choices=["present", "absent"]), @@ -186,7 +186,7 @@ def main(): realm = module.params["realm"] client_id = module.params["client_id"] - clientscope_id = module.params["clientscope_id"] + client_scope_id = module.params["client_scope_id"] role_names = module.params["role_names"] state = module.params["state"] @@ -194,9 +194,9 @@ def main(): if not realm_object: module.fail_json(msg=f"Failed to retrieve realm '{realm}'") - clientscope_object = kc.get_clientscope_by_name(clientscope_id, realm) - if not clientscope_object: - module.fail_json(msg=f"Failed to retrieve client-scope '{clientscope_id}'") + client_scope_object = kc.get_client_scope_by_name(client_scope_id, realm) + if not client_scope_object: + module.fail_json(msg=f"Failed to retrieve client-scope '{client_scope_id}'") if client_id: # add client role @@ -206,11 +206,11 @@ def main(): if client_object["fullScopeAllowed"] and state == "present": module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{client_id}'") - before_roles = kc.get_clientscope_scope_mappings_client(clientscope_object["id"], client_object["id"], realm) + before_roles = kc.get_client_scope_scope_mappings_client(client_scope_object["id"], client_object["id"], realm) available_roles_by_name = kc.get_client_roles_by_id(client_object["id"], realm) else: # add realm role - before_roles = kc.get_clientscope_scope_mappings_realm(clientscope_object["id"], realm) + before_roles = kc.get_client_scope_scope_mappings_realm(client_scope_object["id"], realm) available_roles_by_name = kc.get_realm_roles(realm) # convert to indexed Dict by name @@ -248,33 +248,33 @@ def main(): if not result["changed"]: # no changes result["end_state"] = before_roles - result["msg"] = f"No changes required for clientscope {clientscope_id}." + result["msg"] = f"No changes required for client scope {client_scope_id}." elif state == "present": # doing update if module.check_mode: result["end_state"] = desired_role_mapping elif client_id: - result["end_state"] = kc.update_clientscope_scope_mappings_client( - changed_roles, clientscope_object["id"], client_object["id"], realm + result["end_state"] = kc.update_client_scope_scope_mappings_client( + changed_roles, client_scope_object["id"], client_object["id"], realm ) else: - result["end_state"] = kc.update_clientscope_scope_mappings_realm( - changed_roles, clientscope_object["id"], realm + result["end_state"] = kc.update_client_scope_scope_mappings_realm( + changed_roles, client_scope_object["id"], realm ) - result["msg"] = f"Clientscope scope mappings for {clientscope_id} have been updated" + result["msg"] = f"Clientscope scope mappings for {client_scope_id} have been updated" else: # doing delete if module.check_mode: result["end_state"] = desired_role_mapping elif client_id: - result["end_state"] = kc.delete_clientscope_scope_mappings_client( - changed_roles, clientscope_object["id"], client_object["id"], realm + result["end_state"] = kc.delete_client_scope_scope_mappings_client( + changed_roles, client_scope_object["id"], client_object["id"], realm ) else: - result["end_state"] = kc.delete_clientscope_scope_mappings_realm( - changed_roles, clientscope_object["id"], realm + result["end_state"] = kc.delete_client_scope_scope_mappings_realm( + changed_roles, client_scope_object["id"], realm ) - result["msg"] = f"Clientscope scope mappings for {clientscope_id} have been deleted" + result["msg"] = f"Clientscope scope mappings for {client_scope_id} have been deleted" module.exit_json(**result) diff --git a/plugins/modules/keycloak_clientscope_type.py b/plugins/modules/keycloak_clientscope_type.py index 6c467a3..00cbd05 100644 --- a/plugins/modules/keycloak_clientscope_type.py +++ b/plugins/modules/keycloak_clientscope_type.py @@ -6,15 +6,15 @@ from __future__ import annotations DOCUMENTATION = r""" -module: keycloak_clientscope_type +module: keycloak_client_scope_type -short_description: Set the type of aclientscope in realm or client using Keycloak API +short_description: Set the type of a client scope in a realm or client using the Keycloak API # Originally added in community.general 6.6.0 version_added: "3.0.0" description: - - This module allows you to set the type (optional, default) of clientscopes using the Keycloak REST API. It requires access + - This module allows you to set the type (optional, default) of client scopes using the Keycloak REST API. It requires access to the REST API using OpenID Connect; the user connecting and the client being used must have the requisite access rights. In a default Keycloak installation, admin-cli and an admin user would work, as would a separate client definition with the scope tailored to your needs and a user having the expected roles. @@ -36,18 +36,18 @@ options: client_id: description: - - The O(client_id) of the client. If not set the clientscope types are set as a default for the realm. + - The O(client_id) of the client. If not set the client scope types are set as a default for the realm. aliases: - clientId type: str - default_clientscopes: + default_client_scopes: description: - Client scopes that should be of type default. type: list elements: str - optional_clientscopes: + optional_client_scopes: description: - Client scopes that should be of type optional. type: list @@ -64,26 +64,26 @@ author: EXAMPLES = r""" - name: Set default client scopes on realm level - middleware_automation.keycloak.keycloak_clientscope_type: + middleware_automation.keycloak.keycloak_client_scope_type: auth_client_id: admin-cli auth_keycloak_url: https://auth.example.com auth_realm: master auth_username: USERNAME auth_password: PASSWORD realm: "MyCustomRealm" - default_clientscopes: ['profile', 'roles'] + default_client_scopes: ['profile', 'roles'] delegate_to: localhost - name: Set default and optional client scopes on client level with token auth - middleware_automation.keycloak.keycloak_clientscope_type: + middleware_automation.keycloak.keycloak_client_scope_type: auth_client_id: admin-cli auth_keycloak_url: https://auth.example.com token: TOKEN realm: "MyCustomRealm" client_id: "MyCustomClient" - default_clientscopes: ['profile', 'roles'] - optional_clientscopes: ['phone'] + default_client_scopes: ['profile', 'roles'] + optional_client_scopes: ['phone'] delegate_to: localhost """ @@ -99,11 +99,11 @@ proposed: type: dict sample: { - "default_clientscopes": [ + "default_client_scopes": [ "profile", "role" ], - "optional_clientscopes": [] + "optional_client_scopes": [] } existing: description: @@ -112,11 +112,11 @@ existing: type: dict sample: { - "default_clientscopes": [ + "default_client_scopes": [ "profile", "role" ], - "optional_clientscopes": [ + "optional_client_scopes": [ "phone" ] } @@ -128,11 +128,11 @@ end_state: type: dict sample: { - "default_clientscopes": [ + "default_client_scopes": [ "profile", "role" ], - "optional_clientscopes": [] + "optional_client_scopes": [] } """ @@ -146,7 +146,7 @@ from ansible_collections.middleware_automation.keycloak.plugins.module_utils.ide ) -def keycloak_clientscope_type_module(): +def keycloak_client_scope_type_module(): """ Returns an AnsibleModule definition. @@ -157,8 +157,8 @@ def keycloak_clientscope_type_module(): meta_args = dict( realm=dict(default="master"), client_id=dict(type="str", aliases=["clientId"]), - default_clientscopes=dict(type="list", elements="str"), - optional_clientscopes=dict(type="list", elements="str"), + default_client_scopes=dict(type="list", elements="str"), + optional_client_scopes=dict(type="list", elements="str"), ) argument_spec.update(meta_args) @@ -169,7 +169,7 @@ def keycloak_clientscope_type_module(): required_one_of=( [ ["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"], - ["default_clientscopes", "optional_clientscopes"], + ["default_client_scopes", "optional_client_scopes"], ] ), required_together=([["auth_username", "auth_password"]]), @@ -180,21 +180,21 @@ def keycloak_clientscope_type_module(): return module -def clientscopes_to_add(existing, proposed): +def client_scopes_to_add(existing, proposed): to_add = [] - existing_clientscope_ids = extract_field(existing, "id") - for clientscope in proposed: - if clientscope["id"] not in existing_clientscope_ids: - to_add.append(clientscope) + existing_client_scope_ids = extract_field(existing, "id") + for client_scope in proposed: + if client_scope["id"] not in existing_client_scope_ids: + to_add.append(client_scope) return to_add -def clientscopes_to_delete(existing, proposed): +def client_scopes_to_delete(existing, proposed): to_delete = [] - proposed_clientscope_ids = extract_field(proposed, "id") - for clientscope in existing: - if clientscope["id"] not in proposed_clientscope_ids: - to_delete.append(clientscope) + proposed_client_scope_ids = extract_field(proposed, "id") + for client_scope in existing: + if client_scope["id"] not in proposed_client_scope_ids: + to_delete.append(client_scope) return to_delete @@ -204,21 +204,21 @@ def extract_field(dictionary, field="name"): def normalize_scopes(scopes): scopes_copy = scopes.copy() - if isinstance(scopes_copy.get("default_clientscopes"), list): - scopes_copy["default_clientscopes"] = sorted(scopes_copy["default_clientscopes"]) - if isinstance(scopes_copy.get("optional_clientscopes"), list): - scopes_copy["optional_clientscopes"] = sorted(scopes_copy["optional_clientscopes"]) + if isinstance(scopes_copy.get("default_client_scopes"), list): + scopes_copy["default_client_scopes"] = sorted(scopes_copy["default_client_scopes"]) + if isinstance(scopes_copy.get("optional_client_scopes"), list): + scopes_copy["optional_client_scopes"] = sorted(scopes_copy["optional_client_scopes"]) return scopes_copy def main(): """ - Module keycloak_clientscope_type + Module keycloak_client_scope_type :return: """ - module = keycloak_clientscope_type_module() + module = keycloak_client_scope_type_module() # Obtain access token, initialize API try: @@ -230,81 +230,81 @@ def main(): realm = module.params.get("realm") client_id = module.params.get("client_id") - default_clientscopes = module.params.get("default_clientscopes") - optional_clientscopes = module.params.get("optional_clientscopes") + default_client_scopes = module.params.get("default_client_scopes") + optional_client_scopes = module.params.get("optional_client_scopes") result = dict(changed=False, msg="", proposed={}, existing={}, end_state={}) - all_clientscopes = kc.get_clientscopes(realm) - default_clientscopes_real = [] - optional_clientscopes_real = [] + all_client_scopes = kc.get_client_scopes(realm) + default_client_scopes_real = [] + optional_client_scopes_real = [] - for client_scope in all_clientscopes: - if default_clientscopes is not None and client_scope["name"] in default_clientscopes: - default_clientscopes_real.append(client_scope) - if optional_clientscopes is not None and client_scope["name"] in optional_clientscopes: - optional_clientscopes_real.append(client_scope) + for client_scope in all_client_scopes: + if default_client_scopes is not None and client_scope["name"] in default_client_scopes: + default_client_scopes_real.append(client_scope) + if optional_client_scopes is not None and client_scope["name"] in optional_client_scopes: + optional_client_scopes_real.append(client_scope) - if default_clientscopes is not None and len(default_clientscopes_real) != len(default_clientscopes): - module.fail_json(msg="At least one of the default_clientscopes does not exist!") + if default_client_scopes is not None and len(default_client_scopes_real) != len(default_client_scopes): + module.fail_json(msg="At least one of the default_client_scopes does not exist!") - if optional_clientscopes is not None and len(optional_clientscopes_real) != len(optional_clientscopes): - module.fail_json(msg="At least one of the optional_clientscopes does not exist!") + if optional_client_scopes is not None and len(optional_client_scopes_real) != len(optional_client_scopes): + module.fail_json(msg="At least one of the optional_client_scopes does not exist!") result["proposed"].update( { - "default_clientscopes": "no-change" if default_clientscopes is None else default_clientscopes, - "optional_clientscopes": "no-change" if optional_clientscopes is None else optional_clientscopes, + "default_client_scopes": "no-change" if default_client_scopes is None else default_client_scopes, + "optional_client_scopes": "no-change" if optional_client_scopes is None else optional_client_scopes, } ) - default_clientscopes_existing = kc.get_default_clientscopes(realm, client_id) - optional_clientscopes_existing = kc.get_optional_clientscopes(realm, client_id) + default_client_scopes_existing = kc.get_default_client_scopes(realm, client_id) + optional_client_scopes_existing = kc.get_optional_client_scopes(realm, client_id) result["existing"].update( { - "default_clientscopes": extract_field(default_clientscopes_existing), - "optional_clientscopes": extract_field(optional_clientscopes_existing), + "default_client_scopes": extract_field(default_client_scopes_existing), + "optional_client_scopes": extract_field(optional_client_scopes_existing), } ) if module._diff: result["diff"] = dict(before=normalize_scopes(result["existing"]), after=normalize_scopes(result["proposed"])) - default_clientscopes_add = clientscopes_to_add(default_clientscopes_existing, default_clientscopes_real) - optional_clientscopes_add = clientscopes_to_add(optional_clientscopes_existing, optional_clientscopes_real) + default_client_scopes_add = client_scopes_to_add(default_client_scopees_existing, defaultclient_scopees_real) + optional_client_scopes_add = client_scopes_to_add(optional_client_scopes_existing, optional_client_scopes_real) - default_clientscopes_delete = clientscopes_to_delete(default_clientscopes_existing, default_clientscopes_real) - optional_clientscopes_delete = clientscopes_to_delete(optional_clientscopes_existing, optional_clientscopes_real) + default_client_scopes_delete = client_scopes_to_delete(default_client_scopes_existing, default_client_scopes_real) + optional_client_scopes_delete = client_scopes_to_delete(optional_client_scopes_existing, optional_client_scopes_real) result["changed"] = any( len(x) > 0 for x in [ - default_clientscopes_add, - optional_clientscopes_add, - default_clientscopes_delete, - optional_clientscopes_delete, + default_client_scopes_add, + optional_client_scopes_add, + default_client_scopes_delete, + optional_client_scopes_delete, ] ) if module.check_mode: module.exit_json(**result) - # first delete so clientscopes can change type - for clientscope in default_clientscopes_delete: - kc.delete_default_clientscope(clientscope["id"], realm, client_id) - for clientscope in optional_clientscopes_delete: - kc.delete_optional_clientscope(clientscope["id"], realm, client_id) + # first delete so client_scopes can change type + for client_scope in default_client_scopes_delete: + kc.delete_default_client_scope(client_scope["id"], realm, client_id) + for client_scope in optional_client_scopes_delete: + kc.delete_optional_client_scope(client_scope["id"], realm, client_id) - for clientscope in default_clientscopes_add: - kc.add_default_clientscope(clientscope["id"], realm, client_id) - for clientscope in optional_clientscopes_add: - kc.add_optional_clientscope(clientscope["id"], realm, client_id) + for client_scope in default_client_scopes_add: + kc.add_default_client_scope(client_scope["id"], realm, client_id) + for client_scope in optional_client_scopes_add: + kc.add_optional_client_scope(client_scope["id"], realm, client_id) result["end_state"].update( { - "default_clientscopes": extract_field(kc.get_default_clientscopes(realm, client_id)), - "optional_clientscopes": extract_field(kc.get_optional_clientscopes(realm, client_id)), + "default_client_scopes": extract_field(kc.get_default_client_scopes(realm, client_id)), + "optional_client_scopes": extract_field(kc.get_optional_client_scopes(realm, client_id)), } ) From ba3f716e5c6df2050aabc89bc345ec5210099606 Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Mon, 8 Jun 2026 11:56:39 +0000 Subject: [PATCH 2/8] fix scope_id etc. --- .../identity/keycloak/keycloak.py | 62 +++++++++---------- .../keycloak_clientscope_rolemappings.py | 2 +- plugins/modules/keycloak_clientscope_type.py | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index e2e9da3..1c24736 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -1293,7 +1293,7 @@ class KeycloakAPI: """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the group, make a subsequent call to - get_client_scope_by_client_scopeid, passing in the ID of the group you wish to return. + get_client_scope_by_client_scope_id, passing in the ID of the group you wish to return. :param realm: Realm in which the client scope resides; default 'master'. :return The client scopes of this realm (default "master") @@ -1304,7 +1304,7 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not fetch list of client scopes in realm {realm}: {e}") - def get_client_scope_by_client_scopeid(self, cid, realm: str = "master"): + def get_client_scope_by_client_scope_id(self, cid, realm: str = "master"): """Fetch a keycloak client scope from the provided realm using the client scope's unique ID. If the client scope does not exist, None is returned. @@ -1341,38 +1341,38 @@ class KeycloakAPI: for client_scope in all_client_scopes: if client_scope["name"] == name: - return self.get_client_scope_by_client_scopeid(client_scope["id"], realm=realm) + return self.get_client_scope_by_client_scope_id(client_scope["id"], realm=realm) return None except Exception as e: self.module.fail_json(msg=f"Could not fetch client scope {name} in realm {realm}: {e}") - def create_client_scope(self, client_scoperep, realm: str = "master"): + def create_client_scope(self, client_scope_rep, realm: str = "master"): """Create a Keycloak client scope. - :param client_scoperep: a ClientScopeRepresentation of the client scope to be created. Must contain at minimum the field name. + :param client_scope_rep: a ClientScopeRepresentation of the client scope to be created. Must contain at minimum the field name. :return: HTTPResponse object on success """ client_scopes_url = URL_CLIENT_SCOPES.format(url=self.baseurl, realm=realm) try: - return self._request(client_scopes_url, method="POST", data=json.dumps(client_scoperep)) + return self._request(client_scopes_url, method="POST", data=json.dumps(client_scope_rep)) except Exception as e: - self.fail_request(e, msg=f"Could not create client scope {client_scoperep['name']} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not create client scope {client_scope_rep['name']} in realm {realm}: {e}") - def update_client_scope(self, client_scoperep, realm: str = "master"): + def update_client_scope(self, client_scope_rep, realm: str = "master"): """Update an existing client scope. :param grouprep: A GroupRepresentation of the updated group. :return HTTPResponse object on success """ - client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=client_scoperep["id"]) + client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=client_scope_rep["id"]) try: - return self._request(client_scope_url, method="PUT", data=json.dumps(client_scoperep)) + return self._request(client_scope_url, method="PUT", data=json.dumps(client_scope_rep)) except Exception as e: - self.fail_request(e, msg=f"Could not update client scope {client_scoperep['name']} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not update client scope {client_scope_rep['name']} in realm {realm}: {e}") def delete_client_scope(self, name=None, cid=None, realm: str = "master"): """Delete a client scope. One of name or cid must be provided. @@ -1414,7 +1414,7 @@ class KeycloakAPI: """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the group, make a subsequent call to - get_client_scope_by_client_scopeid, passing in the ID of the group you wish to return. + get_client_scope_by_client_scope_id, passing in the ID of the group you wish to return. :param cid: id of client scope (not name). :param realm: Realm in which the client_scope resides; default 'master'. @@ -1511,7 +1511,7 @@ class KeycloakAPI: """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return. :param realm: Realm in which the client scope resides. :param client_id: The client in which the client scope resides. @@ -1524,7 +1524,7 @@ class KeycloakAPI: """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return. :param realm: Realm in which the client scope resides. :param client_id: The client in which the client scope resides. @@ -1537,7 +1537,7 @@ class KeycloakAPI: """Fetch the name and ID of all client scopes on the Keycloak server. To fetch the full data of the client scope, make a subsequent call to - get_client_scope_by_client_scopeid, passing in the ID of the client scope you wish to return. + get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return. :param realm: Realm in which the client scope resides. :param url_template the template for the right type @@ -3269,7 +3269,7 @@ class KeycloakAPI: try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: - self.fail_request(e, msg=f"Could not fetch roles for client-scope {client_scope_id} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch roles for client scope {client_scope_id} in realm {realm}: {e}") def get_client_scope_scope_mappings_realm(self, client_scope_id, realm: str = "master"): """Fetch the realm roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server. @@ -3284,7 +3284,7 @@ class KeycloakAPI: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: self.fail_request( - e, msg=f"Could not fetch realm roles for client-scope {client_scope_id} in realm {realm}: {e}" + e, msg=f"Could not fetch realm roles for client scope {client_scope_id} in realm {realm}: {e}" ) def get_client_scope_scope_mappings_client(self, client_scope_id, client_id, realm: str = "master"): @@ -3302,34 +3302,34 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not fetch client roles from client {client_id} for client-scope {client_scope_id} in realm {realm}: {e}", + msg=f"Could not fetch client roles from client {client_id} for client scope {client_scope_id} in realm {realm}: {e}", ) - def get_client_role_scope_from_client(self, clientid, client_scopeid, realm: str = "master"): + def get_client_role_scope_from_client(self, clientid, client_scope_id, realm: str = "master"): """Fetch the roles associated with the client's scope for a specific client on the Keycloak server. :param clientid: ID of the client from which to obtain the associated roles. - :param client_scopeid: ID of the client who owns the roles. + :param client_scope_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the scope. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id ) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}") - def update_client_role_scope_from_client(self, payload, clientid, client_scopeid, realm: str = "master"): + def update_client_role_scope_from_client(self, payload, clientid, client_scope_id, realm: str = "master"): """Update and fetch the roles associated with the client's scope on the Keycloak server. :param payload: List of roles to be added to the scope. :param clientid: ID of the client to update scope. - :param client_scopeid: ID of the client who owns the roles. + :param client_scope_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id ) try: self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) @@ -3337,18 +3337,18 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, client_scopeid, realm) + return self.get_client_role_scope_from_client(clientid, client_scope_id, realm) - def delete_client_role_scope_from_client(self, payload, clientid, client_scopeid, realm: str = "master"): + def delete_client_role_scope_from_client(self, payload, clientid, client_scope_id, realm: str = "master"): """Delete the roles contains in the payload from the client's scope on the Keycloak server. :param payload: List of roles to be deleted. :param clientid: ID of the client to delete roles from scope. - :param client_scopeid: ID of the client who owns the roles. + :param client_scope_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scopeid + url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id ) try: self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) @@ -3356,7 +3356,7 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, client_scopeid, realm) + return self.get_client_role_scope_from_client(clientid, client_scope_id, realm) def update_client_scope_scope_mappings_client( self, payload: list[dict], client_scope_id: str, client_id: str, realm: str = "master" @@ -3377,7 +3377,7 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not update scope mappings for client-scope {client_id}.{client_scope_id} in realm {realm}: {e}", + msg=f"Could not update scope mappings for client scope {client_id}.{client_scope_id} in realm {realm}: {e}", ) return self.get_client_scope_scope_mappings_client(client_scope_id, client_id, realm) @@ -3441,7 +3441,7 @@ class KeycloakAPI: except Exception as e: self.fail_request( - e, msg=f"Could not delete scope mappings for client-scope {client_scope_id} in realm {realm}: {e}" + e, msg=f"Could not delete scope mappings for client scope {client_scope_id} in realm {realm}: {e}" ) return self.get_client_scope_scope_mappings_realm(client_scope_id, realm) diff --git a/plugins/modules/keycloak_clientscope_rolemappings.py b/plugins/modules/keycloak_clientscope_rolemappings.py index a89241d..a503336 100644 --- a/plugins/modules/keycloak_clientscope_rolemappings.py +++ b/plugins/modules/keycloak_clientscope_rolemappings.py @@ -196,7 +196,7 @@ def main(): client_scope_object = kc.get_client_scope_by_name(client_scope_id, realm) if not client_scope_object: - module.fail_json(msg=f"Failed to retrieve client-scope '{client_scope_id}'") + module.fail_json(msg=f"Failed to retrieve client scope '{client_scope_id}'") if client_id: # add client role diff --git a/plugins/modules/keycloak_clientscope_type.py b/plugins/modules/keycloak_clientscope_type.py index 00cbd05..d069c28 100644 --- a/plugins/modules/keycloak_clientscope_type.py +++ b/plugins/modules/keycloak_clientscope_type.py @@ -94,7 +94,7 @@ msg: type: str sample: "" proposed: - description: Representation of proposed client-scope types mapping. + description: Representation of proposed client scope types mapping. returned: always type: dict sample: From 1ec94b961f66d06ea5caed95bcc48c6312dd5499 Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Mon, 8 Jun 2026 11:57:26 +0000 Subject: [PATCH 3/8] rename files to match new convention --- ...cope_rolemappings.py => keycloak_client_scope_rolemappings.py} | 0 ...keycloak_clientscope_type.py => keycloak_client_scope_type.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/modules/{keycloak_clientscope_rolemappings.py => keycloak_client_scope_rolemappings.py} (100%) rename plugins/modules/{keycloak_clientscope_type.py => keycloak_client_scope_type.py} (100%) diff --git a/plugins/modules/keycloak_clientscope_rolemappings.py b/plugins/modules/keycloak_client_scope_rolemappings.py similarity index 100% rename from plugins/modules/keycloak_clientscope_rolemappings.py rename to plugins/modules/keycloak_client_scope_rolemappings.py diff --git a/plugins/modules/keycloak_clientscope_type.py b/plugins/modules/keycloak_client_scope_type.py similarity index 100% rename from plugins/modules/keycloak_clientscope_type.py rename to plugins/modules/keycloak_client_scope_type.py From a1bb84ea5b30558234a326065ad12809db818061 Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Mon, 8 Jun 2026 12:20:32 +0000 Subject: [PATCH 4/8] fix some missed ones --- README.md | 2 +- plugins/module_utils/identity/keycloak/keycloak.py | 4 ++-- plugins/modules/keycloak_client_scope_type.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 55474ed..d9ebd81 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ All Keycloak administration modules from `community.general` are provided in thi * `keycloak_client_rolemapping`: manage client role mappings for users and groups. * `keycloak_client_rolescope`: manage client role scope mappings. * `keycloak_client_scope`: manage client scopes and protocol mappers (replaces `community.general.keycloak_clientscope`). -* `keycloak_client_scopeee_type`: manage default and optional client scope assignments. +* `keycloak_client_scope_type`: manage default and optional client scope assignments. * `keycloak_clientsecret_info`: retrieve client secret information. * `keycloak_clientsecret_regenerate`: regenerate a client secret. * `keycloak_clienttemplate`: manage legacy client templates. diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index 1c24736..a4b7c1d 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -1417,7 +1417,7 @@ class KeycloakAPI: get_client_scope_by_client_scope_id, passing in the ID of the group you wish to return. :param cid: id of client scope (not name). - :param realm: Realm in which the client_scope resides; default 'master'. + :param realm: Realm in which the client scope resides; default 'master'. :return The protocolmappers of this realm (default "master") """ protocolmappers_url = URL_CLIENT_SCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm) @@ -1634,7 +1634,7 @@ class KeycloakAPI: except Exception as e: place = "realm" if client_id is None else f"client {client_id}" - self.fail_request(e, msg=f"Unable to {action} {scope_type} client_scope {id} @ {place} : {e}") + self.fail_request(e, msg=f"Unable to {action} {scope_type} client scope {id} @ {place} : {e}") def create_clientsecret(self, id, realm: str = "master"): """Generate a new client secret by id diff --git a/plugins/modules/keycloak_client_scope_type.py b/plugins/modules/keycloak_client_scope_type.py index d069c28..f8040d0 100644 --- a/plugins/modules/keycloak_client_scope_type.py +++ b/plugins/modules/keycloak_client_scope_type.py @@ -271,7 +271,7 @@ def main(): if module._diff: result["diff"] = dict(before=normalize_scopes(result["existing"]), after=normalize_scopes(result["proposed"])) - default_client_scopes_add = client_scopes_to_add(default_client_scopees_existing, defaultclient_scopees_real) + default_client_scopes_add = client_scopes_to_add(default_client_scopes_existing, default_client_scopes_real) optional_client_scopes_add = client_scopes_to_add(optional_client_scopes_existing, optional_client_scopes_real) default_client_scopes_delete = client_scopes_to_delete(default_client_scopes_existing, default_client_scopes_real) From 92d6dddd492773e088ffa4a5f75dbe273ed6bace Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Mon, 8 Jun 2026 13:21:27 +0000 Subject: [PATCH 5/8] rename to role_owner_client_id --- .../identity/keycloak/keycloak.py | 74 +++++++++---------- .../keycloak/keycloak_clientsecret.py | 2 +- plugins/modules/keycloak_client.py | 4 +- plugins/modules/keycloak_client_rolescope.py | 66 ++++++++--------- .../keycloak_client_scope_rolemappings.py | 2 +- 5 files changed, 74 insertions(+), 74 deletions(-) diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index a4b7c1d..7360397 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -701,7 +701,7 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not obtain list of clients for realm {realm}: {e}") - def get_client_by_clientid(self, client_id, realm: str = "master"): + def get_client_by_client_id(self, client_id, realm: str = "master"): """Get client representation by clientId :param client_id: The clientId to be queried :param realm: realm from which to obtain the client representation @@ -744,7 +744,7 @@ class KeycloakAPI: :param realm: client template from this realm :return: id of client (usually a UUID) """ - result = self.get_client_by_clientid(client_id, realm) + result = self.get_client_by_client_id(client_id, realm) if isinstance(result, dict) and "id" in result: return result["id"] else: @@ -2020,7 +2020,7 @@ class KeycloakAPI: composite_url = "" try: if clientid is not None: - client = self.get_client_by_clientid(client_id=clientid, realm=realm) + client = self.get_client_by_client_id(client_id=clientid, realm=realm) cid = client["id"] composite_url = URL_CLIENT_ROLE_COMPOSITES.format( url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="") @@ -2038,7 +2038,7 @@ class KeycloakAPI: composite_url = "" try: if clientid is not None: - client = self.get_client_by_clientid(client_id=clientid, realm=realm) + client = self.get_client_by_client_id(client_id=clientid, realm=realm) cid = client["id"] composite_url = URL_CLIENT_ROLE_COMPOSITES.format( url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="") @@ -2057,7 +2057,7 @@ class KeycloakAPI: composite_url = "" try: if clientid is not None: - client = self.get_client_by_clientid(client_id=clientid, realm=realm) + client = self.get_client_by_client_id(client_id=clientid, realm=realm) cid = client["id"] composite_url = URL_CLIENT_ROLE_COMPOSITES.format( url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="") @@ -3305,71 +3305,71 @@ class KeycloakAPI: msg=f"Could not fetch client roles from client {client_id} for client scope {client_scope_id} in realm {realm}: {e}", ) - def get_client_role_scope_from_client(self, clientid, client_scope_id, realm: str = "master"): + def get_client_role_scope_from_client(self, target_client_id, role_owner_client_id, realm: str = "master"): """Fetch the roles associated with the client's scope for a specific client on the Keycloak server. - :param clientid: ID of the client from which to obtain the associated roles. - :param client_scope_id: ID of the client who owns the roles. + :param target_client_id: ID of the client from which to obtain the associated roles. + :param role_owner_client_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the scope. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id + url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id ) try: return self._request_and_deserialize(client_role_scope_url, method="GET") except Exception as e: - self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not fetch roles scope for client {target_client_id} in realm {realm}: {e}") - def update_client_role_scope_from_client(self, payload, clientid, client_scope_id, realm: str = "master"): + def update_client_role_scope_from_client(self, roles, target_client_id, role_owner_client_id, realm: str = "master"): """Update and fetch the roles associated with the client's scope on the Keycloak server. - :param payload: List of roles to be added to the scope. - :param clientid: ID of the client to update scope. - :param client_scope_id: ID of the client who owns the roles. + :param roles: List of roles to be added to the scope. + :param target_client_id: ID of the client to update scope. + :param role_owner_client_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id + url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id ) try: - self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) + self._request(client_role_scope_url, method="POST", data=json.dumps(roles)) except Exception as e: - self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not update roles scope for client {target_client_id} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, client_scope_id, realm) + return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm) - def delete_client_role_scope_from_client(self, payload, clientid, client_scope_id, realm: str = "master"): - """Delete the roles contains in the payload from the client's scope on the Keycloak server. - :param payload: List of roles to be deleted. - :param clientid: ID of the client to delete roles from scope. - :param client_scope_id: ID of the client who owns the roles. + def delete_client_role_scope_from_client(self, roles, target_client_id, role_owner_client_id, realm: str = "master"): + """Delete the roles contained in the payload from the client's scope on the Keycloak server. + :param roles: List of roles to be deleted. + :param target_client_id: ID of the client to delete roles from scope. + :param role_owner_client_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the clients. :return: The client scope of roles from specified client. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format( - url=self.baseurl, realm=realm, id=clientid, scopeid=client_scope_id + url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id ) try: - self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) + self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles)) except Exception as e: - self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}") + self.fail_request(e, msg=f"Could not delete roles from scope for client {target_client_id} in realm {realm}: {e}") - return self.get_client_role_scope_from_client(clientid, client_scope_id, realm) + return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm) def update_client_scope_scope_mappings_client( - self, payload: list[dict], client_scope_id: str, client_id: str, realm: str = "master" + self, payload: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master" ): """Update and fetch the client roles (scope-mappings) associated with the client scope on the Keycloak server. :param payload: List of client roles to be added to the scope. :param client_scope_id: ID of the client scope to update scope-mappings. - :param clientid: ID of the client from which to obtain the associated roles. + :param role_owner_client_id: ID of the client from which to obtain the associated roles. :param realm: Realm from which to obtain the client. :return: The client scope client scope-mappings. """ client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format( - url=self.baseurl, realm=realm, id=client_scope_id, client=client_id + url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id ) try: self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) @@ -3377,10 +3377,10 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not update scope mappings for client scope {client_id}.{client_scope_id} in realm {realm}: {e}", + msg=f"Could not update scope mappings for client scope {role_owner_client_id}.{client_scope_id} in realm {realm}: {e}", ) - return self.get_client_scope_scope_mappings_client(client_scope_id, client_id, realm) + return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm) def update_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): """Update and fetch the realm roles (scope-mappings) associated with the client scope on the Keycloak server. @@ -3403,17 +3403,17 @@ class KeycloakAPI: return self.get_client_scope_scope_mappings_realm(client_scope_id, realm) def delete_client_scope_scope_mappings_client( - self, payload: list[dict], client_scope_id: str, client_id: str, realm: str = "master" + self, roles: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master" ): """Delete the client roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. :param payload: List of roles to be deleted. :param client_scope_id: ID of the client scope to delete roles from scope-mappings. - :param clientid: ID of the client who owns the roles. + :param role_owner_client_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the client. :return: The client scope client scope-mappings. """ client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format( - url=self.baseurl, realm=realm, id=client_scope_id, client=client_id + url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id ) try: self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) @@ -3421,10 +3421,10 @@ class KeycloakAPI: except Exception as e: self.fail_request( e, - msg=f"Could not delete scope mappings for client scope {client_id}.{client_scope_id} in realm {realm}: {e}", + msg=f"Could not delete scope mappings for client scope {role_owner_client_id}.{client_scope_id} in realm {realm}: {e}", ) - return self.get_client_scope_scope_mappings_client(client_scope_id, client_id, realm) + return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm) def delete_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): """Delete the realm roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. diff --git a/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py b/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py index ffa5974..54fa277 100644 --- a/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py +++ b/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py @@ -66,7 +66,7 @@ def keycloak_clientsecret_module_resolve_params(module: AnsibleModule, kc: Keycl # less lookup. if id is None: # Due to the required_one_of spec, client_id is guaranteed to not be None - client = kc.get_client_by_clientid(client_id, realm=realm) + client = kc.get_client_by_client_id(client_id, realm=realm) if client is None: module.fail_json(msg=f"Client does not exist {client_id}") diff --git a/plugins/modules/keycloak_client.py b/plugins/modules/keycloak_client.py index 0407a13..e6f0f9e 100644 --- a/plugins/modules/keycloak_client.py +++ b/plugins/modules/keycloak_client.py @@ -1346,7 +1346,7 @@ def main(): # See if it already exists in Keycloak if cid is None: - before_client = kc.get_client_by_clientid(module.params.get("client_id"), realm=realm) + before_client = kc.get_client_by_client_idd(module.params.get("client_id"), realm=realm) if before_client is not None: cid = before_client["id"] else: @@ -1440,7 +1440,7 @@ def main(): # create it kc.create_client(desired_client, realm=realm) - after_client = kc.get_client_by_clientid(desired_client["clientId"], realm=realm) + after_client = kc.get_client_by_client_id(desired_client["clientId"], realm=realm) result["end_state"] = sanitize_cr(after_client) diff --git a/plugins/modules/keycloak_client_rolescope.py b/plugins/modules/keycloak_client_rolescope.py index 2ea710a..df16e29 100644 --- a/plugins/modules/keycloak_client_rolescope.py +++ b/plugins/modules/keycloak_client_rolescope.py @@ -19,7 +19,7 @@ description: to the REST API using OpenID Connect; the user connecting and the client being used must have the requisite access rights. In a default Keycloak installation, admin-cli and an admin user would work, as would a separate client definition with the scope tailored to your needs and a user having the expected roles. - - Client O(client_id) must have O(middleware_automation.keycloak.keycloak_client#module:full_scope_allowed) set to V(false). + - Client O(target_client_id) must have O(middleware_automation.keycloak.keycloak_client#module:full_scope_allowed) set to V(false). - Attributes are multi-valued in the Keycloak API. All attributes are lists of individual values and are returned that way by this module. You may pass single values for attributes when calling the module, and this is translated into a list suitable for the API. @@ -50,12 +50,12 @@ options: - The Keycloak realm under which clients resides. default: 'master' - client_id: + target_client_id: type: str required: true description: - Roles provided in O(role_names) while be added to this client scope. - client_scope_id: + role_owner_client_id: type: str description: - If the O(role_names) are client role, the client ID under which it resides. @@ -66,8 +66,8 @@ options: elements: str description: - Names of roles to manipulate. - - If O(client_scope_id) is present, all roles must be under this client. - - If O(client_scope_id) is absent, all roles must be under the realm. + - If O(role_owner_client_id) is present, all roles must be under this client. + - If O(role_owner_client_id) is absent, all roles must be under the realm. extends_documentation_fragment: - middleware_automation.keycloak.keycloak - middleware_automation.keycloak.actiongroup_keycloak @@ -85,8 +85,8 @@ EXAMPLES = r""" auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm - client_id: frontend-client-public - client_scope_id: backend-client-private + target_client_id: frontend-client-public + role_owner_client_id: backend-client-private role_names: - backend-role-admin - backend-role-user @@ -98,8 +98,8 @@ EXAMPLES = r""" auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm - client_id: frontend-client-public - client_scope_id: backend-client-private + target_client_id: frontend-client-public + role_owner_client_id: backend-client-private role_names: - backend-role-admin state: absent @@ -111,7 +111,7 @@ EXAMPLES = r""" auth_username: USERNAME auth_password: PASSWORD realm: MyCustomRealm - client_id: frontend-client-public + target_client_id: frontend-client-public role_names: - realm-role-admin - realm-role-user @@ -167,8 +167,8 @@ def main(): argument_spec = keycloak_argument_spec() meta_args = dict( - client_id=dict(type="str", required=True), - client_scope_id=dict(type="str"), + target_client_id=dict(type="str", required=True), + role_owner_client_id=dict(type="str"), realm=dict(type="str", default="master"), role_names=dict(type="list", elements="str", required=True), state=dict(type="str", default="present", choices=["present", "absent"]), @@ -189,8 +189,8 @@ def main(): kc = KeycloakAPI(module, connection_header) realm = module.params.get("realm") - clientid = module.params.get("client_id") - client_scope_id = module.params.get("client_scope_id") + target_client_id = module.params.get("target_client_id") + role_owner_client_id = module.params.get("role_owner_client_id") role_names = module.params.get("role_names") state = module.params.get("state") @@ -198,23 +198,23 @@ def main(): if not objRealm: module.fail_json(msg=f"Failed to retrive realm '{realm}'") - objClient = kc.get_client_by_clientid(clientid, realm) + objClient = kc.get_client_by_target_client_id(target_client_id, realm) if not objClient: - module.fail_json(msg=f"Failed to retrive client '{realm}.{clientid}'") + module.fail_json(msg=f"Failed to retrive client '{realm}.{target_client_id}'") if objClient["fullScopeAllowed"] and state == "present": - module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{clientid}'") + module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{target_client_id}'") - if client_scope_id: - objClientScope = kc.get_client_by_clientid(client_scope_id, realm) - if not objClientScope: - module.fail_json(msg=f"Failed to retrive client '{realm}.{client_scope_id}'") - before_role_mapping = kc.get_client_role_scope_from_client(objClient["id"], objClientScope["id"], realm) + if role_owner_client_id: + role_owner_client = kc.get_client_by_client_id(role_owner_client_id, realm) + if not role_owner_client: + module.fail_json(msg=f"Failed to retrive client '{realm}.{role_owner_client_id}'") + before_role_mapping = kc.get_client_role_scope_from_client(objClient["id"], role_owner_client["id"], realm) else: before_role_mapping = kc.get_client_role_scope_from_realm(objClient["id"], realm) - if client_scope_id: + if role_owner_client_id: # retrive all role from client_scope - client_scope_roles_by_name = kc.get_client_roles_by_id(objClientScope["id"], realm) + client_scope_roles_by_name = kc.get_client_roles_by_id(role_owner_client["id"], realm) else: # retrive all role from realm client_scope_roles_by_name = kc.get_realm_roles(realm) @@ -228,8 +228,8 @@ def main(): # update desired for role_name in role_names: if role_name not in client_scope_roles_by_name: - if client_scope_id: - module.fail_json(msg=f"Failed to retrive role '{realm}.{client_scope_id}.{role_name}'") + if role_owner_client_id: + module.fail_json(msg=f"Failed to retrive role '{realm}.{role_owner_client_id}.{role_name}'") else: module.fail_json(msg=f"Failed to retrive role '{realm}.{role_name}'") if role_name not in role_mapping_by_name: @@ -253,33 +253,33 @@ def main(): if not result["changed"]: # no changes result["end_state"] = before_role_mapping - result["msg"] = f"No changes required for client role scope {clientid}." + result["msg"] = f"No changes required for client role scope {target_client_id}." elif state == "present": # doing update if module.check_mode: result["end_state"] = desired_role_mapping - elif client_scope_id: + elif role_owner_client_id: result["end_state"] = kc.update_client_role_scope_from_client( - role_mapping_to_manipulate, objClient["id"], objClientScope["id"], realm + role_mapping_to_manipulate, objClient["id"], role_owner_client["id"], realm ) else: result["end_state"] = kc.update_client_role_scope_from_realm( role_mapping_to_manipulate, objClient["id"], realm ) - result["msg"] = f"Client role scope for {clientid} has been updated" + result["msg"] = f"Client role scope for {target_client_id} has been updated" else: # doing delete if module.check_mode: result["end_state"] = desired_role_mapping - elif client_scope_id: + elif role_owner_client_id: result["end_state"] = kc.delete_client_role_scope_from_client( - role_mapping_to_manipulate, objClient["id"], objClientScope["id"], realm + role_mapping_to_manipulate, objClient["id"], role_owner_client["id"], realm ) else: result["end_state"] = kc.delete_client_role_scope_from_realm( role_mapping_to_manipulate, objClient["id"], realm ) - result["msg"] = f"Client role scope for {clientid} has been deleted" + result["msg"] = f"Client role scope for {target_client_id} has been deleted" module.exit_json(**result) diff --git a/plugins/modules/keycloak_client_scope_rolemappings.py b/plugins/modules/keycloak_client_scope_rolemappings.py index a503336..bd8e6fb 100644 --- a/plugins/modules/keycloak_client_scope_rolemappings.py +++ b/plugins/modules/keycloak_client_scope_rolemappings.py @@ -200,7 +200,7 @@ def main(): if client_id: # add client role - client_object = kc.get_client_by_clientid(client_id, realm) + client_object = kc.get_client_by_client_id(client_id, realm) if not client_object: module.fail_json(msg=f"Failed to retrieve client '{realm}.{client_id}'") if client_object["fullScopeAllowed"] and state == "present": From 942f0ae89681fea9c7495d07b4adf12add0f1b43 Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Tue, 9 Jun 2026 07:14:32 +0000 Subject: [PATCH 6/8] fix rename payload -> roles --- .../identity/keycloak/keycloak.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index 7360397..19e0d5e 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -3340,7 +3340,7 @@ class KeycloakAPI: return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm) def delete_client_role_scope_from_client(self, roles, target_client_id, role_owner_client_id, realm: str = "master"): - """Delete the roles contained in the payload from the client's scope on the Keycloak server. + """Delete the roles from the client's scope on the Keycloak server. :param roles: List of roles to be deleted. :param target_client_id: ID of the client to delete roles from scope. :param role_owner_client_id: ID of the client who owns the roles. @@ -3359,10 +3359,10 @@ class KeycloakAPI: return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm) def update_client_scope_scope_mappings_client( - self, payload: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master" + self, roles: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master" ): """Update and fetch the client roles (scope-mappings) associated with the client scope on the Keycloak server. - :param payload: List of client roles to be added to the scope. + :param roles: List of client roles to be added to the scope. :param client_scope_id: ID of the client scope to update scope-mappings. :param role_owner_client_id: ID of the client from which to obtain the associated roles. :param realm: Realm from which to obtain the client. @@ -3372,7 +3372,7 @@ class KeycloakAPI: url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id ) try: - self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) + self._request(client_role_scope_url, method="POST", data=json.dumps(roles)) except Exception as e: self.fail_request( @@ -3382,9 +3382,9 @@ class KeycloakAPI: return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm) - def update_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): + def update_client_scope_scope_mappings_realm(self, roles: list[dict], client_scope_id: str, realm: str = "master"): """Update and fetch the realm roles (scope-mappings) associated with the client scope on the Keycloak server. - :param payload: List of realm roles to be added to the scope. + :param roles: List of realm roles to be added to the scope. :param client_scope_id: ID of the client scope to update scope-mappings. :param realm: Realm from which to obtain the roles. :return: The client scope realm scope-mappings. @@ -3393,7 +3393,7 @@ class KeycloakAPI: url=self.baseurl, realm=realm, id=client_scope_id ) try: - self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) + self._request(client_role_scope_url, method="POST", data=json.dumps(roles)) except Exception as e: self.fail_request( @@ -3405,8 +3405,8 @@ class KeycloakAPI: def delete_client_scope_scope_mappings_client( self, roles: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master" ): - """Delete the client roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. - :param payload: List of roles to be deleted. + """Delete the client roles (scope_mappings) from the client scope on the Keycloak server. + :param roles: List of roles to be deleted. :param client_scope_id: ID of the client scope to delete roles from scope-mappings. :param role_owner_client_id: ID of the client who owns the roles. :param realm: Realm from which to obtain the client. @@ -3416,7 +3416,7 @@ class KeycloakAPI: url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id ) try: - self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) + self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles)) except Exception as e: self.fail_request( @@ -3426,9 +3426,9 @@ class KeycloakAPI: return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm) - def delete_client_scope_scope_mappings_realm(self, payload: list[dict], client_scope_id: str, realm: str = "master"): - """Delete the realm roles (scope_mappings) contained in the payload from the client scope on the Keycloak server. - :param payload: List of roles to be deleted. + def delete_client_scope_scope_mappings_realm(self, roles: list[dict], client_scope_id: str, realm: str = "master"): + """Delete the realm roles (scope_mappings) contained in the roles from the client scope on the Keycloak server. + :param roles: List of roles to be deleted. :param client_scope_id: ID of the client scope to delete roles from scope-mappings. :param realm: Realm from which to obtain the roles. :return: The client scope realm scope-mappings. @@ -3437,7 +3437,7 @@ class KeycloakAPI: url=self.baseurl, realm=realm, id=client_scope_id ) try: - self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) + self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles)) except Exception as e: self.fail_request( @@ -3458,32 +3458,32 @@ class KeycloakAPI: except Exception as e: self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}") - def update_client_role_scope_from_realm(self, payload, clientid, realm: str = "master"): + def update_client_role_scope_from_realm(self, roles, clientid, realm: str = "master"): """Update and fetch the realm roles from the client's scope on the Keycloak server. - :param payload: List of realm roles to add. + :param roles: List of realm roles to add. :param clientid: ID of the client to update scope. :param realm: Realm from which to obtain the clients. :return: The client realm roles scope. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid) try: - self._request(client_role_scope_url, method="POST", data=json.dumps(payload)) + self._request(client_role_scope_url, method="POST", data=json.dumps(roles)) except Exception as e: self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}") return self.get_client_role_scope_from_realm(clientid, realm) - def delete_client_role_scope_from_realm(self, payload, clientid, realm: str = "master"): - """Delete the realm roles contains in the payload from the client's scope on the Keycloak server. - :param payload: List of realm roles to delete. + def delete_client_role_scope_from_realm(self, roles, clientid, realm: str = "master"): + """Delete the realm roles from the client's scope on the Keycloak server. + :param roles: List of realm roles to delete. :param clientid: ID of the client to delete roles from scope. :param realm: Realm from which to obtain the clients. :return: The client realm roles scope. """ client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid) try: - self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload)) + self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles)) except Exception as e: self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}") From 0fad56294b0fb8d145241a484925ec119e73c2cb Mon Sep 17 00:00:00 2001 From: Felix Grzelka Date: Wed, 10 Jun 2026 11:14:23 +0000 Subject: [PATCH 7/8] fix typo --- plugins/modules/keycloak_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/keycloak_client.py b/plugins/modules/keycloak_client.py index e6f0f9e..8c3ba16 100644 --- a/plugins/modules/keycloak_client.py +++ b/plugins/modules/keycloak_client.py @@ -1346,7 +1346,7 @@ def main(): # See if it already exists in Keycloak if cid is None: - before_client = kc.get_client_by_client_idd(module.params.get("client_id"), realm=realm) + before_client = kc.get_client_by_client_id(module.params.get("client_id"), realm=realm) if before_client is not None: cid = before_client["id"] else: From c8bcff39ef6936e4de74cfdb5ce0c4265d727850 Mon Sep 17 00:00:00 2001 From: Harsha Cherukuri Date: Wed, 10 Jun 2026 08:22:52 -0400 Subject: [PATCH 8/8] Fix keycloak client module --- molecule/keycloak_modules/verify.yml | 4 ++-- plugins/modules/keycloak_client_rolescope.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/molecule/keycloak_modules/verify.yml b/molecule/keycloak_modules/verify.yml index a313b24..b08f1a8 100644 --- a/molecule/keycloak_modules/verify.yml +++ b/molecule/keycloak_modules/verify.yml @@ -299,7 +299,7 @@ - name: keycloak_client_rolescope — restrict realm role on client middleware_automation.keycloak.keycloak_client_rolescope: realm: "{{ target_realm }}" - client_id: "{{ client }}" + target_client_id: "{{ client }}" role_names: - "{{ role }}" state: present @@ -537,7 +537,7 @@ - name: keycloak_client_rolescope — remove role scope mapping middleware_automation.keycloak.keycloak_client_rolescope: realm: "{{ target_realm }}" - client_id: "{{ client }}" + target_client_id: "{{ client }}" role_names: - "{{ role }}" state: absent diff --git a/plugins/modules/keycloak_client_rolescope.py b/plugins/modules/keycloak_client_rolescope.py index df16e29..7dee9c6 100644 --- a/plugins/modules/keycloak_client_rolescope.py +++ b/plugins/modules/keycloak_client_rolescope.py @@ -198,7 +198,7 @@ def main(): if not objRealm: module.fail_json(msg=f"Failed to retrive realm '{realm}'") - objClient = kc.get_client_by_target_client_id(target_client_id, realm) + objClient = kc.get_client_by_client_id(target_client_id, realm) if not objClient: module.fail_json(msg=f"Failed to retrive client '{realm}.{target_client_id}'") if objClient["fullScopeAllowed"] and state == "present":