rename to role_owner_client_id

This commit is contained in:
Felix Grzelka
2026-06-08 13:21:27 +00:00
parent a1bb84ea5b
commit 92d6dddd49
5 changed files with 74 additions and 74 deletions

View File

@@ -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.

View File

@@ -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}")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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":