8000 New Module: Keycloak Client ScopeMapping by fynncfchen · Pull Request #4017 · ansible-collections/community.general · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

New Module: Keycloak Client ScopeMapping #4017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/BOTMETA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ files:
maintainers: Gaetan2907
$modules/identity/keycloak/keycloak_client_rolemapping.py:
maintainers: Gaetan2907
$modules/identity/keycloak/keycloak_client_scopemapping.py:
maintainers: fynncfchen
$modules/identity/keycloak/keycloak_group.py:
maintainers: adamgoossens
$modules/identity/keycloak/keycloak_identity_provider.py:
Expand Down Expand Up @@ -1193,7 +1195,7 @@ files:
maintainers: lekum
$tests/a_module.py:
maintainers: felixfontein
#########################
#########################
tests/:
labels: tests
tests/unit/:
Expand Down
119 changes: 110 additions & 9 deletions plugins/module_utils/identity/keycloak/keycloak.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
URL_CLIENT_ROLE = "{url}/admin/realms/{realm}/clients/{id}/roles/{name}"
URL_CLIENT_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/clients/{id}/roles/{name}/composites"

URL_CLIENT_SCOPEMAPPINGS_CLIENT = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/clients/{client}"
URL_CLIENT_SCOPEMAPPINGS_CLIENT_AVAILABLE = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/clients/{client}/available"
URL_CLIENT_SCOPEMAPPINGS_CLIENT_COMPOSITE = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/clients/{client}/composite"

URL_REALM_ROLES = "{url}/admin/realms/{realm}/roles"
URL_REALM_ROLE = "{url}/admin/realms/{realm}/roles/{name}"
URL_REALM_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/roles/{name}/composites"
Expand Down Expand Up @@ -224,6 +228,7 @@ class KeycloakAPI(object):
""" Keycloak API access; Keycloak uses OAuth 2.0 to protect its API, an access token for which
is obtained through OpenID connect
"""

def __init__(self, module, connection_header):
self.module = module
self.baseurl = self.module.params.get('auth_keycloak_url')
Expand Down Expand Up @@ -421,9 +426,9 @@ def delete_client(self, id, realm="master"):
def get_client_roles_by_id(self, cid, realm="master"):
""" Fetch the roles of the a client on the Keycloak server.

: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").
:param cid: ID of the client from which to obtain the roles.
:param realm: Realm from which to obtain roles.
:return: The roles of specified client of the realm (default "master").
"""
client_roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
try:
Expand All @@ -433,17 +438,30 @@ def get_client_roles_by_id(self, cid, realm="master"):
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
% (cid, realm, str(e)))

def get_client_role_by_name(self, gid, cid, name, realm="master"):
def get_client_role_name_by_id(self, cid, rid, realm="master"):
""" Get the role name of a client.

:param cid: ID of the client from which to obtain the roles.
:param rid: ID of the role.
:param realm: Realm from which to obtain the roles.
:return: The name of the role, None if not found.
"""
roles = self.get_client_roles_by_id(cid, realm=realm)
for role in roles:
if rid == role['id']:
return role['name']
return None

def get_client_role_id_by_name(self, cid, name, realm="master"):
""" 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 roles.
:param name: Name of the role.
:param realm: Realm from which to obtain the rolemappings.
:param realm: Realm from which to obtain the roles.
:return: The ID of the role, None if not found.
"""
rolemappings = self.get_client_roles_by_id(cid, realm=realm)
for role in rolemappings:
roles = self.get_client_roles_by_id(cid, realm=realm)
for role in roles:
if name == role['name']:
return role['id']
return None
Expand Down Expand Up @@ -501,6 +519,89 @@ def get_client_composite_rolemappings(self, gid, cid, realm="master"):
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
% (cid, gid, realm, str(e)))

def add_client_scopemappings_client(self, cid, client, rolereps, realm="master"):
""" Add client-level roles to the client's scope

:param cid Client ID to grant
:param client Target client ID of the roles
:param rolereps Role representations of the target client
"""
client_scopemappings_client_url = URL_CLIENT_SCOPEMAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=cid, client=client)
try:
open_url(client_scopemappings_client_url, method="POST", headers=self.restheaders,
data=json.dumps(rolereps), validate_certs=self.validate_certs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and others) need to be adjusted similar to #4178.

except Exception as e:
self.module.fail_json(msg="Could not assign roles from client %s to client %s, realm %s: %s"
% (client, cid, realm, str(e)))

def remove_client_scopemappings_client(self, cid, client, rolereps, realm="master"):
""" Remove client-level roles from the client's scope.

:param cid Client ID to grant
:param client Target client ID of the roles
:param rolereps Role representations of the target client
"""
client_scopemappings_client_url = URL_CLIENT_SCOPEMAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=cid, client=client)
try:
open_url(client_scopemappings_client_url, method="DELETE", headers=self.restheaders,
data=json.dumps(rolereps), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg="Could not remove assigned roles from client %s to client %s, realm %s: %s"
% (client, cid, realm, str(e)))

def get_client_scopemappings_client(self, cid, client, realm="master"):
""" Fetch the roles associated with a client's scope, returns roles for the client.

:param cid: ID of the client from which to obtain the scope mapping.
:param client: ID of the client associated to the role to grant.
:param realm: Realm from which to obtain the scope mapping.
:return: The scope mapping of specified client and client of the realm (default "master").
"""
client_scopemappings_client_url = URL_CLIENT_SCOPEMAPPINGS_CLIENT.format(
url=self.baseurl, realm=realm, id=cid, client=client)
try:
return json.loads(to_native(open_url(client_scopemappings_client_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg="Could not fetch scope mappings of the client %s for client %s, realm %s: %s"
% (cid, client, realm, str(e)))

def get_client_scopemappings_client_available(self, cid, client, realm="master"):
""" Fetch available client-level roles, returns the roles for the client that can be associated with the client's scope

:param cid: ID of the client from which to obtain the scope mapping.
:param client: ID of the client associated to the role to grant.
:param realm: Realm from which to obtain the scope mapping.
:return: The scope mapping of specified client and client of the realm (default "master").
"""
client_scopemappings_client_available_url = URL_CLIENT_SCOPEMAPPINGS_CLIENT_AVAILABLE.format(
url=self.baseurl, realm=realm, id=cid, client=client)
try:
return json.loads(to_native(open_url(client_scopemappings_client_available_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg="Could not fetch scope mappings of the client %s for client %s, realm %s: %s"
% (cid, client, realm, str(e)))

def get_client_scopemappings_client_composite(self, cid, client, realm="master"):
""" Fetch effective client roles, returns the roles for the client that are associated with the client's scope.

:param cid: ID of the client from which to obtain the scope mapping.
:param client: ID of the client associated to the role to grant.
:param realm: Realm from which to obtain the scope mapping.
:return: The scope mapping of specified client and client of the realm (default "master").
"""
client_scopemappings_client_composite_url = URL_CLIENT_SCOPEMAPPINGS_CLIENT_COMPOSITE.format(
url=self.baseurl, realm=realm, id=cid, client=client)
try:
return json.loads(to_native(open_url(client_scopemappings_client_composite_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg="Could not fetch scope mappings of the client %s for client %s, realm %s: %s"
% (cid, client, realm, str(e)))

def add_group_rolemapping(self, gid, cid, role_rep, realm="master"):
""" Fetch the composite role of a client in a specified goup 6D47 on the Keycloak server.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def main():
module.fail_json(msg='Either the `name` or `id` has to be specified on each role.')
# Fetch missing role_id
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:
role['id'] = role_id
else:
Expand Down
Loading
0