Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions plugins/module_utils/identity/keycloak/keycloak.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
URL_CLIENT_ROLE_COMPOSITES = "{url}/admin/realms/{realm}/clients/{id}/roles/{name}/composites"

URL_CLIENT_ROLE_SCOPE_CLIENTS = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/clients/{scopeid}"

#SUGGESTED FIX
# URL_CLIENT_ROLE_SCOPE_CLIENTS = "{url}/admin/realms/{realm}/client-scopes/{scopeid}/scope-mappings/clients/{id}"
URL_CLIENT_ROLE_SCOPE_REALM = "{url}/admin/realms/{realm}/clients/{id}/scope-mappings/realm"

URL_REALM_ROLES = "{url}/admin/realms/{realm}/roles"
Expand Down
20 changes: 14 additions & 6 deletions plugins/modules/keycloak_client_rolescope.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,27 +193,35 @@ def main():

objRealm = kc.get_realm_by_id(realm)
if not objRealm:
module.fail_json(msg="Failed to retrive realm '{realm}'".format(realm=realm))
module.fail_json(msg="Failed to retrieve realm '{realm}'".format(realm=realm))

objClient = kc.get_client_by_clientid(clientid, realm)
if not objClient:
module.fail_json(msg="Failed to retrive client '{realm}.{clientid}'".format(realm=realm, clientid=clientid))
module.fail_json(msg="Failed to retrieve client '{realm}.{clientid}'".format(realm=realm, clientid=clientid))
if objClient["fullScopeAllowed"] and state == "present":
module.fail_json(msg="FullScopeAllowed is active for Client '{realm}.{clientid}'".format(realm=realm, clientid=clientid))

if client_scope_id:
objClientScope = kc.get_client_by_clientid(client_scope_id, realm)

#SUGGESTED FIX
# objClientScope = kc.get_clientscope_by_clientscopeid(client_scope_id, realm)
if not objClientScope:
module.fail_json(msg="Failed to retrive client '{realm}.{client_scope_id}'".format(realm=realm, client_scope_id=client_scope_id))
module.fail_json(msg="Failed to retrieve client '{realm}.{client_scope_id}'".format(realm=realm, client_scope_id=client_scope_id))
before_role_mapping = kc.get_client_role_scope_from_client(objClient["id"], objClientScope["id"], realm)
else:
before_role_mapping = kc.get_client_role_scope_from_realm(objClient["id"], realm)

if client_scope_id:
# retrive all role from client_scope
client_scope_roles_by_name = kc.get_client_roles_by_id(objClientScope["id"], realm)

#SUGGESTED FIX
# if objClient:
# # retrieve all role from client
# client_scope_roles_by_name = kc.get_client_roles_by_id(objClient["id"], realm)
else:
# retrive all role from realm
# retrieve all role from realm
client_scope_roles_by_name = kc.get_realm_roles(realm)

# convert to indexed Dict by name
Expand All @@ -226,10 +234,10 @@ def main():
for role_name in role_names:
if role_name not in client_scope_roles_by_name:
if client_scope_id:
module.fail_json(msg="Failed to retrive role '{realm}.{client_scope_id}.{role_name}'"
module.fail_json(msg="Failed to retrieve role '{realm}.{client_scope_id}.{role_name}'"
.format(realm=realm, client_scope_id=client_scope_id, role_name=role_name))
else:
module.fail_json(msg="Failed to retrive role '{realm}.{role_name}'".format(realm=realm, role_name=role_name))
module.fail_json(msg="Failed to retrieve role '{realm}.{role_name}'".format(realm=realm, role_name=role_name))
if role_name not in role_mapping_by_name:
role_mapping_to_manipulate.append(client_scope_roles_by_name[role_name])
role_mapping_by_name[role_name] = client_scope_roles_by_name[role_name]
Expand Down
76 changes: 73 additions & 3 deletions tests/integration/targets/keycloak_client_rolescope/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,77 @@
- "{{ realm_role_admin }}"
- "{{ realm_role_user }}"

#############################
### Additional Steps which lead to failure

- name: Client private 2
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_private_2"
state: present
redirect_uris:
- "https://my-backend-api.c.org/"
fullScopeAllowed: false
attributes: "{{client_attributes1}}"
public_client: false

- name: Create a Keycloak client role for Client private 2
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_private_2"
with_items:
- "client_role_admin_2"
- "client_role_user_2"

- name: Create client scope
community.general.keycloak_clientscope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"

name: client_scope_name
protocol: openid-connect
attributes:
"include.in.token.scope": "true"
"display.on.consent.screen": "false"
state: present
register: new_scope

# This step fails - expectation was for role to be assigned to client scope - when making suggested changes this steps works
# when client_scope_id is given instead the {{ client_name_private }} (as for the task 'Map roles to public client') this step does not fail, however no role is assigned to this scope
# With the fix I am providing, when client_scope_id is provided with the scope_id (as opposed to a client name), the role is indeed assigned
# to the scope but the 'Map roles to public client' task fails probably for the reverse reasons
# Unless mistaken, somehow the underlying code seems to be confusing the notion of a client_scope_id with that of a client_id

- name: STEP which fails - Assign admin role to client scope
community.general.keycloak_client_rolescope:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
auth_username: "{{ admin_user }}"
auth_password: "{{ admin_password }}"
realm: "{{ realm }}"

client_scope_id: "{{ new_scope.end_state.id}}"
# client_scope_id: "{{ client_name_private }}"
client_id: "client_name_private_2"

role_names:
- "client_role_admin_2"
state: present

#############################

- name: Client private
community.general.keycloak_client:
auth_keycloak_url: "{{ url }}"
Expand All @@ -55,7 +126,7 @@
redirect_uris:
- "https://my-backend-api.c.org/"
fullScopeAllowed: true
attributes: '{{client_attributes1}}'
attributes: "{{client_attributes1}}"
public_client: false

- name: Create a Keycloak client role
Expand All @@ -81,11 +152,10 @@
client_id: "{{ client_name_public }}"
redirect_uris:
- "https://my-onepage-app-frontend.c.org/"
attributes: '{{client_attributes1}}'
attributes: "{{client_attributes1}}"
full_scope_allowed: false
public_client: true


- name: Map roles to public client
community.general.keycloak_client_rolescope:
auth_keycloak_url: "{{ url }}"
Expand Down
Loading