1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-06-11 02:25:36 +00:00

new module: keycloak_clientscope_rolemappings (#11841)

* init

* stuff

* this should work

* helper functions

* fix docstrings

* s/client scope/clientscope/

* fix docstrings

* add type hints

* fix old function

* nox -Re formatters

* fix clientscope_id

* fix blank line contains whitespace

* add BOTMETA info

* set version_added

* Apply suggestions from code review to prepare for 13.0.0

Co-authored-by: Felix Fontein <felix@fontein.de>

* fix yaml indent in doc string

* add keycloak_clientscope_rolemappings to keycloak action group

* original author credit

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* init tests

* Update plugins/modules/keycloak_clientscope_rolemappings.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* fix integration tests

* use [] instead of .get()

* fix typo

* Update plugins/modules/keycloak_clientscope_rolemappings.py

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* update fedora version

* fix --docker fedora

* revert

* Apply suggestions from code review

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* remove unnecessary docstring

* change something

* change it back

* Apply suggestions from code review

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update plugins/modules/keycloak_clientscope_rolemappings.py

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
felix-grzelka 2026-05-30 13:45:37 +02:00 committed by GitHub
parent 2d89fb1c15
commit 20a07fc973
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 813 additions and 0 deletions

2
.github/BOTMETA.yml vendored
View file

@ -857,6 +857,8 @@ files:
maintainers: Gaetan2907
$modules/keycloak_client_rolescope.py:
maintainers: desand01
$modules/keycloak_clientscope_rolemappings.py:
maintainers: felix-grzelka
$modules/keycloak_clientscope.py:
maintainers: Gaetan2907
$modules/keycloak_clientscope_type.py:

View file

@ -31,6 +31,7 @@ action_groups:
- keycloak_client_rolescope
- keycloak_clientscope
- keycloak_clientscope_type
- keycloak_clientscope_rolemappings
- keycloak_clientsecret_info
- keycloak_clientsecret_regenerate
- keycloak_clienttemplate

View file

@ -62,6 +62,10 @@ 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}"
@ -3243,6 +3247,52 @@ 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.
:param realm: Realm from which to obtain the scope.
:return: The clientscope scope-mappings.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS.format(url=self.baseurl, realm=realm, id=clientscope_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}")
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.
:param realm: Realm from which to obtain the scope.
:return: The clientscope realm scope-mappings.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
url=self.baseurl, realm=realm, id=clientscope_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}"
)
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.
: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.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=clientscope_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}",
)
def get_client_role_scope_from_client(self, clientid, clientscopeid, 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.
@ -3258,6 +3308,50 @@ 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_clientscope_scope_mappings_client(
self, payload: list[dict], clientscope_id: str, client_id: str, realm: str = "master"
):
"""Update and fetch the client roles (scope-mappings) associated with the clientscope 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 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.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=clientscope_id, client=client_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 {client_id}.{clientscope_id} in realm {realm}: {e}",
)
return self.get_clientscope_scope_mappings_client(clientscope_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.
:param payload: List of realm roles to be added to the scope.
:param clientscope_id: ID of the clientscope to update scope-mappings.
:param realm: Realm from which to obtain the roles.
:return: The clientscope realm scope-mappings.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
url=self.baseurl, realm=realm, id=clientscope_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}"
)
return self.get_clientscope_scope_mappings_realm(clientscope_id, realm)
def update_client_role_scope_from_client(self, payload, clientid, clientscopeid, 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.
@ -3296,6 +3390,50 @@ class KeycloakAPI:
return self.get_client_role_scope_from_client(clientid, clientscopeid, realm)
def delete_clientscope_scope_mappings_client(
self, payload: list[dict], clientscope_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.
:param payload: List of roles to be deleted.
:param clientscope_id: ID of the clientscope 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.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=clientscope_id, client=client_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 {client_id}.{clientscope_id} in realm {realm}: {e}",
)
return self.get_clientscope_scope_mappings_client(clientscope_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.
:param payload: List of roles to be deleted.
:param clientscope_id: ID of the clientscope to delete roles from scope-mappings.
:param realm: Realm from which to obtain the roles.
:return: The clientscope realm scope-mappings.
"""
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
url=self.baseurl, realm=realm, id=clientscope_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}"
)
return self.get_clientscope_scope_mappings_realm(clientscope_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.
:param clientid: ID of the client from which to obtain the associated realm roles.

View file

@ -0,0 +1,279 @@
#!/usr/bin/python
# Copyright (c) Ansible project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: keycloak_clientscope_rolemappings
short_description: Allows administration of Keycloak clientscope scope mappings to restrict the usage of certain roles to
specific clientscopes
version_added: 13.1.0
description:
- This module allows you to add or remove Keycloak roles from clientscopes 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.
- 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.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the role mapping.
- On V(present), all roles in O(role_names) are mapped if not exist yet.
- On V(absent), all roles mapping in O(role_names) are removed if they exist.
default: 'present'
type: str
choices:
- present
- absent
realm:
type: str
description:
- The Keycloak realm under which clients resides.
default: 'master'
clientscope_id:
required: true
type: str
description:
- Roles provided in O(role_names) will be added to this clientscope.
client_id:
type: str
description:
- If the O(role_names) are client roles, the client ID under which it resides.
- If this parameter is absent, the roles are considered realm roles.
role_names:
required: true
type: list
elements: str
description:
- Names of roles to add.
- If O(client_id) is present, all roles must be under this client.
- If O(client_id) is absent, all roles must be under the realm.
extends_documentation_fragment:
- community.general._keycloak
- community.general._keycloak.actiongroup_keycloak
- community.general._attributes
author:
- Felix Grzelka (@felix-grzelka)
# This module was adapted from keycloak_client_rolescope, which was written by Andre Desrosiers (@desand01).
"""
EXAMPLES = r"""
- name: Add roles to clientscope
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
realm: MyCustomRealm
client_id: frontend-client-public
clientscope_id: frontend-clientscope
role_names:
- backend-role-admin
- backend-role-user
- name: Remove roles from clientscope
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
realm: MyCustomRealm
client_id: frontend-client-public
clientscope_id: frontend-clientscope
role_names:
- backend-role-admin
state: absent
- name: Add realm roles to clientscope
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
realm: MyCustomRealm
clientscope_id: frontend-clientscope
role_names:
- realm-role-admin
- realm-role-user
"""
RETURN = r"""
end_state:
description: Representation of clientscope scope mappings after module execution.
returned: on success
type: list
elements: dict
sample:
[
{
"clientRole": false,
"composite": false,
"containerId": "77f9bd4e-13a6-451e-9c72-ee6997299c1f",
"description": "User role",
"id": "9e155ef7-86f5-4def-b507-581ce7b87013",
"name": "realm-role-user"
},
{
"clientRole": false,
"composite": false,
"containerId": "77f9bd4e-13a6-451e-9c72-ee6997299c1f",
"description": "Admin role",
"id": "9e155ef7-86f5-4def-b507-581ce7b87013",
"name": "realm-role-admin"
}
]
"""
import copy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._keycloak import (
KeycloakAPI,
KeycloakError,
get_token,
keycloak_argument_spec,
)
def main():
argument_spec = keycloak_argument_spec()
meta_args = dict(
client_id=dict(type="str"),
clientscope_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"]),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
result = dict(changed=False, msg="", diff={}, end_state={})
# Obtain access token, initialize API
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params["realm"]
client_id = module.params["client_id"]
clientscope_id = module.params["clientscope_id"]
role_names = module.params["role_names"]
state = module.params["state"]
realm_object = kc.get_realm_by_id(realm)
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}'")
if client_id:
# add client role
client_object = kc.get_client_by_clientid(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":
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)
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)
available_roles_by_name = kc.get_realm_roles(realm)
# convert to indexed Dict by name
available_roles_by_name = {role["name"]: role for role in available_roles_by_name}
before_roles_by_name = {role["name"]: role for role in before_roles}
desired_roles = copy.deepcopy(before_roles)
changed_roles = []
if state == "present":
# update desired
for role_name in role_names:
if role_name not in available_roles_by_name:
if client_id:
module.fail_json(msg=f"Failed to retrieve role '{realm}.{client_id}.{role_name}'")
else:
module.fail_json(msg=f"Failed to retrieve role '{realm}.{role_name}'")
if role_name not in before_roles_by_name:
changed_roles.append(available_roles_by_name[role_name])
desired_roles.append(available_roles_by_name[role_name])
else:
# remove role if present
for role_name in role_names:
if role_name in before_roles_by_name:
changed_roles.append(before_roles_by_name[role_name])
desired_roles.remove(available_roles_by_name[role_name])
before_roles = sorted(before_roles, key=lambda d: d["name"])
desired_role_mapping = sorted(desired_roles, key=lambda d: d["name"])
result["changed"] = bool(changed_roles)
if module._diff:
result["diff"] = dict(before={"roles": before_roles}, after={"roles": desired_role_mapping})
if not result["changed"]:
# no changes
result["end_state"] = before_roles
result["msg"] = f"No changes required for clientscope {clientscope_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
)
else:
result["end_state"] = kc.update_clientscope_scope_mappings_realm(
changed_roles, clientscope_object["id"], realm
)
result["msg"] = f"Clientscope scope mappings for {clientscope_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
)
else:
result["end_state"] = kc.delete_clientscope_scope_mappings_realm(
changed_roles, clientscope_object["id"], realm
)
result["msg"] = f"Clientscope scope mappings for {clientscope_id} have been deleted"
module.exit_json(**result)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,20 @@
<!--
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
-->
# Running keycloak_clientscope_rolemappings module integration test
To run Keycloak component info module's integration test, start a keycloak server using Docker:
docker run -d --rm --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:latest start-dev --http-relative-path /auth
Run integration tests:
ansible-test integration -v keycloak_clientscope_rolemappings --allow-unsupported --docker fedora --docker-network host
Cleanup:
docker stop mykeycloak

View file

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
unsupported

View file

@ -0,0 +1,342 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Wait for Keycloak
uri:
url: "{{ url }}/admin/"
status_code: 200
validate_certs: false
register: result
until: result.status == 200
retries: 10
delay: 10
- name: Delete realm if exists
community.general.keycloak_realm:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
state: absent
- name: Create realm
community.general.keycloak_realm:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
id: "{{ realm }}"
realm: "{{ realm }}"
state: present
- name: Create a Keycloak realm role
community.general.keycloak_role:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
name: "{{ item }}"
realm: "{{ realm }}"
with_items:
- "{{ realm_role_admin }}"
- "{{ realm_role_user }}"
- name: Create client
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
state: present
public_client: true
full_scope_allowed: false
- name: Create full scope client
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ full_scope_client_name }}"
state: present
public_client: true
full_scope_allowed: true
- name: Create a Keycloak client roles
community.general.keycloak_role:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
name: "{{ item }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
with_items:
- "{{ client_role_admin }}"
- "{{ client_role_user }}"
- name: Create a Keycloak client roles in full scope client
community.general.keycloak_role:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
name: "{{ item }}"
realm: "{{ realm }}"
client_id: "{{ full_scope_client_name }}"
with_items:
- "{{ client_role_admin }}"
- "{{ client_role_user }}"
- name: Create clientscopes
community.general.keycloak_clientscope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
name: "{{ clientscope_id }}"
description: ""
protocol: "openid-connect"
state: "present"
- name: Map client roles
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ client_role_admin }}"
- "{{ client_role_user }}"
register: result
- name: Assert mapping created
assert:
that:
- result is changed
- result.end_state | length == 2
- name: Remap the user client role
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ client_role_user }}"
register: result
- name: Assert no change
assert:
that:
- result is not changed
- result.end_state | length == 2
- name: Remove admin role
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ client_role_admin }}"
state: absent
register: result
- name: Assert mapping deleted
assert:
that:
- result is changed
- result.end_state | length == 1
- result.end_state[0].name == client_role_user
- name: Map non exisiting client role
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ client_role_admin }}"
- "{{ client_role_not_exists }}"
ignore_errors: true
register: result
- name: Assert failed mapping missing role
assert:
that:
- result is failed
- "result.msg == 'Failed to retrieve role \\'myrealm.backend-client.client-role-missing\\''"
- name: Map roles duplicate
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
client_id: "{{ client_name }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ client_role_admin }}"
- "{{ client_role_admin }}"
register: result
- name: Assert result
assert:
that:
- result is changed
- result.end_state | length == 2
- name: Map full scope client roles
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
client_id: "{{ full_scope_client_name }}"
role_names:
- "{{ client_role_admin }}"
ignore_errors: true
register: result
- name: Assert failed mapping role to full scope client
assert:
that:
- result is failed
- "result.msg == 'FullScopeAllowed is active for Client \\'myrealm.full-scope-client\\''"
- name: Map realm role
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ realm_role_admin }}"
register: result
- name: Assert result
assert:
that:
- result is changed
- result.end_state | length == 1
- name: Map two realm roles
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ realm_role_admin }}"
- "{{ realm_role_user }}"
register: result
- name: Assert result
assert:
that:
- result is changed
- result.end_state | length == 2
- name: Unmap all realm roles
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ realm_role_admin }}"
- "{{ realm_role_user }}"
state: absent
register: result
- name: Assert result
assert:
that:
- result is changed
- result.end_state | length == 0
- name: Map non exisiting realm role
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ realm_role_not_exists }}"
ignore_errors: true
register: result
- name: Assert failed mapping missing realm role
assert:
that:
- result is failed
- "result.msg == 'Failed to retrieve role \\'myrealm.client-role-missing\\''"
- name: Check-mode try to Map realm roles to public client
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names:
- "{{ realm_role_admin }}"
- "{{ realm_role_user }}"
check_mode: true
register: result
- name: Assert result
assert:
that:
- result is changed
- result.end_state | length == 2
- name: Check-mode step two, check if change where applied
community.general.keycloak_clientscope_rolemappings:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"
clientscope_id: "{{ clientscope_id }}"
role_names: []
register: result
- name: Assert result
assert:
that:
- result is not changed
- result.end_state | length == 0

View file

@ -0,0 +1,26 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
url: http://localhost:8080/auth
admin_realm: master
admin_user: admin
admin_password: password
realm: myrealm
client_name: backend-client
full_scope_client_name: full-scope-client
client_role_admin: client-role-admin
client_role_user: client-role-user
client_role_not_exists: client-role-missing
clientscope_id: "client-scope"
realm_role_admin: realm-role-admin
realm_role_user: realm-role-user
realm_role_not_exists: client-role-missing
client_attributes1: {"backchannel.logout.session.required": true, "backchannel.logout.revoke.offline.tokens": false, "client.secret.creation.time": 0}