mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New Module: Keycloak User Rolemapping (#4898)
* keycloak_user_rolemapping: implement user role mapping * keycloak_user_rolemapping: additional logging * keycloak_user_rolemapping: move to getters, use names parameters * keycloak_user_rolemapping: add service account user example * Add keyring and keyring_info modules (#4764) * keycloak_user_rolemapping: write tests, address ansibullbot concerns no.1 * keycloak_user_rolemapping: address felixfontein concerns no.1 * keycloak_user_rolemapping: remove rebase mistakes * keycloak_user_rolemapping: address felixfontein concerns no.2 * keycloak_user_rolemapping: refactor duplicate username usage example * keycloak_user_rolemapping: fix sanity check errors no.1 * keycloak_user_rolemapping: fix sanity check errors no.2 * keycloak_user_rolemapping: fix sanity check errors no.3 * keycloak_user_rolemapping: fix sanity check errors no.4 * keycloak_user_rolemapping: write tests, address ansibullbot concerns no.1 * keycloak_user_rolemapping: resolve rebase conflicts with origin/main branch # Conflicts: # plugins/module_utils/identity/keycloak/keycloak.py * keycloak_user_rolemapping: remove keycloak_role_composites from BOTMETA.yml * keycloak_user_rolemapping: fix sanity check errors no.5 * keycloak_user_rolemapping: address felixfontein reviews concerns no.1 * keycloak_user_rolemapping: address felixfontein reviews concerns no.2 Co-authored-by: Dušan Markovič <dusan.markovic@better.care> Co-authored-by: ahussey-redhat <93101976+ahussey-redhat@users.noreply.github.com>
This commit is contained in:
parent
2eba5dc4e7
commit
2cac3ae879
9 changed files with 916 additions and 90 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -589,6 +589,8 @@ files:
|
||||||
maintainers: Gaetan2907
|
maintainers: Gaetan2907
|
||||||
$modules/identity/keycloak/keycloak_client_rolemapping.py:
|
$modules/identity/keycloak/keycloak_client_rolemapping.py:
|
||||||
maintainers: Gaetan2907
|
maintainers: Gaetan2907
|
||||||
|
$modules/identity/keycloak/keycloak_user_rolemapping.py:
|
||||||
|
maintainers: bratwurzt
|
||||||
$modules/identity/keycloak/keycloak_group.py:
|
$modules/identity/keycloak/keycloak_group.py:
|
||||||
maintainers: adamgoossens
|
maintainers: adamgoossens
|
||||||
$modules/identity/keycloak/keycloak_identity_provider.py:
|
$modules/identity/keycloak/keycloak_identity_provider.py:
|
||||||
|
|
|
@ -612,6 +612,8 @@ plugin_routing:
|
||||||
redirect: community.general.identity.keycloak.keycloak_role
|
redirect: community.general.identity.keycloak.keycloak_role
|
||||||
keycloak_user_federation:
|
keycloak_user_federation:
|
||||||
redirect: community.general.identity.keycloak.keycloak_user_federation
|
redirect: community.general.identity.keycloak.keycloak_user_federation
|
||||||
|
keycloak_user_rolemapping:
|
||||||
|
redirect: community.general.identity.keycloak.keycloak_user_rolemapping
|
||||||
keyring:
|
keyring:
|
||||||
redirect: community.general.system.keyring
|
redirect: community.general.system.keyring
|
||||||
keyring_info:
|
keyring_info:
|
||||||
|
|
|
@ -29,8 +29,15 @@ URL_CLIENT_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/clients/{id}/roles/{nam
|
||||||
|
|
||||||
URL_REALM_ROLES = "{url}/admin/realms/{realm}/roles"
|
URL_REALM_ROLES = "{url}/admin/realms/{realm}/roles"
|
||||||
URL_REALM_ROLE = "{url}/admin/realms/{realm}/roles/{name}"
|
URL_REALM_ROLE = "{url}/admin/realms/{realm}/roles/{name}"
|
||||||
|
URL_REALM_ROLEMAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/realm"
|
||||||
|
URL_REALM_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/realm/available"
|
||||||
|
URL_REALM_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/realm/composite"
|
||||||
URL_REALM_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/roles/{name}/composites"
|
URL_REALM_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/roles/{name}/composites"
|
||||||
|
|
||||||
|
URL_ROLES_BY_ID = "{url}/admin/realms/{realm}/roles-by-id/{id}"
|
||||||
|
URL_ROLES_BY_ID_COMPOSITES_CLIENTS = "{url}/admin/realms/{realm}/roles-by-id/{id}/composites/clients/{cid}"
|
||||||
|
URL_ROLES_BY_ID_COMPOSITES = "{url}/admin/realms/{realm}/roles-by-id/{id}/composites"
|
||||||
|
|
||||||
URL_CLIENTTEMPLATE = "{url}/admin/realms/{realm}/client-templates/{id}"
|
URL_CLIENTTEMPLATE = "{url}/admin/realms/{realm}/client-templates/{id}"
|
||||||
URL_CLIENTTEMPLATES = "{url}/admin/realms/{realm}/client-templates"
|
URL_CLIENTTEMPLATES = "{url}/admin/realms/{realm}/client-templates"
|
||||||
URL_GROUPS = "{url}/admin/realms/{realm}/groups"
|
URL_GROUPS = "{url}/admin/realms/{realm}/groups"
|
||||||
|
@ -41,9 +48,15 @@ URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}"
|
||||||
URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models"
|
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_CLIENTSCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}"
|
||||||
|
|
||||||
URL_CLIENT_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}"
|
URL_CLIENT_GROUP_ROLEMAPPINGS = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}"
|
||||||
URL_CLIENT_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/available"
|
URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/available"
|
||||||
URL_CLIENT_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/composite"
|
URL_CLIENT_GROUP_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/groups/{id}/role-mappings/clients/{client}/composite"
|
||||||
|
|
||||||
|
URL_USERS = "{url}/admin/realms/{realm}/users"
|
||||||
|
URL_CLIENT_SERVICE_ACCOUNT_USER = "{url}/admin/realms/{realm}/clients/{id}/service-account-user"
|
||||||
|
URL_CLIENT_USER_ROLEMAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}"
|
||||||
|
URL_CLIENT_USER_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/available"
|
||||||
|
URL_CLIENT_USER_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/composite"
|
||||||
|
|
||||||
URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows"
|
URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows"
|
||||||
URL_AUTHENTICATION_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{id}"
|
URL_AUTHENTICATION_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{id}"
|
||||||
|
@ -446,10 +459,9 @@ class KeycloakAPI(object):
|
||||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
||||||
% (cid, realm, str(e)))
|
% (cid, realm, str(e)))
|
||||||
|
|
||||||
def get_client_role_by_name(self, gid, cid, name, realm="master"):
|
def get_client_role_id_by_name(self, cid, name, realm="master"):
|
||||||
""" Get the role ID of a client.
|
""" Get the role ID of a client.
|
||||||
|
|
||||||
:param gid: ID of the group from which to obtain the rolemappings.
|
|
||||||
:param cid: ID of the client from which to obtain the rolemappings.
|
:param cid: ID of the client from which to obtain the rolemappings.
|
||||||
:param name: Name of the role.
|
:param name: Name of the role.
|
||||||
:param realm: Realm from which to obtain the rolemappings.
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
@ -461,7 +473,7 @@ class KeycloakAPI(object):
|
||||||
return role['id']
|
return role['id']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_client_rolemapping_by_id(self, gid, cid, rid, realm='master'):
|
def get_client_group_rolemapping_by_id(self, gid, cid, rid, realm='master'):
|
||||||
""" Obtain client representation by id
|
""" Obtain client representation by id
|
||||||
|
|
||||||
:param gid: ID of the group from which to obtain the rolemappings.
|
:param gid: ID of the group from which to obtain the rolemappings.
|
||||||
|
@ -470,7 +482,7 @@ class KeycloakAPI(object):
|
||||||
:param realm: client from this realm
|
:param realm: client from this realm
|
||||||
:return: dict of rolemapping representation or None if none matching exist
|
:return: dict of rolemapping representation or None if none matching exist
|
||||||
"""
|
"""
|
||||||
rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||||
try:
|
try:
|
||||||
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
timeout=self.connection_timeout,
|
timeout=self.connection_timeout,
|
||||||
|
@ -483,7 +495,7 @@ class KeycloakAPI(object):
|
||||||
% (cid, gid, realm, str(e)))
|
% (cid, gid, realm, str(e)))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_client_available_rolemappings(self, gid, cid, realm="master"):
|
def get_client_group_available_rolemappings(self, gid, cid, realm="master"):
|
||||||
""" Fetch the available role of a client in a specified goup on the Keycloak server.
|
""" Fetch the available role of a client in a specified goup on the Keycloak server.
|
||||||
|
|
||||||
:param gid: ID of the group from which to obtain the rolemappings.
|
:param gid: ID of the group from which to obtain the rolemappings.
|
||||||
|
@ -491,7 +503,7 @@ class KeycloakAPI(object):
|
||||||
:param realm: Realm from which to obtain the rolemappings.
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
:return: The rollemappings of specified group and client of the realm (default "master").
|
:return: The rollemappings of specified group and client of the realm (default "master").
|
||||||
"""
|
"""
|
||||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
available_rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||||
try:
|
try:
|
||||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
timeout=self.connection_timeout,
|
timeout=self.connection_timeout,
|
||||||
|
@ -500,7 +512,7 @@ class KeycloakAPI(object):
|
||||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||||
% (cid, gid, realm, str(e)))
|
% (cid, gid, realm, str(e)))
|
||||||
|
|
||||||
def get_client_composite_rolemappings(self, gid, cid, realm="master"):
|
def get_client_group_composite_rolemappings(self, gid, cid, realm="master"):
|
||||||
""" Fetch the composite role of a client in a specified group on the Keycloak server.
|
""" Fetch the composite role of a client in a specified group on the Keycloak server.
|
||||||
|
|
||||||
:param gid: ID of the group from which to obtain the rolemappings.
|
:param gid: ID of the group from which to obtain the rolemappings.
|
||||||
|
@ -508,15 +520,64 @@ class KeycloakAPI(object):
|
||||||
:param realm: Realm from which to obtain the rolemappings.
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
:return: The rollemappings of specified group and client of the realm (default "master").
|
:return: The rollemappings of specified group and client of the realm (default "master").
|
||||||
"""
|
"""
|
||||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
composite_rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||||
try:
|
try:
|
||||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
return json.loads(to_native(open_url(composite_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
timeout=self.connection_timeout,
|
timeout=self.connection_timeout,
|
||||||
validate_certs=self.validate_certs).read()))
|
validate_certs=self.validate_certs).read()))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||||
% (cid, gid, realm, str(e)))
|
% (cid, gid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_role_by_id(self, rid, realm="master"):
|
||||||
|
""" Fetch a role by its id on the Keycloak server.
|
||||||
|
|
||||||
|
:param rid: ID of the role.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The role.
|
||||||
|
"""
|
||||||
|
client_roles_url = URL_ROLES_BY_ID.format(url=self.baseurl, realm=realm, id=rid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(client_roles_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch role for id %s in realm %s: %s"
|
||||||
|
% (rid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_client_roles_by_id_composite_rolemappings(self, rid, cid, realm="master"):
|
||||||
|
""" Fetch a role by its id on the Keycloak server.
|
||||||
|
|
||||||
|
:param rid: ID of the composite role.
|
||||||
|
:param cid: ID of the client from which to obtain the rolemappings.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The role.
|
||||||
|
"""
|
||||||
|
client_roles_url = URL_ROLES_BY_ID_COMPOSITES_CLIENTS.format(url=self.baseurl, realm=realm, id=rid, cid=cid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(client_roles_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch role for id %s and cid %s in realm %s: %s"
|
||||||
|
% (rid, cid, realm, str(e)))
|
||||||
|
|
||||||
|
def add_client_roles_by_id_composite_rolemapping(self, rid, roles_rep, realm="master"):
|
||||||
|
""" Assign roles to composite role
|
||||||
|
|
||||||
|
:param rid: ID of the composite role.
|
||||||
|
:param roles_rep: Representation of the roles to assign.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
available_rolemappings_url = URL_ROLES_BY_ID_COMPOSITES.format(url=self.baseurl, realm=realm, id=rid)
|
||||||
|
try:
|
||||||
|
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(roles_rep),
|
||||||
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not assign roles to composite role %s and realm %s: %s"
|
||||||
|
% (rid, realm, str(e)))
|
||||||
|
|
||||||
def add_group_rolemapping(self, gid, cid, role_rep, realm="master"):
|
def add_group_rolemapping(self, gid, cid, role_rep, realm="master"):
|
||||||
""" Fetch the composite role of a client in a specified goup on the Keycloak server.
|
""" Fetch the composite role of a client in a specified goup on the Keycloak server.
|
||||||
|
|
||||||
|
@ -526,7 +587,7 @@ class KeycloakAPI(object):
|
||||||
:param realm: Realm from which to obtain the rolemappings.
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
available_rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||||
try:
|
try:
|
||||||
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
@ -543,7 +604,7 @@ class KeycloakAPI(object):
|
||||||
:param realm: Realm from which to obtain the rolemappings.
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
available_rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||||
try:
|
try:
|
||||||
open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders,
|
open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
@ -551,6 +612,206 @@ class KeycloakAPI(object):
|
||||||
self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
||||||
% (cid, gid, realm, str(e)))
|
% (cid, gid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_client_user_rolemapping_by_id(self, uid, cid, rid, realm='master'):
|
||||||
|
""" Obtain client representation by id
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param cid: ID of the client from which to obtain the rolemappings.
|
||||||
|
:param rid: ID of the role.
|
||||||
|
:param realm: client from this realm
|
||||||
|
:return: dict of rolemapping representation or None if none matching exist
|
||||||
|
"""
|
||||||
|
rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||||
|
try:
|
||||||
|
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
for role in rolemappings:
|
||||||
|
if rid == role['id']:
|
||||||
|
return role
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch rolemappings for client %s and user %s, realm %s: %s"
|
||||||
|
% (cid, uid, realm, str(e)))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_client_user_available_rolemappings(self, uid, cid, realm="master"):
|
||||||
|
""" Fetch the available role of a client for a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param cid: ID of the client from which to obtain the rolemappings.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The effective rollemappings of specified client and user of the realm (default "master").
|
||||||
|
"""
|
||||||
|
available_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch effective rolemappings for client %s and user %s, realm %s: %s"
|
||||||
|
% (cid, uid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_client_user_composite_rolemappings(self, uid, cid, realm="master"):
|
||||||
|
""" Fetch the composite role of a client for a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param cid: ID of the client from which to obtain the rolemappings.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The rollemappings of specified group and client of the realm (default "master").
|
||||||
|
"""
|
||||||
|
composite_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(composite_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||||
|
% (uid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_realm_user_rolemapping_by_id(self, uid, rid, realm='master'):
|
||||||
|
""" Obtain role representation by id
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param rid: ID of the role.
|
||||||
|
:param realm: client from this realm
|
||||||
|
:return: dict of rolemapping representation or None if none matching exist
|
||||||
|
"""
|
||||||
|
rolemappings_url = URL_REALM_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid)
|
||||||
|
try:
|
||||||
|
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
for role in rolemappings:
|
||||||
|
if rid == role['id']:
|
||||||
|
return role
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch rolemappings for user %s, realm %s: %s"
|
||||||
|
% (uid, realm, str(e)))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_realm_user_available_rolemappings(self, uid, realm="master"):
|
||||||
|
""" Fetch the available role of a realm for a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The rollemappings of specified group and client of the realm (default "master").
|
||||||
|
"""
|
||||||
|
available_rolemappings_url = URL_REALM_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=uid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch available rolemappings for user %s of realm %s: %s"
|
||||||
|
% (uid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_realm_user_composite_rolemappings(self, uid, realm="master"):
|
||||||
|
""" Fetch the composite role of a realm for a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to obtain the rolemappings.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: The effective rollemappings of specified client and user of the realm (default "master").
|
||||||
|
"""
|
||||||
|
composite_rolemappings_url = URL_REALM_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=uid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(composite_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
|
timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not fetch effective rolemappings for user %s, realm %s: %s"
|
||||||
|
% (uid, realm, str(e)))
|
||||||
|
|
||||||
|
def get_user_by_username(self, username, realm="master"):
|
||||||
|
""" Fetch a keycloak user within a realm based on its username.
|
||||||
|
|
||||||
|
If the user does not exist, None is returned.
|
||||||
|
:param username: Username of the user to fetch.
|
||||||
|
:param realm: Realm in which the user resides; default 'master'
|
||||||
|
"""
|
||||||
|
users_url = URL_USERS.format(url=self.baseurl, realm=realm)
|
||||||
|
users_url += '?username=%s&exact=true' % username
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(users_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except ValueError as e:
|
||||||
|
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the user for realm %s and username %s: %s'
|
||||||
|
% (realm, username, str(e)))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg='Could not obtain the user for realm %s and username %s: %s'
|
||||||
|
% (realm, username, str(e)))
|
||||||
|
|
||||||
|
def get_service_account_user_by_client_id(self, client_id, realm="master"):
|
||||||
|
""" Fetch a keycloak service account user within a realm based on its client_id.
|
||||||
|
|
||||||
|
If the user does not exist, None is returned.
|
||||||
|
:param client_id: clientId of the service account user to fetch.
|
||||||
|
:param realm: Realm in which the user resides; default 'master'
|
||||||
|
"""
|
||||||
|
cid = self.get_client_id(client_id, realm=realm)
|
||||||
|
|
||||||
|
service_account_user_url = URL_CLIENT_SERVICE_ACCOUNT_USER.format(url=self.baseurl, realm=realm, id=cid)
|
||||||
|
try:
|
||||||
|
return json.loads(to_native(open_url(service_account_user_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||||
|
validate_certs=self.validate_certs).read()))
|
||||||
|
except ValueError as e:
|
||||||
|
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain the service-account-user for realm %s and client_id %s: %s'
|
||||||
|
% (realm, client_id, str(e)))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg='Could not obtain the service-account-user for realm %s and client_id %s: %s'
|
||||||
|
% (realm, client_id, str(e)))
|
||||||
|
|
||||||
|
def add_user_rolemapping(self, uid, cid, role_rep, realm="master"):
|
||||||
|
""" Assign a realm or client role to a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user roles are assigned to.
|
||||||
|
:param cid: ID of the client from which to obtain the rolemappings. If empty, roles are from the realm
|
||||||
|
:param role_rep: Representation of the role to assign.
|
||||||
|
:param realm: Realm from which to obtain the rolemappings.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
if cid is None:
|
||||||
|
user_realm_rolemappings_url = URL_REALM_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid)
|
||||||
|
try:
|
||||||
|
open_url(user_realm_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||||
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not map roles to userId %s for realm %s and roles %s: %s"
|
||||||
|
% (uid, realm, json.dumps(role_rep), str(e)))
|
||||||
|
else:
|
||||||
|
user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||||
|
try:
|
||||||
|
open_url(user_client_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||||
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not map roles to userId %s for client %s, realm %s and roles %s: %s"
|
||||||
|
% (cid, uid, realm, json.dumps(role_rep), str(e)))
|
||||||
|
|
||||||
|
def delete_user_rolemapping(self, uid, cid, role_rep, realm="master"):
|
||||||
|
""" Delete the rolemapping of a client in a specified user on the Keycloak server.
|
||||||
|
|
||||||
|
:param uid: ID of the user from which to remove the rolemappings.
|
||||||
|
:param cid: ID of the client from which to remove the rolemappings.
|
||||||
|
:param role_rep: Representation of the role to remove from rolemappings.
|
||||||
|
:param realm: Realm from which to remove the rolemappings.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
if cid is None:
|
||||||
|
user_realm_rolemappings_url = URL_REALM_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid)
|
||||||
|
try:
|
||||||
|
open_url(user_realm_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||||
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not remove roles %s from userId %s, realm %s: %s"
|
||||||
|
% (json.dumps(role_rep), uid, realm, str(e)))
|
||||||
|
else:
|
||||||
|
user_client_rolemappings_url = URL_CLIENT_USER_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=uid, client=cid)
|
||||||
|
try:
|
||||||
|
open_url(user_client_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||||
|
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Could not remove roles %s for client %s from userId %s, realm %s: %s"
|
||||||
|
% (json.dumps(role_rep), cid, uid, realm, str(e)))
|
||||||
|
|
||||||
def get_client_templates(self, realm='master'):
|
def get_client_templates(self, realm='master'):
|
||||||
""" Obtains client template representations for client templates in a realm
|
""" Obtains client template representations for client templates in a realm
|
||||||
|
|
||||||
|
@ -930,7 +1191,6 @@ class KeycloakAPI(object):
|
||||||
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||||
timeout=self.connection_timeout,
|
timeout=self.connection_timeout,
|
||||||
validate_certs=self.validate_certs).read()))
|
validate_certs=self.validate_certs).read()))
|
||||||
|
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
if e.code == 404:
|
if e.code == 404:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -279,20 +279,20 @@ def main():
|
||||||
module.fail_json(msg='Either the `name` or `id` has to be specified on each role.')
|
module.fail_json(msg='Either the `name` or `id` has to be specified on each role.')
|
||||||
# Fetch missing role_id
|
# Fetch missing role_id
|
||||||
if role['id'] is None:
|
if role['id'] is None:
|
||||||
role_id = kc.get_client_role_by_name(gid, cid, role['name'], realm=realm)
|
role_id = kc.get_client_role_id_by_name(cid, role['name'], realm=realm)
|
||||||
if role_id is not None:
|
if role_id is not None:
|
||||||
role['id'] = role_id
|
role['id'] = role_id
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='Could not fetch role %s:' % (role['name']))
|
module.fail_json(msg='Could not fetch role %s:' % (role['name']))
|
||||||
# Fetch missing role_name
|
# Fetch missing role_name
|
||||||
else:
|
else:
|
||||||
role['name'] = kc.get_client_rolemapping_by_id(gid, cid, role['id'], realm=realm)['name']
|
role['name'] = kc.get_client_group_rolemapping_by_id(gid, cid, role['id'], realm=realm)['name']
|
||||||
if role['name'] is None:
|
if role['name'] is None:
|
||||||
module.fail_json(msg='Could not fetch role %s' % (role['id']))
|
module.fail_json(msg='Could not fetch role %s' % (role['id']))
|
||||||
|
|
||||||
# Get effective client-level role mappings
|
# Get effective client-level role mappings
|
||||||
available_roles_before = kc.get_client_available_rolemappings(gid, cid, realm=realm)
|
available_roles_before = kc.get_client_group_available_rolemappings(gid, cid, realm=realm)
|
||||||
assigned_roles_before = kc.get_client_composite_rolemappings(gid, cid, realm=realm)
|
assigned_roles_before = kc.get_client_group_composite_rolemappings(gid, cid, realm=realm)
|
||||||
|
|
||||||
result['existing'] = assigned_roles_before
|
result['existing'] = assigned_roles_before
|
||||||
result['proposed'] = roles
|
result['proposed'] = roles
|
||||||
|
@ -326,7 +326,7 @@ def main():
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
kc.add_group_rolemapping(gid, cid, update_roles, realm=realm)
|
kc.add_group_rolemapping(gid, cid, update_roles, realm=realm)
|
||||||
result['msg'] = 'Roles %s assigned to group %s.' % (update_roles, group_name)
|
result['msg'] = 'Roles %s assigned to group %s.' % (update_roles, group_name)
|
||||||
assigned_roles_after = kc.get_client_composite_rolemappings(gid, cid, realm=realm)
|
assigned_roles_after = kc.get_client_group_composite_rolemappings(gid, cid, realm=realm)
|
||||||
result['end_state'] = assigned_roles_after
|
result['end_state'] = assigned_roles_after
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
else:
|
else:
|
||||||
|
@ -338,7 +338,7 @@ def main():
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
kc.delete_group_rolemapping(gid, cid, update_roles, realm=realm)
|
kc.delete_group_rolemapping(gid, cid, update_roles, realm=realm)
|
||||||
result['msg'] = 'Roles %s removed from group %s.' % (update_roles, group_name)
|
result['msg'] = 'Roles %s removed from group %s.' % (update_roles, group_name)
|
||||||
assigned_roles_after = kc.get_client_composite_rolemappings(gid, cid, realm=realm)
|
assigned_roles_after = kc.get_client_group_composite_rolemappings(gid, cid, realm=realm)
|
||||||
result['end_state'] = assigned_roles_after
|
result['end_state'] = assigned_roles_after
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
# Do nothing
|
# Do nothing
|
||||||
|
|
401
plugins/modules/identity/keycloak/keycloak_user_rolemapping.py
Normal file
401
plugins/modules/identity/keycloak/keycloak_user_rolemapping.py
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2022, Dušan Marković (@bratwurzt)
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: keycloak_user_rolemapping
|
||||||
|
|
||||||
|
short_description: Allows administration of Keycloak user_rolemapping with the Keycloak API
|
||||||
|
|
||||||
|
version_added: 5.7.0
|
||||||
|
|
||||||
|
description:
|
||||||
|
- This module allows you to add, remove or modify Keycloak user_rolemapping with the Keycloak REST API.
|
||||||
|
It requires access to the REST API via 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.
|
||||||
|
|
||||||
|
- The names of module options are snake_cased versions of the camelCase ones found in the
|
||||||
|
Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html).
|
||||||
|
|
||||||
|
- Attributes are multi-valued in the Keycloak API. All attributes are lists of individual values and will
|
||||||
|
be returned that way by this module. You may pass single values for attributes when calling the module,
|
||||||
|
and this will be translated into a list suitable for the API.
|
||||||
|
|
||||||
|
- When updating a user_rolemapping, where possible provide the role ID to the module. This removes a lookup
|
||||||
|
to the API to translate the name into the role ID.
|
||||||
|
|
||||||
|
|
||||||
|
options:
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- State of the user_rolemapping.
|
||||||
|
- On C(present), the user_rolemapping will be created if it does not yet exist, or updated with the parameters you provide.
|
||||||
|
- On C(absent), the user_rolemapping will be removed if it exists.
|
||||||
|
default: 'present'
|
||||||
|
type: str
|
||||||
|
choices:
|
||||||
|
- present
|
||||||
|
- absent
|
||||||
|
|
||||||
|
realm:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- They Keycloak realm under which this role_representation resides.
|
||||||
|
default: 'master'
|
||||||
|
|
||||||
|
target_username:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- Username of the user roles are mapped to.
|
||||||
|
- This parameter is not required (can be replaced by uid for less API call).
|
||||||
|
|
||||||
|
uid:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- ID of the user to be mapped.
|
||||||
|
- This parameter is not required for updating or deleting the rolemapping but
|
||||||
|
providing it will reduce the number of API calls required.
|
||||||
|
|
||||||
|
service_account_user_client_id:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- Client ID of the service-account-user to be mapped.
|
||||||
|
- This parameter is not required for updating or deleting the rolemapping but
|
||||||
|
providing it will reduce the number of API calls required.
|
||||||
|
|
||||||
|
client_id:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- Name of the client to be mapped (different than I(cid)).
|
||||||
|
- This parameter is required if I(cid) is not provided (can be replaced by I(cid)
|
||||||
|
to reduce the number of API calls that must be made).
|
||||||
|
|
||||||
|
cid:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- ID of the client to be mapped.
|
||||||
|
- This parameter is not required for updating or deleting the rolemapping but
|
||||||
|
providing it will reduce the number of API calls required.
|
||||||
|
|
||||||
|
roles:
|
||||||
|
description:
|
||||||
|
- Roles to be mapped to the user.
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
suboptions:
|
||||||
|
name:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- Name of the role representation.
|
||||||
|
- This parameter is required only when creating or updating the role_representation.
|
||||||
|
id:
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
- The unique identifier for this role_representation.
|
||||||
|
- This parameter is not required for updating or deleting a role_representation but
|
||||||
|
providing it will reduce the number of API calls required.
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- community.general.keycloak
|
||||||
|
|
||||||
|
|
||||||
|
author:
|
||||||
|
- Dušan Marković (@bratwurzt)
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
- name: Map a client role to a user, authentication with credentials
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
realm: MyCustomRealm
|
||||||
|
auth_client_id: admin-cli
|
||||||
|
auth_keycloak_url: https://auth.example.com/auth
|
||||||
|
auth_realm: master
|
||||||
|
auth_username: USERNAME
|
||||||
|
auth_password: PASSWORD
|
||||||
|
state: present
|
||||||
|
client_id: client1
|
||||||
|
user_id: user1Id
|
||||||
|
roles:
|
||||||
|
- name: role_name1
|
||||||
|
id: role_id1
|
||||||
|
- name: role_name2
|
||||||
|
id: role_id2
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Map a client role to a service account user for a client, authentication with credentials
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
realm: MyCustomRealm
|
||||||
|
auth_client_id: admin-cli
|
||||||
|
auth_keycloak_url: https://auth.example.com/auth
|
||||||
|
auth_realm: master
|
||||||
|
auth_username: USERNAME
|
||||||
|
auth_password: PASSWORD
|
||||||
|
state: present
|
||||||
|
client_id: client1
|
||||||
|
service_account_user_client_id: clientIdOfServiceAccount
|
||||||
|
roles:
|
||||||
|
- name: role_name1
|
||||||
|
id: role_id1
|
||||||
|
- name: role_name2
|
||||||
|
id: role_id2
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Map a client role to a user, authentication with token
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
realm: MyCustomRealm
|
||||||
|
auth_client_id: admin-cli
|
||||||
|
auth_keycloak_url: https://auth.example.com/auth
|
||||||
|
token: TOKEN
|
||||||
|
state: present
|
||||||
|
client_id: client1
|
||||||
|
target_username: user1
|
||||||
|
roles:
|
||||||
|
- name: role_name1
|
||||||
|
id: role_id1
|
||||||
|
- name: role_name2
|
||||||
|
id: role_id2
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Unmap client role from a user
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
realm: MyCustomRealm
|
||||||
|
auth_client_id: admin-cli
|
||||||
|
auth_keycloak_url: https://auth.example.com/auth
|
||||||
|
auth_realm: master
|
||||||
|
auth_username: USERNAME
|
||||||
|
auth_password: PASSWORD
|
||||||
|
state: absent
|
||||||
|
client_id: client1
|
||||||
|
uid: 70e3ae72-96b6-11e6-9056-9737fd4d0764
|
||||||
|
roles:
|
||||||
|
- name: role_name1
|
||||||
|
id: role_id1
|
||||||
|
- name: role_name2
|
||||||
|
id: role_id2
|
||||||
|
delegate_to: localhost
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
msg:
|
||||||
|
description: Message as to what action was taken.
|
||||||
|
returned: always
|
||||||
|
type: str
|
||||||
|
sample: "Role role1 assigned to user user1."
|
||||||
|
|
||||||
|
proposed:
|
||||||
|
description: Representation of proposed client role mapping.
|
||||||
|
returned: always
|
||||||
|
type: dict
|
||||||
|
sample: {
|
||||||
|
clientId: "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
existing:
|
||||||
|
description:
|
||||||
|
- Representation of existing client role mapping.
|
||||||
|
- The sample is truncated.
|
||||||
|
returned: always
|
||||||
|
type: dict
|
||||||
|
sample: {
|
||||||
|
"adminUrl": "http://www.example.com/admin_url",
|
||||||
|
"attributes": {
|
||||||
|
"request.object.signature.alg": "RS256",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end_state:
|
||||||
|
description:
|
||||||
|
- Representation of client role mapping after module execution.
|
||||||
|
- The sample is truncated.
|
||||||
|
returned: on success
|
||||||
|
type: dict
|
||||||
|
sample: {
|
||||||
|
"adminUrl": "http://www.example.com/admin_url",
|
||||||
|
"attributes": {
|
||||||
|
"request.object.signature.alg": "RS256",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
|
||||||
|
keycloak_argument_spec, get_token, KeycloakError, is_struct_included
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Module execution
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
argument_spec = keycloak_argument_spec()
|
||||||
|
|
||||||
|
roles_spec = dict(
|
||||||
|
name=dict(type='str'),
|
||||||
|
id=dict(type='str'),
|
||||||
|
)
|
||||||
|
|
||||||
|
meta_args = dict(
|
||||||
|
state=dict(default='present', choices=['present', 'absent']),
|
||||||
|
realm=dict(default='master'),
|
||||||
|
uid=dict(type='str'),
|
||||||
|
target_username=dict(type='str'),
|
||||||
|
service_account_user_client_id=dict(type='str'),
|
||||||
|
cid=dict(type='str'),
|
||||||
|
client_id=dict(type='str'),
|
||||||
|
roles=dict(type='list', elements='dict', options=roles_spec),
|
||||||
|
)
|
||||||
|
|
||||||
|
argument_spec.update(meta_args)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
required_one_of=([['token', 'auth_realm', 'auth_username', 'auth_password'],
|
||||||
|
['uid', 'target_username', 'service_account_user_client_id']]),
|
||||||
|
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
|
||||||
|
|
||||||
|
result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, 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.get('realm')
|
||||||
|
state = module.params.get('state')
|
||||||
|
cid = module.params.get('cid')
|
||||||
|
client_id = module.params.get('client_id')
|
||||||
|
uid = module.params.get('uid')
|
||||||
|
target_username = module.params.get('target_username')
|
||||||
|
service_account_user_client_id = module.params.get('service_account_user_client_id')
|
||||||
|
roles = module.params.get('roles')
|
||||||
|
|
||||||
|
# Check the parameters
|
||||||
|
if uid is None and target_username is None and service_account_user_client_id is None:
|
||||||
|
module.fail_json(msg='Either the `target_username`, `uid` or `service_account_user_client_id` has to be specified.')
|
||||||
|
|
||||||
|
# Get the potential missing parameters
|
||||||
|
if uid is None and service_account_user_client_id is None:
|
||||||
|
user_rep = kc.get_user_by_username(username=target_username, realm=realm)
|
||||||
|
if user_rep is not None:
|
||||||
|
uid = user_rep.get('id')
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Could not fetch user for username %s:' % target_username)
|
||||||
|
else:
|
||||||
|
if uid is None and target_username is None:
|
||||||
|
user_rep = kc.get_service_account_user_by_client_id(client_id=service_account_user_client_id, realm=realm)
|
||||||
|
if user_rep is not None:
|
||||||
|
uid = user_rep['id']
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Could not fetch service-account-user for client_id %s:' % target_username)
|
||||||
|
|
||||||
|
if cid is None and client_id is not None:
|
||||||
|
cid = kc.get_client_id(client_id=client_id, realm=realm)
|
||||||
|
if cid is None:
|
||||||
|
module.fail_json(msg='Could not fetch client %s:' % client_id)
|
||||||
|
if roles is None:
|
||||||
|
module.exit_json(msg="Nothing to do (no roles specified).")
|
||||||
|
else:
|
||||||
|
for role_index, role in enumerate(roles, start=0):
|
||||||
|
if role.get('name') is None and role.get('id') is None:
|
||||||
|
module.fail_json(msg='Either the `name` or `id` has to be specified on each role.')
|
||||||
|
# Fetch missing role_id
|
||||||
|
if role.get('id') is None:
|
||||||
|
if cid is None:
|
||||||
|
role_id = kc.get_realm_role(name=role.get('name'), realm=realm)['id']
|
||||||
|
else:
|
||||||
|
role_id = kc.get_client_role_id_by_name(cid=cid, name=role.get('name'), realm=realm)
|
||||||
|
if role_id is not None:
|
||||||
|
role['id'] = role_id
|
||||||
|
else:
|
||||||
|
module.fail_json(msg='Could not fetch role %s for client_id %s or realm %s' % (role.get('name'), client_id, realm))
|
||||||
|
# Fetch missing role_name
|
||||||
|
else:
|
||||||
|
if cid is None:
|
||||||
|
role['name'] = kc.get_realm_user_rolemapping_by_id(uid=uid, rid=role.get('id'), realm=realm)['name']
|
||||||
|
else:
|
||||||
|
role['name'] = kc.get_client_user_rolemapping_by_id(uid=uid, cid=cid, rid=role.get('id'), realm=realm)['name']
|
||||||
|
if role.get('name') is None:
|
||||||
|
module.fail_json(msg='Could not fetch role %s for client_id %s or realm %s' % (role.get('id'), client_id, realm))
|
||||||
|
|
||||||
|
# Get effective role mappings
|
||||||
|
if cid is None:
|
||||||
|
available_roles_before = kc.get_realm_user_available_rolemappings(uid=uid, realm=realm)
|
||||||
|
assigned_roles_before = kc.get_realm_user_composite_rolemappings(uid=uid, realm=realm)
|
||||||
|
else:
|
||||||
|
available_roles_before = kc.get_client_user_available_rolemappings(uid=uid, cid=cid, realm=realm)
|
||||||
|
assigned_roles_before = kc.get_client_user_composite_rolemappings(uid=uid, cid=cid, realm=realm)
|
||||||
|
|
||||||
|
result['existing'] = assigned_roles_before
|
||||||
|
result['proposed'] = roles
|
||||||
|
|
||||||
|
update_roles = []
|
||||||
|
for role_index, role in enumerate(roles, start=0):
|
||||||
|
# Fetch roles to assign if state present
|
||||||
|
if state == 'present':
|
||||||
|
for available_role in available_roles_before:
|
||||||
|
if role.get('name') == available_role.get('name'):
|
||||||
|
update_roles.append({
|
||||||
|
'id': role.get('id'),
|
||||||
|
'name': role.get('name'),
|
||||||
|
})
|
||||||
|
# Fetch roles to remove if state absent
|
||||||
|
else:
|
||||||
|
for assigned_role in assigned_roles_before:
|
||||||
|
if role.get('name') == assigned_role.get('name'):
|
||||||
|
update_roles.append({
|
||||||
|
'id': role.get('id'),
|
||||||
|
'name': role.get('name'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(update_roles):
|
||||||
|
if state == 'present':
|
||||||
|
# Assign roles
|
||||||
|
result['changed'] = True
|
||||||
|
if module._diff:
|
||||||
|
result['diff'] = dict(before=assigned_roles_before, after=update_roles)
|
||||||
|
if module.check_mode:
|
||||||
|
module.exit_json(**result)
|
||||||
|
kc.add_user_rolemapping(uid=uid, cid=cid, role_rep=update_roles, realm=realm)
|
||||||
|
result['msg'] = 'Roles %s assigned to userId %s.' % (update_roles, uid)
|
||||||
|
if cid is None:
|
||||||
|
assigned_roles_after = kc.get_realm_user_composite_rolemappings(uid=uid, realm=realm)
|
||||||
|
else:
|
||||||
|
assigned_roles_after = kc.get_client_user_composite_rolemappings(uid=uid, cid=cid, realm=realm)
|
||||||
|
result['end_state'] = assigned_roles_after
|
||||||
|
module.exit_json(**result)
|
||||||
|
else:
|
||||||
|
# Remove mapping of role
|
||||||
|
result['changed'] = True
|
||||||
|
if module._diff:
|
||||||
|
result['diff'] = dict(before=assigned_roles_before, after=update_roles)
|
||||||
|
if module.check_mode:
|
||||||
|
module.exit_json(**result)
|
||||||
|
kc.delete_user_rolemapping(uid=uid, cid=cid, role_rep=update_roles, realm=realm)
|
||||||
|
result['msg'] = 'Roles %s removed from userId %s.' % (update_roles, uid)
|
||||||
|
if cid is None:
|
||||||
|
assigned_roles_after = kc.get_realm_user_composite_rolemappings(uid=uid, realm=realm)
|
||||||
|
else:
|
||||||
|
assigned_roles_after = kc.get_client_user_composite_rolemappings(uid=uid, cid=cid, realm=realm)
|
||||||
|
result['end_state'] = assigned_roles_after
|
||||||
|
module.exit_json(**result)
|
||||||
|
# Do nothing
|
||||||
|
else:
|
||||||
|
result['changed'] = False
|
||||||
|
result['msg'] = 'Nothing to do, roles %s are correctly mapped to user for username %s.' % (roles, target_username)
|
||||||
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Copyright (c) 2022, Dušan Marković (@bratwurzt)
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
unsupported
|
|
@ -0,0 +1,143 @@
|
||||||
|
# Copyright (c) 2022, Dušan Marković (@bratwurzt)
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- 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 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_id }}"
|
||||||
|
service_accounts_enabled: True
|
||||||
|
state: present
|
||||||
|
register: client
|
||||||
|
|
||||||
|
- name: Create new realm role
|
||||||
|
community.general.keycloak_role:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
name: "{{ role }}"
|
||||||
|
description: "{{ description_1 }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Map a realm role to client service account
|
||||||
|
vars:
|
||||||
|
- roles: [ {'name': '{{ role }}'} ]
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
service_account_user_client_id: "{{ client_id }}"
|
||||||
|
roles: "{{ roles }}"
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert realm role is assigned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.end_state | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count > 0
|
||||||
|
|
||||||
|
- name: Unmap a realm role from client service account
|
||||||
|
vars:
|
||||||
|
- roles: [ {'name': '{{ role }}'} ]
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
service_account_user_client_id: "{{ client_id }}"
|
||||||
|
roles: "{{ roles }}"
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert realm role is unassigned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- (result.end_state | length) == (result.existing | length) - 1
|
||||||
|
- result.existing | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count > 0
|
||||||
|
- result.end_state | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count == 0
|
||||||
|
|
||||||
|
- name: Delete existing realm role
|
||||||
|
community.general.keycloak_role:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
name: "{{ role }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Create new client role
|
||||||
|
community.general.keycloak_role:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
client_id: "{{ client_id }}"
|
||||||
|
name: "{{ role }}"
|
||||||
|
description: "{{ description_1 }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Map a client role to client service account
|
||||||
|
vars:
|
||||||
|
- roles: [ {'name': '{{ role }}'} ]
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
client_id: "{{ client_id }}"
|
||||||
|
service_account_user_client_id: "{{ client_id }}"
|
||||||
|
roles: "{{ roles }}"
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert client role is assigned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.end_state | selectattr("clientRole", "eq", true) | selectattr("name", "eq", "{{role}}") | list | count > 0
|
||||||
|
|
||||||
|
- name: Unmap a client role from client service account
|
||||||
|
vars:
|
||||||
|
- roles: [ {'name': '{{ role }}'} ]
|
||||||
|
community.general.keycloak_user_rolemapping:
|
||||||
|
auth_keycloak_url: "{{ url }}"
|
||||||
|
auth_realm: "{{ admin_realm }}"
|
||||||
|
auth_username: "{{ admin_user }}"
|
||||||
|
auth_password: "{{ admin_password }}"
|
||||||
|
realm: "{{ realm }}"
|
||||||
|
client_id: "{{ client_id }}"
|
||||||
|
service_account_user_client_id: "{{ client_id }}"
|
||||||
|
roles: "{{ roles }}"
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert client role is unassigned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.end_state == []
|
||||||
|
- result.existing | selectattr("clientRole", "eq", true) | selectattr("name", "eq", "{{role}}") | list | count > 0
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
# Copyright (c) 2022, Dušan Marković (@bratwurzt)
|
||||||
|
# GNU General Public License v3.0+ (see COPYING 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_id: myclient
|
||||||
|
role: myrole
|
||||||
|
description_1: desc 1
|
||||||
|
description_2: desc 2
|
|
@ -21,9 +21,9 @@ from ansible.module_utils.six import StringIO
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def patch_keycloak_api(get_group_by_name=None, get_client_id=None, get_client_role_by_name=None,
|
def patch_keycloak_api(get_group_by_name=None, get_client_id=None, get_client_role_id_by_name=None,
|
||||||
get_client_rolemapping_by_id=None, get_client_available_rolemappings=None,
|
get_client_group_rolemapping_by_id=None, get_client_group_available_rolemappings=None,
|
||||||
get_client_composite_rolemappings=None, add_group_rolemapping=None,
|
get_client_group_composite_rolemappings=None, add_group_rolemapping=None,
|
||||||
delete_group_rolemapping=None):
|
delete_group_rolemapping=None):
|
||||||
"""Mock context manager for patching the methods in PwPolicyIPAClient that contact the IPA server
|
"""Mock context manager for patching the methods in PwPolicyIPAClient that contact the IPA server
|
||||||
|
|
||||||
|
@ -44,21 +44,21 @@ def patch_keycloak_api(get_group_by_name=None, get_client_id=None, get_client_ro
|
||||||
side_effect=get_group_by_name) as mock_get_group_by_name:
|
side_effect=get_group_by_name) as mock_get_group_by_name:
|
||||||
with patch.object(obj, 'get_client_id',
|
with patch.object(obj, 'get_client_id',
|
||||||
side_effect=get_client_id) as mock_get_client_id:
|
side_effect=get_client_id) as mock_get_client_id:
|
||||||
with patch.object(obj, 'get_client_role_by_name',
|
with patch.object(obj, 'get_client_role_id_by_name',
|
||||||
side_effect=get_client_role_by_name) as mock_get_client_role_by_name:
|
side_effect=get_client_role_id_by_name) as mock_get_client_role_id_by_name:
|
||||||
with patch.object(obj, 'get_client_rolemapping_by_id',
|
with patch.object(obj, 'get_client_group_rolemapping_by_id',
|
||||||
side_effect=get_client_rolemapping_by_id) as mock_get_client_rolemapping_by_id:
|
side_effect=get_client_group_rolemapping_by_id) as mock_get_client_group_rolemapping_by_id:
|
||||||
with patch.object(obj, 'get_client_available_rolemappings',
|
with patch.object(obj, 'get_client_group_available_rolemappings',
|
||||||
side_effect=get_client_available_rolemappings) as mock_get_client_available_rolemappings:
|
side_effect=get_client_group_available_rolemappings) as mock_get_client_group_available_rolemappings:
|
||||||
with patch.object(obj, 'get_client_composite_rolemappings',
|
with patch.object(obj, 'get_client_group_composite_rolemappings',
|
||||||
side_effect=get_client_composite_rolemappings) as mock_get_client_composite_rolemappings:
|
side_effect=get_client_group_composite_rolemappings) as mock_get_client_group_composite_rolemappings:
|
||||||
with patch.object(obj, 'add_group_rolemapping',
|
with patch.object(obj, 'add_group_rolemapping',
|
||||||
side_effect=add_group_rolemapping) as mock_add_group_rolemapping:
|
side_effect=add_group_rolemapping) as mock_add_group_rolemapping:
|
||||||
with patch.object(obj, 'delete_group_rolemapping',
|
with patch.object(obj, 'delete_group_rolemapping',
|
||||||
side_effect=delete_group_rolemapping) as mock_delete_group_rolemapping:
|
side_effect=delete_group_rolemapping) as mock_delete_group_rolemapping:
|
||||||
yield mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping, \
|
yield mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, \
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings, \
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, \
|
||||||
mock_delete_group_rolemapping
|
mock_get_client_group_composite_rolemappings, mock_delete_group_rolemapping
|
||||||
|
|
||||||
|
|
||||||
def get_response(object_with_future_response, method, get_id_call_count):
|
def get_response(object_with_future_response, method, get_id_call_count):
|
||||||
|
@ -144,8 +144,8 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"subGroups": "[]"
|
"subGroups": "[]"
|
||||||
}]
|
}]
|
||||||
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
||||||
return_value_get_client_role_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
||||||
return_value_get_client_available_rolemappings = [[
|
return_value_get_client_group_available_rolemappings = [[
|
||||||
{
|
{
|
||||||
"clientRole": "true",
|
"clientRole": "true",
|
||||||
"composite": "false",
|
"composite": "false",
|
||||||
|
@ -161,7 +161,7 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"name": "test_role1"
|
"name": "test_role1"
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
return_value_get_client_composite_rolemappings = [
|
return_value_get_client_group_composite_rolemappings = [
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -189,11 +189,11 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
|
|
||||||
with mock_good_connection():
|
with mock_good_connection():
|
||||||
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
||||||
get_client_role_by_name=return_value_get_client_role_by_name,
|
get_client_role_id_by_name=return_value_get_client_role_id_by_name,
|
||||||
get_client_available_rolemappings=return_value_get_client_available_rolemappings,
|
get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings,
|
||||||
get_client_composite_rolemappings=return_value_get_client_composite_rolemappings) \
|
get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \
|
||||||
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping,
|
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping,
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings,
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings,
|
||||||
mock_delete_group_rolemapping):
|
mock_delete_group_rolemapping):
|
||||||
with self.assertRaises(AnsibleExitJson) as exec_info:
|
with self.assertRaises(AnsibleExitJson) as exec_info:
|
||||||
self.module.main()
|
self.module.main()
|
||||||
|
@ -201,9 +201,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_id.call_count, 1)
|
self.assertEqual(mock_get_client_id.call_count, 1)
|
||||||
self.assertEqual(mock_add_group_rolemapping.call_count, 1)
|
self.assertEqual(mock_add_group_rolemapping.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_rolemapping_by_id.call_count, 0)
|
self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_available_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_composite_rolemappings.call_count, 2)
|
self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2)
|
||||||
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
||||||
|
|
||||||
# Verify that the module's changed status matches what is expected
|
# Verify that the module's changed status matches what is expected
|
||||||
|
@ -246,9 +246,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"subGroups": "[]"
|
"subGroups": "[]"
|
||||||
}]
|
}]
|
||||||
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
||||||
return_value_get_client_role_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
||||||
return_value_get_client_available_rolemappings = [[]]
|
return_value_get_client_group_available_rolemappings = [[]]
|
||||||
return_value_get_client_composite_rolemappings = [[
|
return_value_get_client_group_composite_rolemappings = [[
|
||||||
{
|
{
|
||||||
"clientRole": "true",
|
"clientRole": "true",
|
||||||
"composite": "false",
|
"composite": "false",
|
||||||
|
@ -273,11 +273,11 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
|
|
||||||
with mock_good_connection():
|
with mock_good_connection():
|
||||||
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
||||||
get_client_role_by_name=return_value_get_client_role_by_name,
|
get_client_role_id_by_name=return_value_get_client_role_id_by_name,
|
||||||
get_client_available_rolemappings=return_value_get_client_available_rolemappings,
|
get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings,
|
||||||
get_client_composite_rolemappings=return_value_get_client_composite_rolemappings) \
|
get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \
|
||||||
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping,
|
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping,
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings,
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings,
|
||||||
mock_delete_group_rolemapping):
|
mock_delete_group_rolemapping):
|
||||||
with self.assertRaises(AnsibleExitJson) as exec_info:
|
with self.assertRaises(AnsibleExitJson) as exec_info:
|
||||||
self.module.main()
|
self.module.main()
|
||||||
|
@ -285,9 +285,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_id.call_count, 1)
|
self.assertEqual(mock_get_client_id.call_count, 1)
|
||||||
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_rolemapping_by_id.call_count, 0)
|
self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_available_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_composite_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
||||||
|
|
||||||
# Verify that the module's changed status matches what is expected
|
# Verify that the module's changed status matches what is expected
|
||||||
|
@ -330,8 +330,8 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"subGroups": "[]"
|
"subGroups": "[]"
|
||||||
}]
|
}]
|
||||||
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
||||||
return_value_get_client_role_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
||||||
return_value_get_client_available_rolemappings = [[
|
return_value_get_client_group_available_rolemappings = [[
|
||||||
{
|
{
|
||||||
"clientRole": "true",
|
"clientRole": "true",
|
||||||
"composite": "false",
|
"composite": "false",
|
||||||
|
@ -347,7 +347,7 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"name": "test_role1"
|
"name": "test_role1"
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
return_value_get_client_composite_rolemappings = [
|
return_value_get_client_group_composite_rolemappings = [
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -375,11 +375,11 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
|
|
||||||
with mock_good_connection():
|
with mock_good_connection():
|
||||||
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
||||||
get_client_role_by_name=return_value_get_client_role_by_name,
|
get_client_role_id_by_name=return_value_get_client_role_id_by_name,
|
||||||
get_client_available_rolemappings=return_value_get_client_available_rolemappings,
|
get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings,
|
||||||
get_client_composite_rolemappings=return_value_get_client_composite_rolemappings) \
|
get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \
|
||||||
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping,
|
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping,
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings,
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings,
|
||||||
mock_delete_group_rolemapping):
|
mock_delete_group_rolemapping):
|
||||||
with self.assertRaises(AnsibleExitJson) as exec_info:
|
with self.assertRaises(AnsibleExitJson) as exec_info:
|
||||||
self.module.main()
|
self.module.main()
|
||||||
|
@ -387,9 +387,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
self.assertEqual(mock_get_group_by_name.call_count, 0)
|
self.assertEqual(mock_get_group_by_name.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_id.call_count, 0)
|
self.assertEqual(mock_get_client_id.call_count, 0)
|
||||||
self.assertEqual(mock_add_group_rolemapping.call_count, 1)
|
self.assertEqual(mock_add_group_rolemapping.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_rolemapping_by_id.call_count, 0)
|
self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_available_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_composite_rolemappings.call_count, 2)
|
self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2)
|
||||||
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
||||||
|
|
||||||
# Verify that the module's changed status matches what is expected
|
# Verify that the module's changed status matches what is expected
|
||||||
|
@ -432,9 +432,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"subGroups": "[]"
|
"subGroups": "[]"
|
||||||
}]
|
}]
|
||||||
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
||||||
return_value_get_client_role_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
||||||
return_value_get_client_available_rolemappings = [[]]
|
return_value_get_client_group_available_rolemappings = [[]]
|
||||||
return_value_get_client_composite_rolemappings = [
|
return_value_get_client_group_composite_rolemappings = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"clientRole": "true",
|
"clientRole": "true",
|
||||||
|
@ -462,11 +462,11 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
|
|
||||||
with mock_good_connection():
|
with mock_good_connection():
|
||||||
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
||||||
get_client_role_by_name=return_value_get_client_role_by_name,
|
get_client_role_id_by_name=return_value_get_client_role_id_by_name,
|
||||||
get_client_available_rolemappings=return_value_get_client_available_rolemappings,
|
get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings,
|
||||||
get_client_composite_rolemappings=return_value_get_client_composite_rolemappings) \
|
get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \
|
||||||
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping,
|
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping,
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings,
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings,
|
||||||
mock_delete_group_rolemapping):
|
mock_delete_group_rolemapping):
|
||||||
with self.assertRaises(AnsibleExitJson) as exec_info:
|
with self.assertRaises(AnsibleExitJson) as exec_info:
|
||||||
self.module.main()
|
self.module.main()
|
||||||
|
@ -474,9 +474,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_id.call_count, 1)
|
self.assertEqual(mock_get_client_id.call_count, 1)
|
||||||
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_rolemapping_by_id.call_count, 0)
|
self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_available_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_composite_rolemappings.call_count, 2)
|
self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2)
|
||||||
self.assertEqual(mock_delete_group_rolemapping.call_count, 1)
|
self.assertEqual(mock_delete_group_rolemapping.call_count, 1)
|
||||||
|
|
||||||
# Verify that the module's changed status matches what is expected
|
# Verify that the module's changed status matches what is expected
|
||||||
|
@ -519,8 +519,8 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
"subGroups": "[]"
|
"subGroups": "[]"
|
||||||
}]
|
}]
|
||||||
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727"
|
||||||
return_value_get_client_role_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe"
|
||||||
return_value_get_client_available_rolemappings = [
|
return_value_get_client_group_available_rolemappings = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"clientRole": "true",
|
"clientRole": "true",
|
||||||
|
@ -538,7 +538,7 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
return_value_get_client_composite_rolemappings = [[]]
|
return_value_get_client_group_composite_rolemappings = [[]]
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
|
@ -548,11 +548,11 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
|
|
||||||
with mock_good_connection():
|
with mock_good_connection():
|
||||||
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id,
|
||||||
get_client_role_by_name=return_value_get_client_role_by_name,
|
get_client_role_id_by_name=return_value_get_client_role_id_by_name,
|
||||||
get_client_available_rolemappings=return_value_get_client_available_rolemappings,
|
get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings,
|
||||||
get_client_composite_rolemappings=return_value_get_client_composite_rolemappings) \
|
get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \
|
||||||
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_by_name, mock_add_group_rolemapping,
|
as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping,
|
||||||
mock_get_client_rolemapping_by_id, mock_get_client_available_rolemappings, mock_get_client_composite_rolemappings,
|
mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings,
|
||||||
mock_delete_group_rolemapping):
|
mock_delete_group_rolemapping):
|
||||||
with self.assertRaises(AnsibleExitJson) as exec_info:
|
with self.assertRaises(AnsibleExitJson) as exec_info:
|
||||||
self.module.main()
|
self.module.main()
|
||||||
|
@ -560,9 +560,9 @@ class TestKeycloakRealm(ModuleTestCase):
|
||||||
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
self.assertEqual(mock_get_group_by_name.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_id.call_count, 1)
|
self.assertEqual(mock_get_client_id.call_count, 1)
|
||||||
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_add_group_rolemapping.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_rolemapping_by_id.call_count, 0)
|
self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0)
|
||||||
self.assertEqual(mock_get_client_available_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_get_client_composite_rolemappings.call_count, 1)
|
self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 1)
|
||||||
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
self.assertEqual(mock_delete_group_rolemapping.call_count, 0)
|
||||||
|
|
||||||
# Verify that the module's changed status matches what is expected
|
# Verify that the module's changed status matches what is expected
|
||||||
|
|
Loading…
Reference in a new issue