mirror of
https://github.com/ansible-middleware/keycloak.git
synced 2026-06-13 12:05:54 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f172e019b4 | ||
|
|
e764cfd6f1 | ||
|
|
01b00dfb2e | ||
|
|
1908794569 | ||
|
|
c8bcff39ef | ||
|
|
0fad56294b | ||
|
|
b2b52ddfb5 | ||
|
|
7369a5724c | ||
|
|
942f0ae896 | ||
|
|
92d6dddd49 | ||
|
|
a1bb84ea5b | ||
|
|
1ec94b961f | ||
|
|
ba3f716e5c | ||
|
|
808d137e4c | ||
|
|
1794d4ff9b | ||
|
|
e898a2511a | ||
|
|
dd2cfaa87d | ||
|
|
b114c7b252 | ||
|
|
9920dc93c9 | ||
|
|
5cb555d6c2 |
@@ -1,4 +1,6 @@
|
|||||||
# .ansible-lint
|
# .ansible-lint
|
||||||
|
profile: production
|
||||||
|
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
- .cache/
|
- .cache/
|
||||||
- .github/
|
- .github/
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ middleware\_automation.keycloak Release Notes
|
|||||||
|
|
||||||
This changelog describes changes after version 0.2.6.
|
This changelog describes changes after version 0.2.6.
|
||||||
|
|
||||||
|
v3.0.9
|
||||||
|
======
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- AMW-551 Providing correct rhbk version `#344 <https://github.com/ansible-middleware/keycloak/pull/344>`_
|
||||||
|
|
||||||
|
v3.0.8
|
||||||
|
======
|
||||||
|
|
||||||
v3.0.7
|
v3.0.7
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -49,9 +49,9 @@ A requirement file is provided to install:
|
|||||||
<!--start roles_paths -->
|
<!--start roles_paths -->
|
||||||
### Included roles
|
### Included roles
|
||||||
|
|
||||||
* `keycloak_quarkus`: role for installing keycloak (>= 19.0.0, quarkus based).
|
* [`keycloak_quarkus`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_quarkus/README.md): role for installing keycloak (>= 19.0.0, quarkus based).
|
||||||
* `keycloak_realm`: role for configuring a realm, user federation(s), clients and users, in an installed service.
|
* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service.
|
||||||
* `keycloak`: role for installing legacy keycloak (<= 19.0, wildfly based).
|
* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing legacy keycloak (<= 19.0, wildfly based).
|
||||||
|
|
||||||
<!--end roles_paths -->
|
<!--end roles_paths -->
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ All Keycloak administration modules from `community.general` are provided in thi
|
|||||||
* `keycloak_client_rolemapping`: manage client role mappings for users and groups.
|
* `keycloak_client_rolemapping`: manage client role mappings for users and groups.
|
||||||
* `keycloak_client_rolescope`: manage client role scope mappings.
|
* `keycloak_client_rolescope`: manage client role scope mappings.
|
||||||
* `keycloak_client_scope`: manage client scopes and protocol mappers (replaces `community.general.keycloak_clientscope`).
|
* `keycloak_client_scope`: manage client scopes and protocol mappers (replaces `community.general.keycloak_clientscope`).
|
||||||
* `keycloak_clientscope_type`: manage default and optional client scope assignments.
|
* `keycloak_client_scope_type`: manage default and optional client scope assignments.
|
||||||
* `keycloak_clientsecret_info`: retrieve client secret information.
|
* `keycloak_clientsecret_info`: retrieve client secret information.
|
||||||
* `keycloak_clientsecret_regenerate`: regenerate a client secret.
|
* `keycloak_clientsecret_regenerate`: regenerate a client secret.
|
||||||
* `keycloak_clienttemplate`: manage legacy client templates.
|
* `keycloak_clienttemplate`: manage legacy client templates.
|
||||||
@@ -220,5 +220,5 @@ For details on changes between versions, please see the [CHANGELOG](https://gith
|
|||||||
|
|
||||||
Apache License v2.0 or later
|
Apache License v2.0 or later
|
||||||
<!--start license -->
|
<!--start license -->
|
||||||
See [LICENSE](LICENSE) to view the full text.
|
See [LICENSE](https://github.com/ansible-middleware/keycloak/blob/main/LICENSE) to view the full text.
|
||||||
<!--end license -->
|
<!--end license -->
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ python3-netaddr [platform:rpm platform:dpkg]
|
|||||||
python3-lxml [platform:rpm platform:dpkg]
|
python3-lxml [platform:rpm platform:dpkg]
|
||||||
python3-jmespath [platform:rpm platform:dpkg]
|
python3-jmespath [platform:rpm platform:dpkg]
|
||||||
python3-requests [platform:rpm platform:dpkg]
|
python3-requests [platform:rpm platform:dpkg]
|
||||||
|
podman [platform:rpm platform:dpkg]
|
||||||
|
|
||||||
|
|||||||
@@ -825,3 +825,14 @@ releases:
|
|||||||
- 341.yaml
|
- 341.yaml
|
||||||
- 343.yaml
|
- 343.yaml
|
||||||
release_date: '2026-06-01'
|
release_date: '2026-06-01'
|
||||||
|
3.0.8:
|
||||||
|
release_date: '2026-06-09'
|
||||||
|
3.0.9:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- 'AMW-551 Providing correct rhbk version `#344 <https://github.com/ansible-middleware/keycloak/pull/344>`_
|
||||||
|
|
||||||
|
'
|
||||||
|
fragments:
|
||||||
|
- 344.yaml
|
||||||
|
release_date: '2026-06-11'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
namespace: middleware_automation
|
namespace: middleware_automation
|
||||||
name: keycloak
|
name: keycloak
|
||||||
version: "3.0.7"
|
version: "3.0.9"
|
||||||
readme: README.md
|
readme: README.md
|
||||||
authors:
|
authors:
|
||||||
- Romain Pelisse <rpelisse@redhat.com>
|
- Romain Pelisse <rpelisse@redhat.com>
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ action_groups:
|
|||||||
- keycloak_client_rolemapping
|
- keycloak_client_rolemapping
|
||||||
- keycloak_client_rolescope
|
- keycloak_client_rolescope
|
||||||
- keycloak_client_scope
|
- keycloak_client_scope
|
||||||
- keycloak_clientscope_type
|
- keycloak_client_scope_type
|
||||||
- keycloak_clientscope_rolemappings
|
- keycloak_client_scope_rolemappings
|
||||||
- keycloak_clientsecret_info
|
- keycloak_clientsecret_info
|
||||||
- keycloak_clientsecret_regenerate
|
- keycloak_clientsecret_regenerate
|
||||||
- keycloak_clienttemplate
|
- keycloak_clienttemplate
|
||||||
@@ -44,3 +44,19 @@ plugin_routing:
|
|||||||
warning_text: >-
|
warning_text: >-
|
||||||
The module has been renamed to keycloak_client_scope for Keycloak 17+ (Quarkus).
|
The module has been renamed to keycloak_client_scope for Keycloak 17+ (Quarkus).
|
||||||
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope.
|
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope.
|
||||||
|
|
||||||
|
keycloak_clientscope_type:
|
||||||
|
redirect: middleware_automation.keycloak.keycloak_client_scope_type
|
||||||
|
deprecation:
|
||||||
|
removal_version: 5.0.0
|
||||||
|
warning_text: >-
|
||||||
|
The module has been renamed to keycloak_client_scope_type for Keycloak 17+ (Quarkus).
|
||||||
|
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_type.
|
||||||
|
|
||||||
|
keycloak_clientscope_rolemappings:
|
||||||
|
redirect: middleware_automation.keycloak.keycloak_client_scope_rolemappings
|
||||||
|
deprecation:
|
||||||
|
removal_version: 5.0.0
|
||||||
|
warning_text: >-
|
||||||
|
The module has been renamed to keycloak_client_scope_rolemappings for Keycloak 17+ (Quarkus).
|
||||||
|
Update playbooks to use middleware_automation.keycloak.keycloak_client_scope_rolemappings.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
driver:
|
driver:
|
||||||
name: podman
|
name: docker
|
||||||
platforms:
|
platforms:
|
||||||
- name: instance
|
- name: instance
|
||||||
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
||||||
|
|||||||
@@ -20,8 +20,50 @@
|
|||||||
|
|
||||||
- name: Download keycloak archive to controller directory
|
- name: Download keycloak archive to controller directory
|
||||||
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
|
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
|
||||||
url: https://github.com/keycloak/keycloak/releases/download/26.4.7/keycloak-26.4.7.zip
|
url: https://github.com/keycloak/keycloak/releases/download/26.6.2/keycloak-26.6.2.zip
|
||||||
dest: /tmp/keycloak
|
dest: /tmp/keycloak
|
||||||
mode: '0640'
|
mode: '0640'
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
run_once: true
|
run_once: true
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Attempt RHBK download using redhat.runtimes_common collection
|
||||||
|
when:
|
||||||
|
- rhn_username is defined
|
||||||
|
- rhn_username | length > 0
|
||||||
|
block:
|
||||||
|
- name: Retrieve RHBK product download using Unified Downloads API
|
||||||
|
middleware_automation.common.product_search:
|
||||||
|
client_id: "{{ rhn_username }}"
|
||||||
|
client_secret: "{{ rhn_password }}"
|
||||||
|
product_type: DISTRIBUTION
|
||||||
|
product_version: "{{ keycloak_quarkus_version | default('26.6.2') }}"
|
||||||
|
product_category: "RHBK"
|
||||||
|
register: rhn_products
|
||||||
|
no_log: "{{ omit_rhn_output | default(true) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Determine install zipfile from search results
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
rhn_matched_products: "{{ rhn_products.results | selectattr('file_name', 'match', '.*keycloak-' + (keycloak_quarkus_version | default('26.6.2')) + '.zip$') }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
when:
|
||||||
|
- rhn_products is defined
|
||||||
|
- rhn_products.results is defined
|
||||||
|
|
||||||
|
- name: Download Red Hat Build of Keycloak
|
||||||
|
middleware_automation.common.product_download:
|
||||||
|
client_id: "{{ rhn_username }}"
|
||||||
|
client_secret: "{{ rhn_password }}"
|
||||||
|
product_id: "{{ (rhn_matched_products | first).id }}"
|
||||||
|
dest: "/tmp/keycloak/keycloak-{{ keycloak_quarkus_version | default('26.6.2') }}.zip"
|
||||||
|
no_log: "{{ omit_rhn_output | default(true) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
when:
|
||||||
|
- rhn_matched_products is defined
|
||||||
|
- rhn_matched_products | length > 0
|
||||||
|
ignore_errors: true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
driver:
|
driver:
|
||||||
name: podman
|
name: docker
|
||||||
platforms:
|
platforms:
|
||||||
- name: instance
|
- name: instance
|
||||||
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
||||||
|
|||||||
@@ -37,8 +37,8 @@
|
|||||||
- keycloak_client_rolemapping
|
- keycloak_client_rolemapping
|
||||||
- keycloak_client_rolescope
|
- keycloak_client_rolescope
|
||||||
- keycloak_client_scope
|
- keycloak_client_scope
|
||||||
- keycloak_clientscope_type
|
- keycloak_client_scope_type
|
||||||
- keycloak_clientscope_rolemappings
|
- keycloak_client_scope_rolemappings
|
||||||
- keycloak_clientsecret_info
|
- keycloak_clientsecret_info
|
||||||
- keycloak_clientsecret_regenerate
|
- keycloak_clientsecret_regenerate
|
||||||
- keycloak_clienttemplate
|
- keycloak_clienttemplate
|
||||||
@@ -265,10 +265,10 @@
|
|||||||
- "'404' not in (clienttemplate_result.msg | default(''))"
|
- "'404' not in (clienttemplate_result.msg | default(''))"
|
||||||
- "'Not Found' not in (clienttemplate_result.msg | default(''))"
|
- "'Not Found' not in (clienttemplate_result.msg | default(''))"
|
||||||
|
|
||||||
- name: keycloak_clientscope_type — attach scope as optional on realm
|
- name: keycloak_client_scope_type — attach scope as optional on realm
|
||||||
middleware_automation.keycloak.keycloak_clientscope_type:
|
middleware_automation.keycloak.keycloak_client_scope_type:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
optional_clientscopes:
|
optional_client_scopes:
|
||||||
- "{{ scope }}"
|
- "{{ scope }}"
|
||||||
|
|
||||||
- name: keycloak_user_rolemapping — assign realm role to user
|
- name: keycloak_user_rolemapping — assign realm role to user
|
||||||
@@ -299,54 +299,54 @@
|
|||||||
- name: keycloak_client_rolescope — restrict realm role on client
|
- name: keycloak_client_rolescope — restrict realm role on client
|
||||||
middleware_automation.keycloak.keycloak_client_rolescope:
|
middleware_automation.keycloak.keycloak_client_rolescope:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
client_id: "{{ client }}"
|
target_client_id: "{{ client }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ role }}"
|
- "{{ role }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: keycloak_clientscope_rolemappings — map client roles to clientscope
|
- name: keycloak_client_scope_rolemappings — map client roles to client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
client_id: "{{ client }}"
|
client_id: "{{ client }}"
|
||||||
clientscope_id: "{{ scope }}"
|
client_scope_id: "{{ scope }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ client_role }}"
|
- "{{ client_role }}"
|
||||||
register: clientscope_rolemappings_result
|
register: client_scope_rolemappings_result
|
||||||
|
|
||||||
- name: Assert client scope role mappings were created
|
- name: Assert client scope role mappings were created
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- clientscope_rolemappings_result is changed
|
- client_scope_rolemappings_result is changed
|
||||||
- clientscope_rolemappings_result.end_state | length == 1
|
- client_scope_rolemappings_result.end_state | length == 1
|
||||||
|
|
||||||
- name: keycloak_clientscope_rolemappings — remap client role (idempotency)
|
- name: keycloak_client_scope_rolemappings — remap client role (idempotency)
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
client_id: "{{ client }}"
|
client_id: "{{ client }}"
|
||||||
clientscope_id: "{{ scope }}"
|
client_scope_id: "{{ scope }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ client_role }}"
|
- "{{ client_role }}"
|
||||||
register: clientscope_rolemappings_idempotent_result
|
register: client_scope_rolemappings_idempotent_result
|
||||||
|
|
||||||
- name: Assert client scope role mappings are idempotent
|
- name: Assert client scope role mappings are idempotent
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- clientscope_rolemappings_idempotent_result is not changed
|
- client_scope_rolemappings_idempotent_result is not changed
|
||||||
- clientscope_rolemappings_idempotent_result.end_state | length == 1
|
- client_scope_rolemappings_idempotent_result.end_state | length == 1
|
||||||
|
|
||||||
- name: keycloak_clientscope_rolemappings — map realm role to clientscope
|
- name: keycloak_client_scope_rolemappings — map realm role to client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
clientscope_id: "{{ scope }}"
|
client_scope_id: "{{ scope }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ role }}"
|
- "{{ role }}"
|
||||||
register: clientscope_realm_rolemappings_result
|
register: client_scope_realm_rolemappings_result
|
||||||
|
|
||||||
- name: Assert realm role was mapped to clientscope
|
- name: Assert realm role was mapped to client_scope
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- clientscope_realm_rolemappings_result is changed
|
- client_scope_realm_rolemappings_result is changed
|
||||||
- clientscope_realm_rolemappings_result.end_state | length == 1
|
- client_scope_realm_rolemappings_result.end_state | length == 1
|
||||||
|
|
||||||
- name: keycloak_user — set email_verified explicitly
|
- name: keycloak_user — set email_verified explicitly
|
||||||
middleware_automation.keycloak.keycloak_user:
|
middleware_automation.keycloak.keycloak_user:
|
||||||
@@ -517,19 +517,19 @@
|
|||||||
name: "{{ authz_scope }}"
|
name: "{{ authz_scope }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: keycloak_clientscope_rolemappings — remove realm role from clientscope
|
- name: keycloak_client_scope_rolemappings — remove realm role from client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
clientscope_id: "{{ scope }}"
|
client_scope_id: "{{ scope }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ role }}"
|
- "{{ role }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: keycloak_clientscope_rolemappings — remove client role from clientscope
|
- name: keycloak_client_scope_rolemappings — remove client role from client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
client_id: "{{ client }}"
|
client_id: "{{ client }}"
|
||||||
clientscope_id: "{{ scope }}"
|
client_scope_id: "{{ scope }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ client_role }}"
|
- "{{ client_role }}"
|
||||||
state: absent
|
state: absent
|
||||||
@@ -537,7 +537,7 @@
|
|||||||
- name: keycloak_client_rolescope — remove role scope mapping
|
- name: keycloak_client_rolescope — remove role scope mapping
|
||||||
middleware_automation.keycloak.keycloak_client_rolescope:
|
middleware_automation.keycloak.keycloak_client_rolescope:
|
||||||
realm: "{{ target_realm }}"
|
realm: "{{ target_realm }}"
|
||||||
client_id: "{{ client }}"
|
target_client_id: "{{ client }}"
|
||||||
role_names:
|
role_names:
|
||||||
- "{{ role }}"
|
- "{{ role }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
vars_files:
|
vars_files:
|
||||||
- ../group_vars/all/vars.yml
|
- ../group_vars/all/vars.yml
|
||||||
vars:
|
vars:
|
||||||
|
rhn_username: "{{ lookup('env', 'rhn_username') | default('4278e994-7f90-46eb-b99c-90f2815b845f', true) }}"
|
||||||
|
rhn_password: "{{ lookup('env', 'rhn_password') | default('AHOLJo08ursGdWVm0F66iDR5Owk0CwpL', true) }}"
|
||||||
|
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
|
||||||
keycloak_admin_password: "remembertochangeme"
|
keycloak_admin_password: "remembertochangeme"
|
||||||
|
keycloak_quarkus_hostname: "http://instance:8080"
|
||||||
keycloak_config_override_template: custom.xml.j2
|
keycloak_config_override_template: custom.xml.j2
|
||||||
keycloak_http_port: 8081
|
keycloak_http_port: 8081
|
||||||
keycloak_management_http_port: 19990
|
keycloak_management_http_port: 19990
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
keycloak_quarkus_systemd_wait_for_delay: 2
|
keycloak_quarkus_systemd_wait_for_delay: 2
|
||||||
keycloak_quarkus_systemd_wait_for_log: true
|
keycloak_quarkus_systemd_wait_for_log: true
|
||||||
keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert
|
keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert
|
||||||
keycloak_quarkus_version: 26.4.7
|
keycloak_quarkus_version: 26.6.2
|
||||||
keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx1024m"
|
keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx1024m"
|
||||||
keycloak_quarkus_additional_env_vars:
|
keycloak_quarkus_additional_env_vars:
|
||||||
- key: KC_FEATURES_DISABLED
|
- key: KC_FEATURES_DISABLED
|
||||||
@@ -39,16 +39,16 @@
|
|||||||
- key: default-connection-pool-size
|
- key: default-connection-pool-size
|
||||||
value: 10
|
value: 10
|
||||||
- id: spid-saml
|
- id: spid-saml
|
||||||
url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar
|
url: https://github.com/italia/spid-keycloak-provider/releases/download/26.5.6/spid-provider.jar
|
||||||
- id: spid-saml-w-checksum
|
- id: spid-saml-w-checksum
|
||||||
url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar
|
url: https://github.com/italia/spid-keycloak-provider/releases/download/26.5.6/spid-provider.jar
|
||||||
checksum: sha256:fbb50e73739d7a6d35b5bff611b1c01668b29adf6f6259624b95e466a305f377
|
checksum: sha256:2ddafc389a5f017d8665bfdfa2f72b3784fc74b9f3a482e796fa89a5ba5cc95b
|
||||||
- id: keycloak-kerberos-federation
|
- id: keycloak-kerberos-federation
|
||||||
maven:
|
maven:
|
||||||
repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4
|
repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4
|
||||||
group_id: org.keycloak
|
group_id: org.keycloak
|
||||||
artifact_id: keycloak-kerberos-federation
|
artifact_id: keycloak-kerberos-federation
|
||||||
version: 26.4.7 # optional
|
version: 26.6.3 # optional
|
||||||
# username: myUser # optional
|
# username: myUser # optional
|
||||||
# password: myPAT # optional
|
# password: myPAT # optional
|
||||||
# - id: my-static-theme
|
# - id: my-static-theme
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
driver:
|
driver:
|
||||||
name: podman
|
name: docker
|
||||||
platforms:
|
platforms:
|
||||||
- name: instance
|
- name: instance
|
||||||
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
keycloak_quarkus_additional_env_vars:
|
keycloak_quarkus_additional_env_vars:
|
||||||
- key: KC_FEATURES_DISABLED
|
- key: KC_FEATURES_DISABLED
|
||||||
value: ciba,device-flow,impersonation,kerberos,docker
|
value: ciba,device-flow,impersonation,kerberos,docker
|
||||||
keycloak_quarkus_version: 26.0.7
|
keycloak_quarkus_version: 26.6.2
|
||||||
roles:
|
roles:
|
||||||
- role: keycloak_quarkus
|
- role: keycloak_quarkus
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ dependency:
|
|||||||
options:
|
options:
|
||||||
requirements-file: molecule/requirements.yml
|
requirements-file: molecule/requirements.yml
|
||||||
driver:
|
driver:
|
||||||
name: podman
|
name: docker
|
||||||
platforms:
|
platforms:
|
||||||
- name: instance
|
- name: instance
|
||||||
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
image: registry.access.redhat.com/ubi9/ubi-init:latest
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
- vars.yml
|
- vars.yml
|
||||||
vars:
|
vars:
|
||||||
sudo_pkg_name: sudo
|
sudo_pkg_name: sudo
|
||||||
keycloak_quarkus_version: 26.0.4
|
keycloak_quarkus_version: 26.6.1
|
||||||
keycloak_quarkus_additional_env_vars:
|
keycloak_quarkus_additional_env_vars:
|
||||||
- key: KC_FEATURES_DISABLED
|
- key: KC_FEATURES_DISABLED
|
||||||
value: impersonation,kerberos
|
value: impersonation,kerberos
|
||||||
|
|||||||
@@ -57,23 +57,23 @@ URL_GROUPS = "{url}/admin/realms/{realm}/groups"
|
|||||||
URL_GROUP = "{url}/admin/realms/{realm}/groups/{groupid}"
|
URL_GROUP = "{url}/admin/realms/{realm}/groups/{groupid}"
|
||||||
URL_GROUP_CHILDREN = "{url}/admin/realms/{realm}/groups/{groupid}/children"
|
URL_GROUP_CHILDREN = "{url}/admin/realms/{realm}/groups/{groupid}/children"
|
||||||
|
|
||||||
URL_CLIENTSCOPES = "{url}/admin/realms/{realm}/client-scopes"
|
URL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/client-scopes"
|
||||||
URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}"
|
URL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}"
|
||||||
URL_CLIENTSCOPE_SCOPE_MAPPINGS = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings"
|
URL_CLIENT_SCOPE_SCOPE_MAPPINGS = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings"
|
||||||
URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/realm"
|
URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/realm"
|
||||||
URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/clients/{client}"
|
URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT = "{url}/admin/realms/{realm}/client-scopes/{id}/scope-mappings/clients/{client}"
|
||||||
URL_CLIENTSCOPE_PROTOCOLMAPPERS = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models"
|
URL_CLIENT_SCOPE_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_CLIENT_SCOPE_PROTOCOLMAPPER = "{url}/admin/realms/{realm}/client-scopes/{id}/protocol-mappers/models/{mapper_id}"
|
||||||
|
|
||||||
URL_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes"
|
URL_DEFAULT_CLIENT_SCOPES = "{url}/admin/realms/{realm}/default-default-client-scopes"
|
||||||
URL_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}"
|
URL_DEFAULT_CLIENT_SCOPE = "{url}/admin/realms/{realm}/default-default-client-scopes/{id}"
|
||||||
URL_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes"
|
URL_OPTIONAL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/default-optional-client-scopes"
|
||||||
URL_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}"
|
URL_OPTIONAL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/default-optional-client-scopes/{id}"
|
||||||
|
|
||||||
URL_CLIENT_DEFAULT_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes"
|
URL_CLIENT_DEFAULT_CLIENT_SCOPES = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes"
|
||||||
URL_CLIENT_DEFAULT_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}"
|
URL_CLIENT_DEFAULT_CLIENT_SCOPE = "{url}/admin/realms/{realm}/clients/{cid}/default-client-scopes/{id}"
|
||||||
URL_CLIENT_OPTIONAL_CLIENTSCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes"
|
URL_CLIENT_OPTIONAL_CLIENT_SCOPES = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes"
|
||||||
URL_CLIENT_OPTIONAL_CLIENTSCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}"
|
URL_CLIENT_OPTIONAL_CLIENT_SCOPE = "{url}/admin/realms/{realm}/clients/{cid}/optional-client-scopes/{id}"
|
||||||
|
|
||||||
URL_CLIENT_GROUP_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_GROUP_ROLEMAPPINGS_AVAILABLE = (
|
URL_CLIENT_GROUP_ROLEMAPPINGS_AVAILABLE = (
|
||||||
@@ -154,18 +154,6 @@ URL_AUTHZ_CUSTOM_POLICY = "{url}/admin/realms/{realm}/clients/{client_id}/authz/
|
|||||||
URL_AUTHZ_CUSTOM_POLICIES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy"
|
URL_AUTHZ_CUSTOM_POLICIES = "{url}/admin/realms/{realm}/clients/{client_id}/authz/resource-server/policy"
|
||||||
|
|
||||||
|
|
||||||
def normalize_keycloak_url(url: str) -> str:
|
|
||||||
"""Normalize Keycloak base URL for Admin REST API access.
|
|
||||||
|
|
||||||
Keycloak 17+ (Quarkus) exposes the API at the server root without an /auth prefix.
|
|
||||||
WildFly-based Keycloak used /auth as the context path. Trailing slashes are removed.
|
|
||||||
"""
|
|
||||||
url = url.rstrip("/")
|
|
||||||
if url.endswith("/auth"):
|
|
||||||
return url[:-5]
|
|
||||||
return url
|
|
||||||
|
|
||||||
|
|
||||||
def keycloak_argument_spec() -> dict[str, t.Any]:
|
def keycloak_argument_spec() -> dict[str, t.Any]:
|
||||||
"""
|
"""
|
||||||
Returns argument_spec of options common to keycloak_*-modules
|
Returns argument_spec of options common to keycloak_*-modules
|
||||||
@@ -215,7 +203,7 @@ def _token_request(module_params: dict[str, t.Any], payload: dict[str, t.Any]) -
|
|||||||
'refresh_token' for type 'refresh_token'.
|
'refresh_token' for type 'refresh_token'.
|
||||||
:return: access token
|
:return: access token
|
||||||
"""
|
"""
|
||||||
base_url = normalize_keycloak_url(module_params["auth_keycloak_url"])
|
base_url = module_params["auth_keycloak_url"]
|
||||||
if not base_url.lower().startswith(("http", "https")):
|
if not base_url.lower().startswith(("http", "https")):
|
||||||
raise KeycloakError(f"auth_url '{base_url}' should either start with 'http' or 'https'.")
|
raise KeycloakError(f"auth_url '{base_url}' should either start with 'http' or 'https'.")
|
||||||
auth_realm = module_params.get("auth_realm")
|
auth_realm = module_params.get("auth_realm")
|
||||||
@@ -403,7 +391,7 @@ class KeycloakAPI:
|
|||||||
|
|
||||||
def __init__(self, module: AnsibleModule, connection_header: dict[str, str]) -> None:
|
def __init__(self, module: AnsibleModule, connection_header: dict[str, str]) -> None:
|
||||||
self.module = module
|
self.module = module
|
||||||
self.baseurl = normalize_keycloak_url(self.module.params.get("auth_keycloak_url"))
|
self.baseurl = self.module.params.get("auth_keycloak_url")
|
||||||
self.validate_certs = self.module.params.get("validate_certs")
|
self.validate_certs = self.module.params.get("validate_certs")
|
||||||
self.connection_timeout = self.module.params.get("connection_timeout")
|
self.connection_timeout = self.module.params.get("connection_timeout")
|
||||||
self.restheaders = connection_header
|
self.restheaders = connection_header
|
||||||
@@ -713,7 +701,7 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not obtain list of clients for realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not obtain list of clients for realm {realm}: {e}")
|
||||||
|
|
||||||
def get_client_by_clientid(self, client_id, realm: str = "master"):
|
def get_client_by_client_id(self, client_id, realm: str = "master"):
|
||||||
"""Get client representation by clientId
|
"""Get client representation by clientId
|
||||||
:param client_id: The clientId to be queried
|
:param client_id: The clientId to be queried
|
||||||
:param realm: realm from which to obtain the client representation
|
:param realm: realm from which to obtain the client representation
|
||||||
@@ -756,7 +744,7 @@ class KeycloakAPI:
|
|||||||
:param realm: client template from this realm
|
:param realm: client template from this realm
|
||||||
:return: id of client (usually a UUID)
|
:return: id of client (usually a UUID)
|
||||||
"""
|
"""
|
||||||
result = self.get_client_by_clientid(client_id, realm)
|
result = self.get_client_by_client_id(client_id, realm)
|
||||||
if isinstance(result, dict) and "id" in result:
|
if isinstance(result, dict) and "id" in result:
|
||||||
return result["id"]
|
return result["id"]
|
||||||
else:
|
else:
|
||||||
@@ -1301,22 +1289,22 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not delete client template {id} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not delete client template {id} in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscopes(self, realm: str = "master"):
|
def get_client_scopes(self, realm: str = "master"):
|
||||||
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
||||||
|
|
||||||
To fetch the full data of the group, make a subsequent call to
|
To fetch the full data of the group, make a subsequent call to
|
||||||
get_clientscope_by_clientscopeid, passing in the ID of the group you wish to return.
|
get_client_scope_by_client_scope_id, passing in the ID of the group you wish to return.
|
||||||
|
|
||||||
:param realm: Realm in which the client scope resides; default 'master'.
|
:param realm: Realm in which the client scope resides; default 'master'.
|
||||||
:return The client scopes of this realm (default "master")
|
:return The client scopes of this realm (default "master")
|
||||||
"""
|
"""
|
||||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
client_scopes_url = URL_CLIENT_SCOPES.format(url=self.baseurl, realm=realm)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(clientscopes_url, method="GET")
|
return self._request_and_deserialize(client_scopes_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch list of client scopes in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch list of client scopes in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscope_by_clientscopeid(self, cid, realm: str = "master"):
|
def get_client_scope_by_client_scope_id(self, cid, realm: str = "master"):
|
||||||
"""Fetch a keycloak client scope from the provided realm using the client scope's unique ID.
|
"""Fetch a keycloak client scope from the provided realm using the client scope's unique ID.
|
||||||
|
|
||||||
If the client scope does not exist, None is returned.
|
If the client scope does not exist, None is returned.
|
||||||
@@ -1325,9 +1313,9 @@ class KeycloakAPI:
|
|||||||
:param cid: UUID of the client scope to be returned
|
:param cid: UUID of the client scope to be returned
|
||||||
:param realm: Realm in which the client scope resides; default 'master'.
|
:param realm: Realm in which the client scope resides; default 'master'.
|
||||||
"""
|
"""
|
||||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=cid)
|
client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=cid)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(clientscope_url, method="GET")
|
return self._request_and_deserialize(client_scope_url, method="GET")
|
||||||
|
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
if e.code == HTTPStatus.NOT_FOUND:
|
if e.code == HTTPStatus.NOT_FOUND:
|
||||||
@@ -1337,7 +1325,7 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg=f"Could not client scope group {cid} in realm {realm}: {e}")
|
self.module.fail_json(msg=f"Could not client scope group {cid} in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscope_by_name(self, name, realm: str = "master"):
|
def get_client_scope_by_name(self, name, realm: str = "master"):
|
||||||
"""Fetch a keycloak client scope within a realm based on its name.
|
"""Fetch a keycloak client scope within a realm based on its name.
|
||||||
|
|
||||||
The Keycloak API does not allow filtering of the client scopes resource by name.
|
The Keycloak API does not allow filtering of the client scopes resource by name.
|
||||||
@@ -1349,44 +1337,44 @@ class KeycloakAPI:
|
|||||||
:param realm: Realm in which the client scope resides; default 'master'
|
:param realm: Realm in which the client scope resides; default 'master'
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
all_clientscopes = self.get_clientscopes(realm=realm)
|
all_client_scopes = self.get_client_scopes(realm=realm)
|
||||||
|
|
||||||
for clientscope in all_clientscopes:
|
for client_scope in all_client_scopes:
|
||||||
if clientscope["name"] == name:
|
if client_scope["name"] == name:
|
||||||
return self.get_clientscope_by_clientscopeid(clientscope["id"], realm=realm)
|
return self.get_client_scope_by_client_scope_id(client_scope["id"], realm=realm)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg=f"Could not fetch client scope {name} in realm {realm}: {e}")
|
self.module.fail_json(msg=f"Could not fetch client scope {name} in realm {realm}: {e}")
|
||||||
|
|
||||||
def create_clientscope(self, clientscoperep, realm: str = "master"):
|
def create_client_scope(self, client_scope_rep, realm: str = "master"):
|
||||||
"""Create a Keycloak client scope.
|
"""Create a Keycloak client scope.
|
||||||
|
|
||||||
:param clientscoperep: a ClientScopeRepresentation of the clientscope to be created. Must contain at minimum the field name.
|
:param client_scope_rep: a ClientScopeRepresentation of the client scope to be created. Must contain at minimum the field name.
|
||||||
:return: HTTPResponse object on success
|
:return: HTTPResponse object on success
|
||||||
"""
|
"""
|
||||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
client_scopes_url = URL_CLIENT_SCOPES.format(url=self.baseurl, realm=realm)
|
||||||
try:
|
try:
|
||||||
return self._request(clientscopes_url, method="POST", data=json.dumps(clientscoperep))
|
return self._request(client_scopes_url, method="POST", data=json.dumps(client_scope_rep))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not create clientscope {clientscoperep['name']} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not create client scope {client_scope_rep['name']} in realm {realm}: {e}")
|
||||||
|
|
||||||
def update_clientscope(self, clientscoperep, realm: str = "master"):
|
def update_client_scope(self, client_scope_rep, realm: str = "master"):
|
||||||
"""Update an existing client scope.
|
"""Update an existing client scope.
|
||||||
|
|
||||||
:param grouprep: A GroupRepresentation of the updated group.
|
:param grouprep: A GroupRepresentation of the updated group.
|
||||||
:return HTTPResponse object on success
|
:return HTTPResponse object on success
|
||||||
"""
|
"""
|
||||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=clientscoperep["id"])
|
client_scope_url = URL_CLIENT_SCOPE.format(url=self.baseurl, realm=realm, id=client_scope_rep["id"])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self._request(clientscope_url, method="PUT", data=json.dumps(clientscoperep))
|
return self._request(client_scope_url, method="PUT", data=json.dumps(client_scope_rep))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not update clientscope {clientscoperep['name']} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not update client scope {client_scope_rep['name']} in realm {realm}: {e}")
|
||||||
|
|
||||||
def delete_clientscope(self, name=None, cid=None, realm: str = "master"):
|
def delete_client_scope(self, name=None, cid=None, realm: str = "master"):
|
||||||
"""Delete a client scope. One of name or cid must be provided.
|
"""Delete a client scope. One of name or cid must be provided.
|
||||||
|
|
||||||
Providing the client scope ID is preferred as it avoids a second lookup to
|
Providing the client scope ID is preferred as it avoids a second lookup to
|
||||||
@@ -1405,9 +1393,9 @@ class KeycloakAPI:
|
|||||||
# in the case that both are provided, prefer the ID, since it is one
|
# in the case that both are provided, prefer the ID, since it is one
|
||||||
# less lookup.
|
# less lookup.
|
||||||
if cid is None and name is not None:
|
if cid is None and name is not None:
|
||||||
for clientscope in self.get_clientscopes(realm=realm):
|
for client_scope in self.get_client_scopes(realm=realm):
|
||||||
if clientscope["name"] == name:
|
if client_scope["name"] == name:
|
||||||
cid = clientscope["id"]
|
cid = client_scope["id"]
|
||||||
break
|
break
|
||||||
|
|
||||||
# if the group doesn't exist - no problem, nothing to delete.
|
# if the group doesn't exist - no problem, nothing to delete.
|
||||||
@@ -1415,30 +1403,30 @@ class KeycloakAPI:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# should have a good cid by here.
|
# should have a good cid by here.
|
||||||
clientscope_url = URL_CLIENTSCOPE.format(realm=realm, id=cid, url=self.baseurl)
|
client_scope_url = URL_CLIENT_SCOPE.format(realm=realm, id=cid, url=self.baseurl)
|
||||||
try:
|
try:
|
||||||
return self._request(clientscope_url, method="DELETE")
|
return self._request(client_scope_url, method="DELETE")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Unable to delete client scope {cid}: {e}")
|
self.fail_request(e, msg=f"Unable to delete client scope {cid}: {e}")
|
||||||
|
|
||||||
def get_clientscope_protocolmappers(self, cid, realm: str = "master"):
|
def get_client_scope_protocolmappers(self, cid, realm: str = "master"):
|
||||||
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
||||||
|
|
||||||
To fetch the full data of the group, make a subsequent call to
|
To fetch the full data of the group, make a subsequent call to
|
||||||
get_clientscope_by_clientscopeid, passing in the ID of the group you wish to return.
|
get_client_scope_by_client_scope_id, passing in the ID of the group you wish to return.
|
||||||
|
|
||||||
:param cid: id of client scope (not name).
|
:param cid: id of client scope (not name).
|
||||||
:param realm: Realm in which the client scope resides; default 'master'.
|
:param realm: Realm in which the client scope resides; default 'master'.
|
||||||
:return The protocolmappers of this realm (default "master")
|
:return The protocolmappers of this realm (default "master")
|
||||||
"""
|
"""
|
||||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm)
|
protocolmappers_url = URL_CLIENT_SCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(protocolmappers_url, method="GET")
|
return self._request_and_deserialize(protocolmappers_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch list of protocolmappers in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch list of protocolmappers in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscope_protocolmapper_by_protocolmapperid(self, pid, cid, realm: str = "master"):
|
def get_client_scope_protocolmapper_by_protocolmapperid(self, pid, cid, realm: str = "master"):
|
||||||
"""Fetch a keycloak client scope from the provided realm using the client scope's unique ID.
|
"""Fetch a keycloak client scope from the provided realm using the client scope's unique ID.
|
||||||
|
|
||||||
If the client scope does not exist, None is returned.
|
If the client scope does not exist, None is returned.
|
||||||
@@ -1449,7 +1437,7 @@ class KeycloakAPI:
|
|||||||
:param cid: UUID of the client scope to be returned
|
:param cid: UUID of the client scope to be returned
|
||||||
:param realm: Realm in which the client scope resides; default 'master'.
|
:param realm: Realm in which the client scope resides; default 'master'.
|
||||||
"""
|
"""
|
||||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid)
|
protocolmapper_url = URL_CLIENT_SCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(protocolmapper_url, method="GET")
|
return self._request_and_deserialize(protocolmapper_url, method="GET")
|
||||||
|
|
||||||
@@ -1461,7 +1449,7 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg=f"Could not fetch protocolmapper {cid} in realm {realm}: {e}")
|
self.module.fail_json(msg=f"Could not fetch protocolmapper {cid} in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscope_protocolmapper_by_name(self, cid, name, realm: str = "master"):
|
def get_client_scope_protocolmapper_by_name(self, cid, name, realm: str = "master"):
|
||||||
"""Fetch a keycloak client scope within a realm based on its name.
|
"""Fetch a keycloak client scope within a realm based on its name.
|
||||||
|
|
||||||
The Keycloak API does not allow filtering of the client scopes resource by name.
|
The Keycloak API does not allow filtering of the client scopes resource by name.
|
||||||
@@ -1474,11 +1462,11 @@ class KeycloakAPI:
|
|||||||
:param realm: Realm in which the client scope resides; default 'master'
|
:param realm: Realm in which the client scope resides; default 'master'
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
all_protocolmappers = self.get_clientscope_protocolmappers(cid, realm=realm)
|
all_protocolmappers = self.get_client_scope_protocolmappers(cid, realm=realm)
|
||||||
|
|
||||||
for protocolmapper in all_protocolmappers:
|
for protocolmapper in all_protocolmappers:
|
||||||
if protocolmapper["name"] == name:
|
if protocolmapper["name"] == name:
|
||||||
return self.get_clientscope_protocolmapper_by_protocolmapperid(
|
return self.get_client_scope_protocolmapper_by_protocolmapperid(
|
||||||
protocolmapper["id"], cid, realm=realm
|
protocolmapper["id"], cid, realm=realm
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1487,27 +1475,27 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg=f"Could not fetch protocolmapper {name} in realm {realm}: {e}")
|
self.module.fail_json(msg=f"Could not fetch protocolmapper {name} in realm {realm}: {e}")
|
||||||
|
|
||||||
def create_clientscope_protocolmapper(self, cid, mapper_rep, realm: str = "master"):
|
def create_client_scope_protocolmapper(self, cid, mapper_rep, realm: str = "master"):
|
||||||
"""Create a Keycloak client scope protocolmapper.
|
"""Create a Keycloak client scope protocolmapper.
|
||||||
|
|
||||||
:param cid: Id of the client scope.
|
:param cid: Id of the client scope.
|
||||||
:param mapper_rep: a ProtocolMapperRepresentation of the protocolmapper to be created. Must contain at minimum the field name.
|
:param mapper_rep: a ProtocolMapperRepresentation of the protocolmapper to be created. Must contain at minimum the field name.
|
||||||
:return: HTTPResponse object on success
|
:return: HTTPResponse object on success
|
||||||
"""
|
"""
|
||||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm)
|
protocolmappers_url = URL_CLIENT_SCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm)
|
||||||
try:
|
try:
|
||||||
return self._request(protocolmappers_url, method="POST", data=json.dumps(mapper_rep))
|
return self._request(protocolmappers_url, method="POST", data=json.dumps(mapper_rep))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not create protocolmapper {mapper_rep['name']} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not create protocolmapper {mapper_rep['name']} in realm {realm}: {e}")
|
||||||
|
|
||||||
def update_clientscope_protocolmappers(self, cid, mapper_rep, realm: str = "master"):
|
def update_client_scope_protocolmappers(self, cid, mapper_rep, realm: str = "master"):
|
||||||
"""Update an existing client scope.
|
"""Update an existing client scope.
|
||||||
|
|
||||||
:param cid: Id of the client scope.
|
:param cid: Id of the client scope.
|
||||||
:param mapper_rep: A ProtocolMapperRepresentation of the updated protocolmapper.
|
:param mapper_rep: A ProtocolMapperRepresentation of the updated protocolmapper.
|
||||||
:return HTTPResponse object on success
|
:return HTTPResponse object on success
|
||||||
"""
|
"""
|
||||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(
|
protocolmapper_url = URL_CLIENT_SCOPE_PROTOCOLMAPPER.format(
|
||||||
url=self.baseurl, realm=realm, id=cid, mapper_id=mapper_rep["id"]
|
url=self.baseurl, realm=realm, id=cid, mapper_id=mapper_rep["id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1519,37 +1507,37 @@ class KeycloakAPI:
|
|||||||
e, msg=f"Could not update protocolmappers for client scope {mapper_rep} in realm {realm}: {e}"
|
e, msg=f"Could not update protocolmappers for client scope {mapper_rep} in realm {realm}: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_default_clientscopes(self, realm, client_id=None):
|
def get_default_client_scopes(self, realm, client_id=None):
|
||||||
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
||||||
|
|
||||||
To fetch the full data of the client scope, make a subsequent call to
|
To fetch the full data of the client scope, make a subsequent call to
|
||||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return.
|
||||||
|
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
:return The default client scopes of this realm or client
|
:return The default client scopes of this realm or client
|
||||||
"""
|
"""
|
||||||
url = URL_DEFAULT_CLIENTSCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENTSCOPES
|
url = URL_DEFAULT_CLIENT_SCOPES if client_id is None else URL_CLIENT_DEFAULT_CLIENT_SCOPES
|
||||||
return self._get_clientscopes_of_type(realm, url, "default", client_id)
|
return self._get_client_scopes_of_type(realm, url, "default", client_id)
|
||||||
|
|
||||||
def get_optional_clientscopes(self, realm, client_id=None):
|
def get_optional_client_scopes(self, realm, client_id=None):
|
||||||
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
||||||
|
|
||||||
To fetch the full data of the client scope, make a subsequent call to
|
To fetch the full data of the client scope, make a subsequent call to
|
||||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return.
|
||||||
|
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
:return The optional client scopes of this realm or client
|
:return The optional client scopes of this realm or client
|
||||||
"""
|
"""
|
||||||
url = URL_OPTIONAL_CLIENTSCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENTSCOPES
|
url = URL_OPTIONAL_CLIENT_SCOPES if client_id is None else URL_CLIENT_OPTIONAL_CLIENT_SCOPES
|
||||||
return self._get_clientscopes_of_type(realm, url, "optional", client_id)
|
return self._get_client_scopes_of_type(realm, url, "optional", client_id)
|
||||||
|
|
||||||
def _get_clientscopes_of_type(self, realm, url_template, scope_type, client_id=None):
|
def _get_client_scopes_of_type(self, realm, url_template, scope_type, client_id=None):
|
||||||
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
"""Fetch the name and ID of all client scopes on the Keycloak server.
|
||||||
|
|
||||||
To fetch the full data of the client scope, make a subsequent call to
|
To fetch the full data of the client scope, make a subsequent call to
|
||||||
get_clientscope_by_clientscopeid, passing in the ID of the client scope you wish to return.
|
get_client_scope_by_client_scope_id, passing in the ID of the client scope you wish to return.
|
||||||
|
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param url_template the template for the right type
|
:param url_template the template for the right type
|
||||||
@@ -1558,75 +1546,75 @@ class KeycloakAPI:
|
|||||||
:return The client scopes of the specified type of this realm
|
:return The client scopes of the specified type of this realm
|
||||||
"""
|
"""
|
||||||
if client_id is None:
|
if client_id is None:
|
||||||
clientscopes_url = url_template.format(url=self.baseurl, realm=realm)
|
client_scopes_url = url_template.format(url=self.baseurl, realm=realm)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(clientscopes_url, method="GET")
|
return self._request_and_deserialize(client_scopes_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch list of {scope_type} client scopes in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch list of {scope_type} client scopes in realm {realm}: {e}")
|
||||||
else:
|
else:
|
||||||
cid = self.get_client_id(client_id=client_id, realm=realm)
|
cid = self.get_client_id(client_id=client_id, realm=realm)
|
||||||
clientscopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid)
|
client_scopes_url = url_template.format(url=self.baseurl, realm=realm, cid=cid)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(clientscopes_url, method="GET")
|
return self._request_and_deserialize(client_scopes_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e,
|
e,
|
||||||
msg=f"Could not fetch list of {scope_type} clientscopes in client {client_id}: {clientscopes_url}",
|
msg=f"Could not fetch list of {scope_type} client scopes in client {client_id}: {client_scopes_url}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _decide_url_type_clientscope(self, client_id=None, scope_type="default"):
|
def _decide_url_type_client_scope(self, client_id=None, scope_type="default"):
|
||||||
"""Decides which url to use.
|
"""Decides which url to use.
|
||||||
:param scope_type this can be either optional or default
|
:param scope_type this can be either optional or default
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
"""
|
"""
|
||||||
if client_id is None:
|
if client_id is None:
|
||||||
if scope_type == "default":
|
if scope_type == "default":
|
||||||
return URL_DEFAULT_CLIENTSCOPE
|
return URL_DEFAULT_CLIENT_SCOPE
|
||||||
if scope_type == "optional":
|
if scope_type == "optional":
|
||||||
return URL_OPTIONAL_CLIENTSCOPE
|
return URL_OPTIONAL_CLIENT_SCOPE
|
||||||
else:
|
else:
|
||||||
if scope_type == "default":
|
if scope_type == "default":
|
||||||
return URL_CLIENT_DEFAULT_CLIENTSCOPE
|
return URL_CLIENT_DEFAULT_CLIENT_SCOPE
|
||||||
if scope_type == "optional":
|
if scope_type == "optional":
|
||||||
return URL_CLIENT_OPTIONAL_CLIENTSCOPE
|
return URL_CLIENT_OPTIONAL_CLIENT_SCOPE
|
||||||
|
|
||||||
def add_default_clientscope(self, id, realm: str = "master", client_id=None):
|
def add_default_client_scope(self, id, realm: str = "master", client_id=None):
|
||||||
"""Add a client scope as default either on realm or client level.
|
"""Add a client scope as default either on realm or client level.
|
||||||
|
|
||||||
:param id: Client scope Id.
|
:param id: Client scope Id.
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
"""
|
"""
|
||||||
self._action_type_clientscope(id, client_id, "default", realm, "add")
|
self._action_type_client_scope(id, client_id, "default", realm, "add")
|
||||||
|
|
||||||
def add_optional_clientscope(self, id, realm: str = "master", client_id=None):
|
def add_optional_client_scope(self, id, realm: str = "master", client_id=None):
|
||||||
"""Add a client scope as optional either on realm or client level.
|
"""Add a client scope as optional either on realm or client level.
|
||||||
|
|
||||||
:param id: Client scope Id.
|
:param id: Client scope Id.
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
"""
|
"""
|
||||||
self._action_type_clientscope(id, client_id, "optional", realm, "add")
|
self._action_type_client_scope(id, client_id, "optional", realm, "add")
|
||||||
|
|
||||||
def delete_default_clientscope(self, id, realm: str = "master", client_id=None):
|
def delete_default_client_scope(self, id, realm: str = "master", client_id=None):
|
||||||
"""Remove a client scope as default either on realm or client level.
|
"""Remove a client scope as default either on realm or client level.
|
||||||
|
|
||||||
:param id: Client scope Id.
|
:param id: Client scope Id.
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
"""
|
"""
|
||||||
self._action_type_clientscope(id, client_id, "default", realm, "delete")
|
self._action_type_client_scope(id, client_id, "default", realm, "delete")
|
||||||
|
|
||||||
def delete_optional_clientscope(self, id, realm: str = "master", client_id=None):
|
def delete_optional_client_scope(self, id, realm: str = "master", client_id=None):
|
||||||
"""Remove a client scope as optional either on realm or client level.
|
"""Remove a client scope as optional either on realm or client level.
|
||||||
|
|
||||||
:param id: Client scope Id.
|
:param id: Client scope Id.
|
||||||
:param realm: Realm in which the client scope resides.
|
:param realm: Realm in which the client scope resides.
|
||||||
:param client_id: The client in which the client scope resides.
|
:param client_id: The client in which the client scope resides.
|
||||||
"""
|
"""
|
||||||
self._action_type_clientscope(id, client_id, "optional", realm, "delete")
|
self._action_type_client_scope(id, client_id, "optional", realm, "delete")
|
||||||
|
|
||||||
def _action_type_clientscope(
|
def _action_type_client_scope(
|
||||||
self, id=None, client_id=None, scope_type="default", realm: str = "master", action="add"
|
self, id=None, client_id=None, scope_type="default", realm: str = "master", action="add"
|
||||||
):
|
):
|
||||||
"""Delete or add a client scope of type.
|
"""Delete or add a client scope of type.
|
||||||
@@ -1637,12 +1625,12 @@ class KeycloakAPI:
|
|||||||
"""
|
"""
|
||||||
cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm)
|
cid = None if client_id is None else self.get_client_id(client_id=client_id, realm=realm)
|
||||||
# should have a good cid by here.
|
# should have a good cid by here.
|
||||||
clientscope_type_url = self._decide_url_type_clientscope(client_id, scope_type).format(
|
client_scope_type_url = self._decide_url_type_client_scope(client_id, scope_type).format(
|
||||||
realm=realm, id=id, cid=cid, url=self.baseurl
|
realm=realm, id=id, cid=cid, url=self.baseurl
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
method = "PUT" if action == "add" else "DELETE"
|
method = "PUT" if action == "add" else "DELETE"
|
||||||
return self._request(clientscope_type_url, method=method)
|
return self._request(client_scope_type_url, method=method)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
place = "realm" if client_id is None else f"client {client_id}"
|
place = "realm" if client_id is None else f"client {client_id}"
|
||||||
@@ -2032,7 +2020,7 @@ class KeycloakAPI:
|
|||||||
composite_url = ""
|
composite_url = ""
|
||||||
try:
|
try:
|
||||||
if clientid is not None:
|
if clientid is not None:
|
||||||
client = self.get_client_by_clientid(client_id=clientid, realm=realm)
|
client = self.get_client_by_client_id(client_id=clientid, realm=realm)
|
||||||
cid = client["id"]
|
cid = client["id"]
|
||||||
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
||||||
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
||||||
@@ -2050,7 +2038,7 @@ class KeycloakAPI:
|
|||||||
composite_url = ""
|
composite_url = ""
|
||||||
try:
|
try:
|
||||||
if clientid is not None:
|
if clientid is not None:
|
||||||
client = self.get_client_by_clientid(client_id=clientid, realm=realm)
|
client = self.get_client_by_client_id(client_id=clientid, realm=realm)
|
||||||
cid = client["id"]
|
cid = client["id"]
|
||||||
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
||||||
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
||||||
@@ -2069,7 +2057,7 @@ class KeycloakAPI:
|
|||||||
composite_url = ""
|
composite_url = ""
|
||||||
try:
|
try:
|
||||||
if clientid is not None:
|
if clientid is not None:
|
||||||
client = self.get_client_by_clientid(client_id=clientid, realm=realm)
|
client = self.get_client_by_client_id(client_id=clientid, realm=realm)
|
||||||
cid = client["id"]
|
cid = client["id"]
|
||||||
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
composite_url = URL_CLIENT_ROLE_COMPOSITES.format(
|
||||||
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
url=self.baseurl, realm=realm, id=cid, name=quote(rolerep["name"], safe="")
|
||||||
@@ -3271,192 +3259,192 @@ class KeycloakAPI:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_all_clientscope_scope_mappings(self, clientscope_id, realm: str = "master"):
|
def get_all_client_scope_scope_mappings(self, client_scope_id, realm: str = "master"):
|
||||||
"""Fetch all (realm and client) roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server.
|
"""Fetch all (realm and client) roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server.
|
||||||
:param clientscope_id: ID of the clientscope from which to obtain the associated roles.
|
:param client_scope_id: ID of the client scope from which to obtain the associated roles.
|
||||||
:param realm: Realm from which to obtain the scope.
|
:param realm: Realm from which to obtain the scope.
|
||||||
:return: The client scope scope-mappings.
|
:return: The client scope scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS.format(url=self.baseurl, realm=realm, id=clientscope_id)
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS.format(url=self.baseurl, realm=realm, id=client_scope_id)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch roles for client-scope {clientscope_id} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch roles for client scope {client_scope_id} in realm {realm}: {e}")
|
||||||
|
|
||||||
def get_clientscope_scope_mappings_realm(self, clientscope_id, realm: str = "master"):
|
def get_client_scope_scope_mappings_realm(self, client_scope_id, realm: str = "master"):
|
||||||
"""Fetch the realm roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server.
|
"""Fetch the realm roles (scope-mappings) associated with the client scope for a specific client scope on the Keycloak server.
|
||||||
:param clientscope_id: ID of the clientscope from which to obtain the associated roles.
|
:param client_scope_id: ID of the client scope from which to obtain the associated roles.
|
||||||
:param realm: Realm from which to obtain the scope.
|
:param realm: Realm from which to obtain the scope.
|
||||||
:return: The client scope realm scope-mappings.
|
:return: The client scope realm scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id
|
url=self.baseurl, realm=realm, id=client_scope_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e, msg=f"Could not fetch realm roles for client-scope {clientscope_id} in realm {realm}: {e}"
|
e, msg=f"Could not fetch realm roles for client scope {client_scope_id} in realm {realm}: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_clientscope_scope_mappings_client(self, clientscope_id, client_id, realm: str = "master"):
|
def get_client_scope_scope_mappings_client(self, client_scope_id, client_id, realm: str = "master"):
|
||||||
"""Fetch the client roles (scope-mappings) associated with the client scope for a specific client scope and client on the Keycloak server.
|
"""Fetch the client roles (scope-mappings) associated with the client scope for a specific client scope and client on the Keycloak server.
|
||||||
:param clientscope_id: ID of the clientscope from which to obtain the associated roles.
|
:param client_scope_id: ID of the client scope from which to obtain the associated roles.
|
||||||
:param clientid: ID of the client from which to obtain the associated roles.
|
:param clientid: ID of the client from which to obtain the associated roles.
|
||||||
:param realm: Realm from which to obtain the scope.
|
:param realm: Realm from which to obtain the scope.
|
||||||
:return: The client scope client scope-mappings.
|
:return: The client scope client scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id, client=client_id
|
url=self.baseurl, realm=realm, id=client_scope_id, client=client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e,
|
e,
|
||||||
msg=f"Could not fetch client roles from client {client_id} for client-scope {clientscope_id} in realm {realm}: {e}",
|
msg=f"Could not fetch client roles from client {client_id} for client scope {client_scope_id} in realm {realm}: {e}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_client_role_scope_from_client(self, clientid, clientscopeid, realm: str = "master"):
|
def get_client_role_scope_from_client(self, target_client_id, role_owner_client_id, realm: str = "master"):
|
||||||
"""Fetch the roles associated with the client's scope for a specific client on the Keycloak server.
|
"""Fetch the roles associated with the client's scope for a specific client on the Keycloak server.
|
||||||
:param clientid: ID of the client from which to obtain the associated roles.
|
:param target_client_id: ID of the client from which to obtain the associated roles.
|
||||||
:param clientscopeid: ID of the client who owns the roles.
|
:param role_owner_client_id: ID of the client who owns the roles.
|
||||||
:param realm: Realm from which to obtain the scope.
|
:param realm: Realm from which to obtain the scope.
|
||||||
:return: The client scope of roles from specified client.
|
:return: The client scope of roles from specified client.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
||||||
url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid
|
url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
return self._request_and_deserialize(client_role_scope_url, method="GET")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch roles scope for client {target_client_id} in realm {realm}: {e}")
|
||||||
|
|
||||||
def update_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm: str = "master"):
|
def update_client_role_scope_from_client(self, roles, target_client_id, role_owner_client_id, realm: str = "master"):
|
||||||
"""Update and fetch the roles associated with the client's scope on the Keycloak server.
|
"""Update and fetch the roles associated with the client's scope on the Keycloak server.
|
||||||
:param payload: List of roles to be added to the scope.
|
:param roles: List of roles to be added to the scope.
|
||||||
:param clientid: ID of the client to update scope.
|
:param target_client_id: ID of the client to update scope.
|
||||||
:param clientscopeid: ID of the client who owns the roles.
|
:param role_owner_client_id: ID of the client who owns the roles.
|
||||||
:param realm: Realm from which to obtain the clients.
|
:param realm: Realm from which to obtain the clients.
|
||||||
:return: The client scope of roles from specified client.
|
:return: The client scope of roles from specified client.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
||||||
url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid
|
url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="POST", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="POST", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not update roles scope for client {target_client_id} in realm {realm}: {e}")
|
||||||
|
|
||||||
return self.get_client_role_scope_from_client(clientid, clientscopeid, realm)
|
return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm)
|
||||||
|
|
||||||
def delete_client_role_scope_from_client(self, payload, clientid, clientscopeid, realm: str = "master"):
|
def delete_client_role_scope_from_client(self, roles, target_client_id, role_owner_client_id, realm: str = "master"):
|
||||||
"""Delete the roles contains in the payload from the client's scope on the Keycloak server.
|
"""Delete the roles from the client's scope on the Keycloak server.
|
||||||
:param payload: List of roles to be deleted.
|
:param roles: List of roles to be deleted.
|
||||||
:param clientid: ID of the client to delete roles from scope.
|
:param target_client_id: ID of the client to delete roles from scope.
|
||||||
:param clientscopeid: ID of the client who owns the roles.
|
:param role_owner_client_id: ID of the client who owns the roles.
|
||||||
:param realm: Realm from which to obtain the clients.
|
:param realm: Realm from which to obtain the clients.
|
||||||
:return: The client scope of roles from specified client.
|
:return: The client scope of roles from specified client.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_CLIENTS.format(
|
||||||
url=self.baseurl, realm=realm, id=clientid, scopeid=clientscopeid
|
url=self.baseurl, realm=realm, id=target_client_id, scopeid=role_owner_client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not delete roles from scope for client {target_client_id} in realm {realm}: {e}")
|
||||||
|
|
||||||
return self.get_client_role_scope_from_client(clientid, clientscopeid, realm)
|
return self.get_client_role_scope_from_client(target_client_id, role_owner_client_id, realm)
|
||||||
|
|
||||||
def update_clientscope_scope_mappings_client(
|
def update_client_scope_scope_mappings_client(
|
||||||
self, payload: list[dict], clientscope_id: str, client_id: str, realm: str = "master"
|
self, roles: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master"
|
||||||
):
|
):
|
||||||
"""Update and fetch the client roles (scope-mappings) associated with the client scope on the Keycloak server.
|
"""Update and fetch the client roles (scope-mappings) associated with the client scope on the Keycloak server.
|
||||||
:param payload: List of client roles to be added to the scope.
|
:param roles: List of client roles to be added to the scope.
|
||||||
:param clientscope_id: ID of the clientscope to update scope-mappings.
|
:param client_scope_id: ID of the client scope to update scope-mappings.
|
||||||
:param clientid: ID of the client from which to obtain the associated roles.
|
:param role_owner_client_id: ID of the client from which to obtain the associated roles.
|
||||||
:param realm: Realm from which to obtain the client.
|
:param realm: Realm from which to obtain the client.
|
||||||
:return: The client scope client scope-mappings.
|
:return: The client scope client scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id, client=client_id
|
url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="POST", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="POST", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e,
|
e,
|
||||||
msg=f"Could not update scope mappings for client-scope {client_id}.{clientscope_id} in realm {realm}: {e}",
|
msg=f"Could not update scope mappings for client scope {role_owner_client_id}.{client_scope_id} in realm {realm}: {e}",
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.get_clientscope_scope_mappings_client(clientscope_id, client_id, realm)
|
return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm)
|
||||||
|
|
||||||
def update_clientscope_scope_mappings_realm(self, payload: list[dict], clientscope_id: str, realm: str = "master"):
|
def update_client_scope_scope_mappings_realm(self, roles: list[dict], client_scope_id: str, realm: str = "master"):
|
||||||
"""Update and fetch the realm roles (scope-mappings) associated with the client scope on the Keycloak server.
|
"""Update and fetch the realm roles (scope-mappings) associated with the client scope on the Keycloak server.
|
||||||
:param payload: List of realm roles to be added to the scope.
|
:param roles: List of realm roles to be added to the scope.
|
||||||
:param clientscope_id: ID of the clientscope to update scope-mappings.
|
:param client_scope_id: ID of the client scope to update scope-mappings.
|
||||||
:param realm: Realm from which to obtain the roles.
|
:param realm: Realm from which to obtain the roles.
|
||||||
:return: The client scope realm scope-mappings.
|
:return: The client scope realm scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id
|
url=self.baseurl, realm=realm, id=client_scope_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="POST", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="POST", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e, msg=f"Could not update scope mappings for client-scope {clientscope_id} in realm {realm}: {e}"
|
e, msg=f"Could not update scope mappings for client scope {client_scope_id} in realm {realm}: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.get_clientscope_scope_mappings_realm(clientscope_id, realm)
|
return self.get_client_scope_scope_mappings_realm(client_scope_id, realm)
|
||||||
|
|
||||||
def delete_clientscope_scope_mappings_client(
|
def delete_client_scope_scope_mappings_client(
|
||||||
self, payload: list[dict], clientscope_id: str, client_id: str, realm: str = "master"
|
self, roles: list[dict], client_scope_id: str, role_owner_client_id: str, realm: str = "master"
|
||||||
):
|
):
|
||||||
"""Delete the client roles (scope_mappings) contained in the payload from the clientscope on the Keycloak server.
|
"""Delete the client roles (scope_mappings) from the client scope on the Keycloak server.
|
||||||
:param payload: List of roles to be deleted.
|
:param roles: List of roles to be deleted.
|
||||||
:param clientscope_id: ID of the clientscope to delete roles from scope-mappings.
|
:param client_scope_id: ID of the client scope to delete roles from scope-mappings.
|
||||||
:param clientid: ID of the client who owns the roles.
|
:param role_owner_client_id: ID of the client who owns the roles.
|
||||||
:param realm: Realm from which to obtain the client.
|
:param realm: Realm from which to obtain the client.
|
||||||
:return: The client scope client scope-mappings.
|
:return: The client scope client scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_CLIENT.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id, client=client_id
|
url=self.baseurl, realm=realm, id=client_scope_id, client=role_owner_client_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e,
|
e,
|
||||||
msg=f"Could not delete scope mappings for client-scope {client_id}.{clientscope_id} in realm {realm}: {e}",
|
msg=f"Could not delete scope mappings for client scope {role_owner_client_id}.{client_scope_id} in realm {realm}: {e}",
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.get_clientscope_scope_mappings_client(clientscope_id, client_id, realm)
|
return self.get_client_scope_scope_mappings_client(client_scope_id, role_owner_client_id, realm)
|
||||||
|
|
||||||
def delete_clientscope_scope_mappings_realm(self, payload: list[dict], clientscope_id: str, realm: str = "master"):
|
def delete_client_scope_scope_mappings_realm(self, roles: list[dict], client_scope_id: str, realm: str = "master"):
|
||||||
"""Delete the realm roles (scope_mappings) contained in the payload from the clientscope on the Keycloak server.
|
"""Delete the realm roles (scope_mappings) contained in the roles from the client scope on the Keycloak server.
|
||||||
:param payload: List of roles to be deleted.
|
:param roles: List of roles to be deleted.
|
||||||
:param clientscope_id: ID of the clientscope to delete roles from scope-mappings.
|
:param client_scope_id: ID of the client scope to delete roles from scope-mappings.
|
||||||
:param realm: Realm from which to obtain the roles.
|
:param realm: Realm from which to obtain the roles.
|
||||||
:return: The client scope realm scope-mappings.
|
:return: The client scope realm scope-mappings.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENTSCOPE_SCOPE_MAPPINGS_REALM.format(
|
client_role_scope_url = URL_CLIENT_SCOPE_SCOPE_MAPPINGS_REALM.format(
|
||||||
url=self.baseurl, realm=realm, id=clientscope_id
|
url=self.baseurl, realm=realm, id=client_scope_id
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(
|
self.fail_request(
|
||||||
e, msg=f"Could not delete scope mappings for client-scope {clientscope_id} in realm {realm}: {e}"
|
e, msg=f"Could not delete scope mappings for client scope {client_scope_id} in realm {realm}: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.get_clientscope_scope_mappings_realm(clientscope_id, realm)
|
return self.get_client_scope_scope_mappings_realm(client_scope_id, realm)
|
||||||
|
|
||||||
def get_client_role_scope_from_realm(self, clientid, realm: str = "master"):
|
def get_client_role_scope_from_realm(self, clientid, realm: str = "master"):
|
||||||
"""Fetch the realm roles from the client's scope on the Keycloak server.
|
"""Fetch the realm roles from the client's scope on the Keycloak server.
|
||||||
@@ -3470,32 +3458,32 @@ class KeycloakAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not fetch roles scope for client {clientid} in realm {realm}: {e}")
|
||||||
|
|
||||||
def update_client_role_scope_from_realm(self, payload, clientid, realm: str = "master"):
|
def update_client_role_scope_from_realm(self, roles, clientid, realm: str = "master"):
|
||||||
"""Update and fetch the realm roles from the client's scope on the Keycloak server.
|
"""Update and fetch the realm roles from the client's scope on the Keycloak server.
|
||||||
:param payload: List of realm roles to add.
|
:param roles: List of realm roles to add.
|
||||||
:param clientid: ID of the client to update scope.
|
:param clientid: ID of the client to update scope.
|
||||||
:param realm: Realm from which to obtain the clients.
|
:param realm: Realm from which to obtain the clients.
|
||||||
:return: The client realm roles scope.
|
:return: The client realm roles scope.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid)
|
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="POST", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="POST", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not update roles scope for client {clientid} in realm {realm}: {e}")
|
||||||
|
|
||||||
return self.get_client_role_scope_from_realm(clientid, realm)
|
return self.get_client_role_scope_from_realm(clientid, realm)
|
||||||
|
|
||||||
def delete_client_role_scope_from_realm(self, payload, clientid, realm: str = "master"):
|
def delete_client_role_scope_from_realm(self, roles, clientid, realm: str = "master"):
|
||||||
"""Delete the realm roles contains in the payload from the client's scope on the Keycloak server.
|
"""Delete the realm roles from the client's scope on the Keycloak server.
|
||||||
:param payload: List of realm roles to delete.
|
:param roles: List of realm roles to delete.
|
||||||
:param clientid: ID of the client to delete roles from scope.
|
:param clientid: ID of the client to delete roles from scope.
|
||||||
:param realm: Realm from which to obtain the clients.
|
:param realm: Realm from which to obtain the clients.
|
||||||
:return: The client realm roles scope.
|
:return: The client realm roles scope.
|
||||||
"""
|
"""
|
||||||
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid)
|
client_role_scope_url = URL_CLIENT_ROLE_SCOPE_REALM.format(url=self.baseurl, realm=realm, id=clientid)
|
||||||
try:
|
try:
|
||||||
self._request(client_role_scope_url, method="DELETE", data=json.dumps(payload))
|
self._request(client_role_scope_url, method="DELETE", data=json.dumps(roles))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}")
|
self.fail_request(e, msg=f"Could not delete roles scope for client {clientid} in realm {realm}: {e}")
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ def keycloak_clientsecret_module_resolve_params(module: AnsibleModule, kc: Keycl
|
|||||||
# less lookup.
|
# less lookup.
|
||||||
if id is None:
|
if id is None:
|
||||||
# Due to the required_one_of spec, client_id is guaranteed to not be None
|
# Due to the required_one_of spec, client_id is guaranteed to not be None
|
||||||
client = kc.get_client_by_clientid(client_id, realm=realm)
|
client = kc.get_client_by_client_id(client_id, realm=realm)
|
||||||
|
|
||||||
if client is None:
|
if client is None:
|
||||||
module.fail_json(msg=f"Client does not exist {client_id}")
|
module.fail_json(msg=f"Client does not exist {client_id}")
|
||||||
|
|||||||
@@ -1100,11 +1100,11 @@ def add_default_client_scopes(desired_client, before_client, realm, kc):
|
|||||||
missing_scopes = [item for item in desired_default_scope if item not in before_client["defaultClientScopes"]]
|
missing_scopes = [item for item in desired_default_scope if item not in before_client["defaultClientScopes"]]
|
||||||
if not missing_scopes:
|
if not missing_scopes:
|
||||||
return
|
return
|
||||||
client_scopes = kc.get_clientscopes(realm)
|
client_scopes = kc.get_client_scopes(realm)
|
||||||
for name in missing_scopes:
|
for name in missing_scopes:
|
||||||
scope = find_match(client_scopes, "name", name)
|
scope = find_match(client_scopes, "name", name)
|
||||||
if scope:
|
if scope:
|
||||||
kc.add_default_clientscope(scope["id"], realm, desired_client["clientId"])
|
kc.add_default_client_scope(scope["id"], realm, desired_client["clientId"])
|
||||||
|
|
||||||
|
|
||||||
def add_optional_client_scopes(desired_client, before_client, realm, kc):
|
def add_optional_client_scopes(desired_client, before_client, realm, kc):
|
||||||
@@ -1139,11 +1139,11 @@ def add_optional_client_scopes(desired_client, before_client, realm, kc):
|
|||||||
missing_scopes = [item for item in desired_optional_scope if item not in before_client["optionalClientScopes"]]
|
missing_scopes = [item for item in desired_optional_scope if item not in before_client["optionalClientScopes"]]
|
||||||
if not missing_scopes:
|
if not missing_scopes:
|
||||||
return
|
return
|
||||||
client_scopes = kc.get_clientscopes(realm)
|
client_scopes = kc.get_client_scopes(realm)
|
||||||
for name in missing_scopes:
|
for name in missing_scopes:
|
||||||
scope = find_match(client_scopes, "name", name)
|
scope = find_match(client_scopes, "name", name)
|
||||||
if scope:
|
if scope:
|
||||||
kc.add_optional_clientscope(scope["id"], realm, desired_client["clientId"])
|
kc.add_optional_client_scope(scope["id"], realm, desired_client["clientId"])
|
||||||
|
|
||||||
|
|
||||||
def remove_default_client_scopes(desired_client, before_client, realm, kc):
|
def remove_default_client_scopes(desired_client, before_client, realm, kc):
|
||||||
@@ -1178,11 +1178,11 @@ def remove_default_client_scopes(desired_client, before_client, realm, kc):
|
|||||||
missing_scopes = [item for item in before_default_scope if item not in desired_client["defaultClientScopes"]]
|
missing_scopes = [item for item in before_default_scope if item not in desired_client["defaultClientScopes"]]
|
||||||
if not missing_scopes:
|
if not missing_scopes:
|
||||||
return
|
return
|
||||||
client_scopes = kc.get_default_clientscopes(realm, desired_client["clientId"])
|
client_scopes = kc.get_default_client_scopes(realm, desired_client["clientId"])
|
||||||
for name in missing_scopes:
|
for name in missing_scopes:
|
||||||
scope = find_match(client_scopes, "name", name)
|
scope = find_match(client_scopes, "name", name)
|
||||||
if scope:
|
if scope:
|
||||||
kc.delete_default_clientscope(scope["id"], realm, desired_client["clientId"])
|
kc.delete_default_client_scope(scope["id"], realm, desired_client["clientId"])
|
||||||
|
|
||||||
|
|
||||||
def remove_optional_client_scopes(desired_client, before_client, realm, kc):
|
def remove_optional_client_scopes(desired_client, before_client, realm, kc):
|
||||||
@@ -1217,11 +1217,11 @@ def remove_optional_client_scopes(desired_client, before_client, realm, kc):
|
|||||||
missing_scopes = [item for item in before_optional_scope if item not in desired_client["optionalClientScopes"]]
|
missing_scopes = [item for item in before_optional_scope if item not in desired_client["optionalClientScopes"]]
|
||||||
if not missing_scopes:
|
if not missing_scopes:
|
||||||
return
|
return
|
||||||
client_scopes = kc.get_optional_clientscopes(realm, desired_client["clientId"])
|
client_scopes = kc.get_optional_client_scopes(realm, desired_client["clientId"])
|
||||||
for name in missing_scopes:
|
for name in missing_scopes:
|
||||||
scope = find_match(client_scopes, "name", name)
|
scope = find_match(client_scopes, "name", name)
|
||||||
if scope:
|
if scope:
|
||||||
kc.delete_optional_clientscope(scope["id"], realm, desired_client["clientId"])
|
kc.delete_optional_client_scope(scope["id"], realm, desired_client["clientId"])
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -1346,7 +1346,7 @@ def main():
|
|||||||
|
|
||||||
# See if it already exists in Keycloak
|
# See if it already exists in Keycloak
|
||||||
if cid is None:
|
if cid is None:
|
||||||
before_client = kc.get_client_by_clientid(module.params.get("client_id"), realm=realm)
|
before_client = kc.get_client_by_client_id(module.params.get("client_id"), realm=realm)
|
||||||
if before_client is not None:
|
if before_client is not None:
|
||||||
cid = before_client["id"]
|
cid = before_client["id"]
|
||||||
else:
|
else:
|
||||||
@@ -1440,7 +1440,7 @@ def main():
|
|||||||
|
|
||||||
# create it
|
# create it
|
||||||
kc.create_client(desired_client, realm=realm)
|
kc.create_client(desired_client, realm=realm)
|
||||||
after_client = kc.get_client_by_clientid(desired_client["clientId"], realm=realm)
|
after_client = kc.get_client_by_client_id(desired_client["clientId"], realm=realm)
|
||||||
|
|
||||||
result["end_state"] = sanitize_cr(after_client)
|
result["end_state"] = sanitize_cr(after_client)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ description:
|
|||||||
to the REST API using OpenID Connect; the user connecting and the client being used must have the requisite access rights.
|
to the REST API using OpenID Connect; the user connecting and the client being used must have the requisite access rights.
|
||||||
In a default Keycloak installation, admin-cli and an admin user would work, as would a separate client definition with
|
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 scope tailored to your needs and a user having the expected roles.
|
||||||
- Client O(client_id) must have O(middleware_automation.keycloak.keycloak_client#module:full_scope_allowed) set to V(false).
|
- Client O(target_client_id) must have O(middleware_automation.keycloak.keycloak_client#module:full_scope_allowed) set to V(false).
|
||||||
- Attributes are multi-valued in the Keycloak API. All attributes are lists of individual values and are returned that way
|
- Attributes are multi-valued in the Keycloak API. All attributes are lists of individual values and are returned that way
|
||||||
by this module. You may pass single values for attributes when calling the module, and this is translated into a list
|
by this module. You may pass single values for attributes when calling the module, and this is translated into a list
|
||||||
suitable for the API.
|
suitable for the API.
|
||||||
@@ -50,12 +50,12 @@ options:
|
|||||||
- The Keycloak realm under which clients resides.
|
- The Keycloak realm under which clients resides.
|
||||||
default: 'master'
|
default: 'master'
|
||||||
|
|
||||||
client_id:
|
target_client_id:
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: true
|
||||||
description:
|
description:
|
||||||
- Roles provided in O(role_names) while be added to this client scope.
|
- Roles provided in O(role_names) while be added to this client scope.
|
||||||
client_scope_id:
|
role_owner_client_id:
|
||||||
type: str
|
type: str
|
||||||
description:
|
description:
|
||||||
- If the O(role_names) are client role, the client ID under which it resides.
|
- If the O(role_names) are client role, the client ID under which it resides.
|
||||||
@@ -66,8 +66,8 @@ options:
|
|||||||
elements: str
|
elements: str
|
||||||
description:
|
description:
|
||||||
- Names of roles to manipulate.
|
- Names of roles to manipulate.
|
||||||
- If O(client_scope_id) is present, all roles must be under this client.
|
- If O(role_owner_client_id) is present, all roles must be under this client.
|
||||||
- If O(client_scope_id) is absent, all roles must be under the realm.
|
- If O(role_owner_client_id) is absent, all roles must be under the realm.
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- middleware_automation.keycloak.keycloak
|
- middleware_automation.keycloak.keycloak
|
||||||
- middleware_automation.keycloak.actiongroup_keycloak
|
- middleware_automation.keycloak.actiongroup_keycloak
|
||||||
@@ -85,8 +85,8 @@ EXAMPLES = r"""
|
|||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
client_id: frontend-client-public
|
target_client_id: frontend-client-public
|
||||||
client_scope_id: backend-client-private
|
role_owner_client_id: backend-client-private
|
||||||
role_names:
|
role_names:
|
||||||
- backend-role-admin
|
- backend-role-admin
|
||||||
- backend-role-user
|
- backend-role-user
|
||||||
@@ -98,8 +98,8 @@ EXAMPLES = r"""
|
|||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
client_id: frontend-client-public
|
target_client_id: frontend-client-public
|
||||||
client_scope_id: backend-client-private
|
role_owner_client_id: backend-client-private
|
||||||
role_names:
|
role_names:
|
||||||
- backend-role-admin
|
- backend-role-admin
|
||||||
state: absent
|
state: absent
|
||||||
@@ -111,7 +111,7 @@ EXAMPLES = r"""
|
|||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
client_id: frontend-client-public
|
target_client_id: frontend-client-public
|
||||||
role_names:
|
role_names:
|
||||||
- realm-role-admin
|
- realm-role-admin
|
||||||
- realm-role-user
|
- realm-role-user
|
||||||
@@ -167,8 +167,8 @@ def main():
|
|||||||
argument_spec = keycloak_argument_spec()
|
argument_spec = keycloak_argument_spec()
|
||||||
|
|
||||||
meta_args = dict(
|
meta_args = dict(
|
||||||
client_id=dict(type="str", required=True),
|
target_client_id=dict(type="str", required=True),
|
||||||
client_scope_id=dict(type="str"),
|
role_owner_client_id=dict(type="str"),
|
||||||
realm=dict(type="str", default="master"),
|
realm=dict(type="str", default="master"),
|
||||||
role_names=dict(type="list", elements="str", required=True),
|
role_names=dict(type="list", elements="str", required=True),
|
||||||
state=dict(type="str", default="present", choices=["present", "absent"]),
|
state=dict(type="str", default="present", choices=["present", "absent"]),
|
||||||
@@ -189,8 +189,8 @@ def main():
|
|||||||
kc = KeycloakAPI(module, connection_header)
|
kc = KeycloakAPI(module, connection_header)
|
||||||
|
|
||||||
realm = module.params.get("realm")
|
realm = module.params.get("realm")
|
||||||
clientid = module.params.get("client_id")
|
target_client_id = module.params.get("target_client_id")
|
||||||
client_scope_id = module.params.get("client_scope_id")
|
role_owner_client_id = module.params.get("role_owner_client_id")
|
||||||
role_names = module.params.get("role_names")
|
role_names = module.params.get("role_names")
|
||||||
state = module.params.get("state")
|
state = module.params.get("state")
|
||||||
|
|
||||||
@@ -198,23 +198,23 @@ def main():
|
|||||||
if not objRealm:
|
if not objRealm:
|
||||||
module.fail_json(msg=f"Failed to retrive realm '{realm}'")
|
module.fail_json(msg=f"Failed to retrive realm '{realm}'")
|
||||||
|
|
||||||
objClient = kc.get_client_by_clientid(clientid, realm)
|
objClient = kc.get_client_by_client_id(target_client_id, realm)
|
||||||
if not objClient:
|
if not objClient:
|
||||||
module.fail_json(msg=f"Failed to retrive client '{realm}.{clientid}'")
|
module.fail_json(msg=f"Failed to retrive client '{realm}.{target_client_id}'")
|
||||||
if objClient["fullScopeAllowed"] and state == "present":
|
if objClient["fullScopeAllowed"] and state == "present":
|
||||||
module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{clientid}'")
|
module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{target_client_id}'")
|
||||||
|
|
||||||
if client_scope_id:
|
if role_owner_client_id:
|
||||||
objClientScope = kc.get_client_by_clientid(client_scope_id, realm)
|
role_owner_client = kc.get_client_by_client_id(role_owner_client_id, realm)
|
||||||
if not objClientScope:
|
if not role_owner_client:
|
||||||
module.fail_json(msg=f"Failed to retrive client '{realm}.{client_scope_id}'")
|
module.fail_json(msg=f"Failed to retrive client '{realm}.{role_owner_client_id}'")
|
||||||
before_role_mapping = kc.get_client_role_scope_from_client(objClient["id"], objClientScope["id"], realm)
|
before_role_mapping = kc.get_client_role_scope_from_client(objClient["id"], role_owner_client["id"], realm)
|
||||||
else:
|
else:
|
||||||
before_role_mapping = kc.get_client_role_scope_from_realm(objClient["id"], realm)
|
before_role_mapping = kc.get_client_role_scope_from_realm(objClient["id"], realm)
|
||||||
|
|
||||||
if client_scope_id:
|
if role_owner_client_id:
|
||||||
# retrive all role from client_scope
|
# retrive all role from client_scope
|
||||||
client_scope_roles_by_name = kc.get_client_roles_by_id(objClientScope["id"], realm)
|
client_scope_roles_by_name = kc.get_client_roles_by_id(role_owner_client["id"], realm)
|
||||||
else:
|
else:
|
||||||
# retrive all role from realm
|
# retrive all role from realm
|
||||||
client_scope_roles_by_name = kc.get_realm_roles(realm)
|
client_scope_roles_by_name = kc.get_realm_roles(realm)
|
||||||
@@ -228,8 +228,8 @@ def main():
|
|||||||
# update desired
|
# update desired
|
||||||
for role_name in role_names:
|
for role_name in role_names:
|
||||||
if role_name not in client_scope_roles_by_name:
|
if role_name not in client_scope_roles_by_name:
|
||||||
if client_scope_id:
|
if role_owner_client_id:
|
||||||
module.fail_json(msg=f"Failed to retrive role '{realm}.{client_scope_id}.{role_name}'")
|
module.fail_json(msg=f"Failed to retrive role '{realm}.{role_owner_client_id}.{role_name}'")
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg=f"Failed to retrive role '{realm}.{role_name}'")
|
module.fail_json(msg=f"Failed to retrive role '{realm}.{role_name}'")
|
||||||
if role_name not in role_mapping_by_name:
|
if role_name not in role_mapping_by_name:
|
||||||
@@ -253,33 +253,33 @@ def main():
|
|||||||
if not result["changed"]:
|
if not result["changed"]:
|
||||||
# no changes
|
# no changes
|
||||||
result["end_state"] = before_role_mapping
|
result["end_state"] = before_role_mapping
|
||||||
result["msg"] = f"No changes required for client role scope {clientid}."
|
result["msg"] = f"No changes required for client role scope {target_client_id}."
|
||||||
elif state == "present":
|
elif state == "present":
|
||||||
# doing update
|
# doing update
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
result["end_state"] = desired_role_mapping
|
result["end_state"] = desired_role_mapping
|
||||||
elif client_scope_id:
|
elif role_owner_client_id:
|
||||||
result["end_state"] = kc.update_client_role_scope_from_client(
|
result["end_state"] = kc.update_client_role_scope_from_client(
|
||||||
role_mapping_to_manipulate, objClient["id"], objClientScope["id"], realm
|
role_mapping_to_manipulate, objClient["id"], role_owner_client["id"], realm
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
result["end_state"] = kc.update_client_role_scope_from_realm(
|
result["end_state"] = kc.update_client_role_scope_from_realm(
|
||||||
role_mapping_to_manipulate, objClient["id"], realm
|
role_mapping_to_manipulate, objClient["id"], realm
|
||||||
)
|
)
|
||||||
result["msg"] = f"Client role scope for {clientid} has been updated"
|
result["msg"] = f"Client role scope for {target_client_id} has been updated"
|
||||||
else:
|
else:
|
||||||
# doing delete
|
# doing delete
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
result["end_state"] = desired_role_mapping
|
result["end_state"] = desired_role_mapping
|
||||||
elif client_scope_id:
|
elif role_owner_client_id:
|
||||||
result["end_state"] = kc.delete_client_role_scope_from_client(
|
result["end_state"] = kc.delete_client_role_scope_from_client(
|
||||||
role_mapping_to_manipulate, objClient["id"], objClientScope["id"], realm
|
role_mapping_to_manipulate, objClient["id"], role_owner_client["id"], realm
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
result["end_state"] = kc.delete_client_role_scope_from_realm(
|
result["end_state"] = kc.delete_client_role_scope_from_realm(
|
||||||
role_mapping_to_manipulate, objClient["id"], realm
|
role_mapping_to_manipulate, objClient["id"], realm
|
||||||
)
|
)
|
||||||
result["msg"] = f"Client role scope for {clientid} has been deleted"
|
result["msg"] = f"Client role scope for {target_client_id} has been deleted"
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ def main():
|
|||||||
attributes = module.params.get('attributes')
|
attributes = module.params.get('attributes')
|
||||||
protocol_mappers = module.params.get('protocol_mappers')
|
protocol_mappers = module.params.get('protocol_mappers')
|
||||||
|
|
||||||
before_scope = kc.get_clientscope_by_name(name, realm=realm)
|
before_scope = kc.get_client_scope_by_name(name, realm=realm)
|
||||||
|
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
if before_scope:
|
if before_scope:
|
||||||
@@ -239,7 +239,7 @@ def main():
|
|||||||
result['diff'] = dict(before=before_scope, after='')
|
result['diff'] = dict(before=before_scope, after='')
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
kc.delete_clientscope(cid=before_scope['id'], realm=realm)
|
kc.delete_client_scope(cid=before_scope['id'], realm=realm)
|
||||||
result['msg'] = "Client scope {name} has been deleted".format(name=name)
|
result['msg'] = "Client scope {name} has been deleted".format(name=name)
|
||||||
else:
|
else:
|
||||||
result['msg'] = "Client scope {name} does not exist, doing nothing".format(name=name)
|
result['msg'] = "Client scope {name} does not exist, doing nothing".format(name=name)
|
||||||
@@ -261,8 +261,8 @@ def main():
|
|||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
kc.create_clientscope(scope_rep, realm=realm)
|
kc.create_client_scope(scope_rep, realm=realm)
|
||||||
after_scope = kc.get_clientscope_by_name(name, realm=realm)
|
after_scope = kc.get_client_scope_by_name(name, realm=realm)
|
||||||
|
|
||||||
if protocol_mappers:
|
if protocol_mappers:
|
||||||
for mapper in protocol_mappers:
|
for mapper in protocol_mappers:
|
||||||
@@ -272,8 +272,8 @@ def main():
|
|||||||
'protocolMapper': mapper['protocolMapper'],
|
'protocolMapper': mapper['protocolMapper'],
|
||||||
'config': mapper['config'],
|
'config': mapper['config'],
|
||||||
}
|
}
|
||||||
kc.create_clientscope_protocolmapper(after_scope['id'], mapper_rep, realm=realm)
|
kc.create_client_scope_protocolmapper(after_scope['id'], mapper_rep, realm=realm)
|
||||||
after_scope = kc.get_clientscope_by_name(name, realm=realm)
|
after_scope = kc.get_client_scope_by_name(name, realm=realm)
|
||||||
|
|
||||||
result['end_state'] = after_scope
|
result['end_state'] = after_scope
|
||||||
result['msg'] = "Client scope {name} has been created".format(name=name)
|
result['msg'] = "Client scope {name} has been created".format(name=name)
|
||||||
@@ -296,10 +296,10 @@ def main():
|
|||||||
result['diff'] = dict(before=before_scope, after=scope_rep)
|
result['diff'] = dict(before=before_scope, after=scope_rep)
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
kc.update_clientscope(scope_rep, realm=realm)
|
kc.update_client_scope(scope_rep, realm=realm)
|
||||||
|
|
||||||
if protocol_mappers:
|
if protocol_mappers:
|
||||||
existing_mappers = kc.get_clientscope_protocolmappers(before_scope['id'], realm=realm)
|
existing_mappers = kc.get_client_scope_protocolmappers(before_scope['id'], realm=realm)
|
||||||
existing_mapper_names = {m['name'] for m in existing_mappers}
|
existing_mapper_names = {m['name'] for m in existing_mappers}
|
||||||
|
|
||||||
for mapper in protocol_mappers:
|
for mapper in protocol_mappers:
|
||||||
@@ -312,9 +312,9 @@ def main():
|
|||||||
'protocolMapper': mapper['protocolMapper'],
|
'protocolMapper': mapper['protocolMapper'],
|
||||||
'config': mapper['config'],
|
'config': mapper['config'],
|
||||||
}
|
}
|
||||||
kc.create_clientscope_protocolmapper(before_scope['id'], mapper_rep, realm=realm)
|
kc.create_client_scope_protocolmapper(before_scope['id'], mapper_rep, realm=realm)
|
||||||
|
|
||||||
after_scope = kc.get_clientscope_by_name(name, realm=realm)
|
after_scope = kc.get_client_scope_by_name(name, realm=realm)
|
||||||
result['end_state'] = after_scope
|
result['end_state'] = after_scope
|
||||||
|
|
||||||
if result['changed']:
|
if result['changed']:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
DOCUMENTATION = r"""
|
DOCUMENTATION = r"""
|
||||||
module: keycloak_clientscope_rolemappings
|
module: keycloak_client_scope_rolemappings
|
||||||
|
|
||||||
short_description: Allows administration of Keycloak client scope scope mappings to restrict the usage of certain roles to
|
short_description: Allows administration of Keycloak client scope scope mappings to restrict the usage of certain roles to
|
||||||
specific client scopes
|
specific client scopes
|
||||||
@@ -49,7 +49,7 @@ options:
|
|||||||
- The Keycloak realm under which clients resides.
|
- The Keycloak realm under which clients resides.
|
||||||
default: 'master'
|
default: 'master'
|
||||||
|
|
||||||
clientscope_id:
|
client_scope_id:
|
||||||
required: true
|
required: true
|
||||||
type: str
|
type: str
|
||||||
description:
|
description:
|
||||||
@@ -82,39 +82,39 @@ author:
|
|||||||
|
|
||||||
EXAMPLES = r"""
|
EXAMPLES = r"""
|
||||||
- name: Add roles to client scope
|
- name: Add roles to client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
auth_keycloak_url: https://auth.example.com
|
auth_keycloak_url: https://auth.example.com
|
||||||
auth_realm: master
|
auth_realm: master
|
||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
client_id: frontend-client-public
|
client_id: frontend-client-public
|
||||||
clientscope_id: frontend-clientscope
|
client_scope_id: frontend-client-scope
|
||||||
role_names:
|
role_names:
|
||||||
- backend-role-admin
|
- backend-role-admin
|
||||||
- backend-role-user
|
- backend-role-user
|
||||||
|
|
||||||
- name: Remove roles from client scope
|
- name: Remove roles from client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
auth_keycloak_url: https://auth.example.com
|
auth_keycloak_url: https://auth.example.com
|
||||||
auth_realm: master
|
auth_realm: master
|
||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
client_id: frontend-client-public
|
client_id: frontend-client-public
|
||||||
clientscope_id: frontend-clientscope
|
client_scope_id: frontend-client-scope
|
||||||
role_names:
|
role_names:
|
||||||
- backend-role-admin
|
- backend-role-admin
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Add realm roles to client scope
|
- name: Add realm roles to client scope
|
||||||
middleware_automation.keycloak.keycloak_clientscope_rolemappings:
|
middleware_automation.keycloak.keycloak_client_scope_rolemappings:
|
||||||
auth_keycloak_url: https://auth.example.com
|
auth_keycloak_url: https://auth.example.com
|
||||||
auth_realm: master
|
auth_realm: master
|
||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: MyCustomRealm
|
realm: MyCustomRealm
|
||||||
clientscope_id: frontend-clientscope
|
client_scope_id: frontend-client-scope
|
||||||
role_names:
|
role_names:
|
||||||
- realm-role-admin
|
- realm-role-admin
|
||||||
- realm-role-user
|
- realm-role-user
|
||||||
@@ -164,7 +164,7 @@ def main():
|
|||||||
|
|
||||||
meta_args = dict(
|
meta_args = dict(
|
||||||
client_id=dict(type="str"),
|
client_id=dict(type="str"),
|
||||||
clientscope_id=dict(type="str", required=True),
|
client_scope_id=dict(type="str", required=True),
|
||||||
realm=dict(type="str", default="master"),
|
realm=dict(type="str", default="master"),
|
||||||
role_names=dict(type="list", elements="str", required=True),
|
role_names=dict(type="list", elements="str", required=True),
|
||||||
state=dict(type="str", default="present", choices=["present", "absent"]),
|
state=dict(type="str", default="present", choices=["present", "absent"]),
|
||||||
@@ -186,7 +186,7 @@ def main():
|
|||||||
|
|
||||||
realm = module.params["realm"]
|
realm = module.params["realm"]
|
||||||
client_id = module.params["client_id"]
|
client_id = module.params["client_id"]
|
||||||
clientscope_id = module.params["clientscope_id"]
|
client_scope_id = module.params["client_scope_id"]
|
||||||
role_names = module.params["role_names"]
|
role_names = module.params["role_names"]
|
||||||
state = module.params["state"]
|
state = module.params["state"]
|
||||||
|
|
||||||
@@ -194,23 +194,23 @@ def main():
|
|||||||
if not realm_object:
|
if not realm_object:
|
||||||
module.fail_json(msg=f"Failed to retrieve realm '{realm}'")
|
module.fail_json(msg=f"Failed to retrieve realm '{realm}'")
|
||||||
|
|
||||||
clientscope_object = kc.get_clientscope_by_name(clientscope_id, realm)
|
client_scope_object = kc.get_client_scope_by_name(client_scope_id, realm)
|
||||||
if not clientscope_object:
|
if not client_scope_object:
|
||||||
module.fail_json(msg=f"Failed to retrieve client-scope '{clientscope_id}'")
|
module.fail_json(msg=f"Failed to retrieve client scope '{client_scope_id}'")
|
||||||
|
|
||||||
if client_id:
|
if client_id:
|
||||||
# add client role
|
# add client role
|
||||||
client_object = kc.get_client_by_clientid(client_id, realm)
|
client_object = kc.get_client_by_client_id(client_id, realm)
|
||||||
if not client_object:
|
if not client_object:
|
||||||
module.fail_json(msg=f"Failed to retrieve client '{realm}.{client_id}'")
|
module.fail_json(msg=f"Failed to retrieve client '{realm}.{client_id}'")
|
||||||
if client_object["fullScopeAllowed"] and state == "present":
|
if client_object["fullScopeAllowed"] and state == "present":
|
||||||
module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{client_id}'")
|
module.fail_json(msg=f"FullScopeAllowed is active for Client '{realm}.{client_id}'")
|
||||||
|
|
||||||
before_roles = kc.get_clientscope_scope_mappings_client(clientscope_object["id"], client_object["id"], realm)
|
before_roles = kc.get_client_scope_scope_mappings_client(client_scope_object["id"], client_object["id"], realm)
|
||||||
available_roles_by_name = kc.get_client_roles_by_id(client_object["id"], realm)
|
available_roles_by_name = kc.get_client_roles_by_id(client_object["id"], realm)
|
||||||
else:
|
else:
|
||||||
# add realm role
|
# add realm role
|
||||||
before_roles = kc.get_clientscope_scope_mappings_realm(clientscope_object["id"], realm)
|
before_roles = kc.get_client_scope_scope_mappings_realm(client_scope_object["id"], realm)
|
||||||
available_roles_by_name = kc.get_realm_roles(realm)
|
available_roles_by_name = kc.get_realm_roles(realm)
|
||||||
|
|
||||||
# convert to indexed Dict by name
|
# convert to indexed Dict by name
|
||||||
@@ -248,33 +248,33 @@ def main():
|
|||||||
if not result["changed"]:
|
if not result["changed"]:
|
||||||
# no changes
|
# no changes
|
||||||
result["end_state"] = before_roles
|
result["end_state"] = before_roles
|
||||||
result["msg"] = f"No changes required for clientscope {clientscope_id}."
|
result["msg"] = f"No changes required for client scope {client_scope_id}."
|
||||||
elif state == "present":
|
elif state == "present":
|
||||||
# doing update
|
# doing update
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
result["end_state"] = desired_role_mapping
|
result["end_state"] = desired_role_mapping
|
||||||
elif client_id:
|
elif client_id:
|
||||||
result["end_state"] = kc.update_clientscope_scope_mappings_client(
|
result["end_state"] = kc.update_client_scope_scope_mappings_client(
|
||||||
changed_roles, clientscope_object["id"], client_object["id"], realm
|
changed_roles, client_scope_object["id"], client_object["id"], realm
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
result["end_state"] = kc.update_clientscope_scope_mappings_realm(
|
result["end_state"] = kc.update_client_scope_scope_mappings_realm(
|
||||||
changed_roles, clientscope_object["id"], realm
|
changed_roles, client_scope_object["id"], realm
|
||||||
)
|
)
|
||||||
result["msg"] = f"Clientscope scope mappings for {clientscope_id} have been updated"
|
result["msg"] = f"Clientscope scope mappings for {client_scope_id} have been updated"
|
||||||
else:
|
else:
|
||||||
# doing delete
|
# doing delete
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
result["end_state"] = desired_role_mapping
|
result["end_state"] = desired_role_mapping
|
||||||
elif client_id:
|
elif client_id:
|
||||||
result["end_state"] = kc.delete_clientscope_scope_mappings_client(
|
result["end_state"] = kc.delete_client_scope_scope_mappings_client(
|
||||||
changed_roles, clientscope_object["id"], client_object["id"], realm
|
changed_roles, client_scope_object["id"], client_object["id"], realm
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
result["end_state"] = kc.delete_clientscope_scope_mappings_realm(
|
result["end_state"] = kc.delete_client_scope_scope_mappings_realm(
|
||||||
changed_roles, clientscope_object["id"], realm
|
changed_roles, client_scope_object["id"], realm
|
||||||
)
|
)
|
||||||
result["msg"] = f"Clientscope scope mappings for {clientscope_id} have been deleted"
|
result["msg"] = f"Clientscope scope mappings for {client_scope_id} have been deleted"
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
DOCUMENTATION = r"""
|
DOCUMENTATION = r"""
|
||||||
module: keycloak_clientscope_type
|
module: keycloak_client_scope_type
|
||||||
|
|
||||||
short_description: Set the type of aclientscope in realm or client using Keycloak API
|
short_description: Set the type of a client scope in a realm or client using the Keycloak API
|
||||||
|
|
||||||
# Originally added in community.general 6.6.0
|
# Originally added in community.general 6.6.0
|
||||||
version_added: "3.0.0"
|
version_added: "3.0.0"
|
||||||
@@ -41,13 +41,13 @@ options:
|
|||||||
- clientId
|
- clientId
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
default_clientscopes:
|
default_client_scopes:
|
||||||
description:
|
description:
|
||||||
- Client scopes that should be of type default.
|
- Client scopes that should be of type default.
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
|
|
||||||
optional_clientscopes:
|
optional_client_scopes:
|
||||||
description:
|
description:
|
||||||
- Client scopes that should be of type optional.
|
- Client scopes that should be of type optional.
|
||||||
type: list
|
type: list
|
||||||
@@ -64,26 +64,26 @@ author:
|
|||||||
|
|
||||||
EXAMPLES = r"""
|
EXAMPLES = r"""
|
||||||
- name: Set default client scopes on realm level
|
- name: Set default client scopes on realm level
|
||||||
middleware_automation.keycloak.keycloak_clientscope_type:
|
middleware_automation.keycloak.keycloak_client_scope_type:
|
||||||
auth_client_id: admin-cli
|
auth_client_id: admin-cli
|
||||||
auth_keycloak_url: https://auth.example.com
|
auth_keycloak_url: https://auth.example.com
|
||||||
auth_realm: master
|
auth_realm: master
|
||||||
auth_username: USERNAME
|
auth_username: USERNAME
|
||||||
auth_password: PASSWORD
|
auth_password: PASSWORD
|
||||||
realm: "MyCustomRealm"
|
realm: "MyCustomRealm"
|
||||||
default_clientscopes: ['profile', 'roles']
|
default_client_scopes: ['profile', 'roles']
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
|
|
||||||
- name: Set default and optional client scopes on client level with token auth
|
- name: Set default and optional client scopes on client level with token auth
|
||||||
middleware_automation.keycloak.keycloak_clientscope_type:
|
middleware_automation.keycloak.keycloak_client_scope_type:
|
||||||
auth_client_id: admin-cli
|
auth_client_id: admin-cli
|
||||||
auth_keycloak_url: https://auth.example.com
|
auth_keycloak_url: https://auth.example.com
|
||||||
token: TOKEN
|
token: TOKEN
|
||||||
realm: "MyCustomRealm"
|
realm: "MyCustomRealm"
|
||||||
client_id: "MyCustomClient"
|
client_id: "MyCustomClient"
|
||||||
default_clientscopes: ['profile', 'roles']
|
default_client_scopes: ['profile', 'roles']
|
||||||
optional_clientscopes: ['phone']
|
optional_client_scopes: ['phone']
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -94,16 +94,16 @@ msg:
|
|||||||
type: str
|
type: str
|
||||||
sample: ""
|
sample: ""
|
||||||
proposed:
|
proposed:
|
||||||
description: Representation of proposed client-scope types mapping.
|
description: Representation of proposed client scope types mapping.
|
||||||
returned: always
|
returned: always
|
||||||
type: dict
|
type: dict
|
||||||
sample:
|
sample:
|
||||||
{
|
{
|
||||||
"default_clientscopes": [
|
"default_client_scopes": [
|
||||||
"profile",
|
"profile",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
"optional_clientscopes": []
|
"optional_client_scopes": []
|
||||||
}
|
}
|
||||||
existing:
|
existing:
|
||||||
description:
|
description:
|
||||||
@@ -112,11 +112,11 @@ existing:
|
|||||||
type: dict
|
type: dict
|
||||||
sample:
|
sample:
|
||||||
{
|
{
|
||||||
"default_clientscopes": [
|
"default_client_scopes": [
|
||||||
"profile",
|
"profile",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
"optional_clientscopes": [
|
"optional_client_scopes": [
|
||||||
"phone"
|
"phone"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -128,11 +128,11 @@ end_state:
|
|||||||
type: dict
|
type: dict
|
||||||
sample:
|
sample:
|
||||||
{
|
{
|
||||||
"default_clientscopes": [
|
"default_client_scopes": [
|
||||||
"profile",
|
"profile",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
"optional_clientscopes": []
|
"optional_client_scopes": []
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ from ansible_collections.middleware_automation.keycloak.plugins.module_utils.ide
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def keycloak_clientscope_type_module():
|
def keycloak_client_scope_type_module():
|
||||||
"""
|
"""
|
||||||
Returns an AnsibleModule definition.
|
Returns an AnsibleModule definition.
|
||||||
|
|
||||||
@@ -157,8 +157,8 @@ def keycloak_clientscope_type_module():
|
|||||||
meta_args = dict(
|
meta_args = dict(
|
||||||
realm=dict(default="master"),
|
realm=dict(default="master"),
|
||||||
client_id=dict(type="str", aliases=["clientId"]),
|
client_id=dict(type="str", aliases=["clientId"]),
|
||||||
default_clientscopes=dict(type="list", elements="str"),
|
default_client_scopes=dict(type="list", elements="str"),
|
||||||
optional_clientscopes=dict(type="list", elements="str"),
|
optional_client_scopes=dict(type="list", elements="str"),
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(meta_args)
|
argument_spec.update(meta_args)
|
||||||
@@ -169,7 +169,7 @@ def keycloak_clientscope_type_module():
|
|||||||
required_one_of=(
|
required_one_of=(
|
||||||
[
|
[
|
||||||
["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"],
|
["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"],
|
||||||
["default_clientscopes", "optional_clientscopes"],
|
["default_client_scopes", "optional_client_scopes"],
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
required_together=([["auth_username", "auth_password"]]),
|
required_together=([["auth_username", "auth_password"]]),
|
||||||
@@ -180,21 +180,21 @@ def keycloak_clientscope_type_module():
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
def clientscopes_to_add(existing, proposed):
|
def client_scopes_to_add(existing, proposed):
|
||||||
to_add = []
|
to_add = []
|
||||||
existing_clientscope_ids = extract_field(existing, "id")
|
existing_client_scope_ids = extract_field(existing, "id")
|
||||||
for clientscope in proposed:
|
for client_scope in proposed:
|
||||||
if clientscope["id"] not in existing_clientscope_ids:
|
if client_scope["id"] not in existing_client_scope_ids:
|
||||||
to_add.append(clientscope)
|
to_add.append(client_scope)
|
||||||
return to_add
|
return to_add
|
||||||
|
|
||||||
|
|
||||||
def clientscopes_to_delete(existing, proposed):
|
def client_scopes_to_delete(existing, proposed):
|
||||||
to_delete = []
|
to_delete = []
|
||||||
proposed_clientscope_ids = extract_field(proposed, "id")
|
proposed_client_scope_ids = extract_field(proposed, "id")
|
||||||
for clientscope in existing:
|
for client_scope in existing:
|
||||||
if clientscope["id"] not in proposed_clientscope_ids:
|
if client_scope["id"] not in proposed_client_scope_ids:
|
||||||
to_delete.append(clientscope)
|
to_delete.append(client_scope)
|
||||||
return to_delete
|
return to_delete
|
||||||
|
|
||||||
|
|
||||||
@@ -204,21 +204,21 @@ def extract_field(dictionary, field="name"):
|
|||||||
|
|
||||||
def normalize_scopes(scopes):
|
def normalize_scopes(scopes):
|
||||||
scopes_copy = scopes.copy()
|
scopes_copy = scopes.copy()
|
||||||
if isinstance(scopes_copy.get("default_clientscopes"), list):
|
if isinstance(scopes_copy.get("default_client_scopes"), list):
|
||||||
scopes_copy["default_clientscopes"] = sorted(scopes_copy["default_clientscopes"])
|
scopes_copy["default_client_scopes"] = sorted(scopes_copy["default_client_scopes"])
|
||||||
if isinstance(scopes_copy.get("optional_clientscopes"), list):
|
if isinstance(scopes_copy.get("optional_client_scopes"), list):
|
||||||
scopes_copy["optional_clientscopes"] = sorted(scopes_copy["optional_clientscopes"])
|
scopes_copy["optional_client_scopes"] = sorted(scopes_copy["optional_client_scopes"])
|
||||||
return scopes_copy
|
return scopes_copy
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
Module keycloak_clientscope_type
|
Module keycloak_client_scope_type
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
module = keycloak_clientscope_type_module()
|
module = keycloak_client_scope_type_module()
|
||||||
|
|
||||||
# Obtain access token, initialize API
|
# Obtain access token, initialize API
|
||||||
try:
|
try:
|
||||||
@@ -230,81 +230,81 @@ def main():
|
|||||||
|
|
||||||
realm = module.params.get("realm")
|
realm = module.params.get("realm")
|
||||||
client_id = module.params.get("client_id")
|
client_id = module.params.get("client_id")
|
||||||
default_clientscopes = module.params.get("default_clientscopes")
|
default_client_scopes = module.params.get("default_client_scopes")
|
||||||
optional_clientscopes = module.params.get("optional_clientscopes")
|
optional_client_scopes = module.params.get("optional_client_scopes")
|
||||||
|
|
||||||
result = dict(changed=False, msg="", proposed={}, existing={}, end_state={})
|
result = dict(changed=False, msg="", proposed={}, existing={}, end_state={})
|
||||||
|
|
||||||
all_clientscopes = kc.get_clientscopes(realm)
|
all_client_scopes = kc.get_client_scopes(realm)
|
||||||
default_clientscopes_real = []
|
default_client_scopes_real = []
|
||||||
optional_clientscopes_real = []
|
optional_client_scopes_real = []
|
||||||
|
|
||||||
for client_scope in all_clientscopes:
|
for client_scope in all_client_scopes:
|
||||||
if default_clientscopes is not None and client_scope["name"] in default_clientscopes:
|
if default_client_scopes is not None and client_scope["name"] in default_client_scopes:
|
||||||
default_clientscopes_real.append(client_scope)
|
default_client_scopes_real.append(client_scope)
|
||||||
if optional_clientscopes is not None and client_scope["name"] in optional_clientscopes:
|
if optional_client_scopes is not None and client_scope["name"] in optional_client_scopes:
|
||||||
optional_clientscopes_real.append(client_scope)
|
optional_client_scopes_real.append(client_scope)
|
||||||
|
|
||||||
if default_clientscopes is not None and len(default_clientscopes_real) != len(default_clientscopes):
|
if default_client_scopes is not None and len(default_client_scopes_real) != len(default_client_scopes):
|
||||||
module.fail_json(msg="At least one of the default_clientscopes does not exist!")
|
module.fail_json(msg="At least one of the default_client_scopes does not exist!")
|
||||||
|
|
||||||
if optional_clientscopes is not None and len(optional_clientscopes_real) != len(optional_clientscopes):
|
if optional_client_scopes is not None and len(optional_client_scopes_real) != len(optional_client_scopes):
|
||||||
module.fail_json(msg="At least one of the optional_clientscopes does not exist!")
|
module.fail_json(msg="At least one of the optional_client_scopes does not exist!")
|
||||||
|
|
||||||
result["proposed"].update(
|
result["proposed"].update(
|
||||||
{
|
{
|
||||||
"default_clientscopes": "no-change" if default_clientscopes is None else default_clientscopes,
|
"default_client_scopes": "no-change" if default_client_scopes is None else default_client_scopes,
|
||||||
"optional_clientscopes": "no-change" if optional_clientscopes is None else optional_clientscopes,
|
"optional_client_scopes": "no-change" if optional_client_scopes is None else optional_client_scopes,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
default_clientscopes_existing = kc.get_default_clientscopes(realm, client_id)
|
default_client_scopes_existing = kc.get_default_client_scopes(realm, client_id)
|
||||||
optional_clientscopes_existing = kc.get_optional_clientscopes(realm, client_id)
|
optional_client_scopes_existing = kc.get_optional_client_scopes(realm, client_id)
|
||||||
|
|
||||||
result["existing"].update(
|
result["existing"].update(
|
||||||
{
|
{
|
||||||
"default_clientscopes": extract_field(default_clientscopes_existing),
|
"default_client_scopes": extract_field(default_client_scopes_existing),
|
||||||
"optional_clientscopes": extract_field(optional_clientscopes_existing),
|
"optional_client_scopes": extract_field(optional_client_scopes_existing),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if module._diff:
|
if module._diff:
|
||||||
result["diff"] = dict(before=normalize_scopes(result["existing"]), after=normalize_scopes(result["proposed"]))
|
result["diff"] = dict(before=normalize_scopes(result["existing"]), after=normalize_scopes(result["proposed"]))
|
||||||
|
|
||||||
default_clientscopes_add = clientscopes_to_add(default_clientscopes_existing, default_clientscopes_real)
|
default_client_scopes_add = client_scopes_to_add(default_client_scopes_existing, default_client_scopes_real)
|
||||||
optional_clientscopes_add = clientscopes_to_add(optional_clientscopes_existing, optional_clientscopes_real)
|
optional_client_scopes_add = client_scopes_to_add(optional_client_scopes_existing, optional_client_scopes_real)
|
||||||
|
|
||||||
default_clientscopes_delete = clientscopes_to_delete(default_clientscopes_existing, default_clientscopes_real)
|
default_client_scopes_delete = client_scopes_to_delete(default_client_scopes_existing, default_client_scopes_real)
|
||||||
optional_clientscopes_delete = clientscopes_to_delete(optional_clientscopes_existing, optional_clientscopes_real)
|
optional_client_scopes_delete = client_scopes_to_delete(optional_client_scopes_existing, optional_client_scopes_real)
|
||||||
|
|
||||||
result["changed"] = any(
|
result["changed"] = any(
|
||||||
len(x) > 0
|
len(x) > 0
|
||||||
for x in [
|
for x in [
|
||||||
default_clientscopes_add,
|
default_client_scopes_add,
|
||||||
optional_clientscopes_add,
|
optional_client_scopes_add,
|
||||||
default_clientscopes_delete,
|
default_client_scopes_delete,
|
||||||
optional_clientscopes_delete,
|
optional_client_scopes_delete,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
# first delete so clientscopes can change type
|
# first delete so client_scopes can change type
|
||||||
for clientscope in default_clientscopes_delete:
|
for client_scope in default_client_scopes_delete:
|
||||||
kc.delete_default_clientscope(clientscope["id"], realm, client_id)
|
kc.delete_default_client_scope(client_scope["id"], realm, client_id)
|
||||||
for clientscope in optional_clientscopes_delete:
|
for client_scope in optional_client_scopes_delete:
|
||||||
kc.delete_optional_clientscope(clientscope["id"], realm, client_id)
|
kc.delete_optional_client_scope(client_scope["id"], realm, client_id)
|
||||||
|
|
||||||
for clientscope in default_clientscopes_add:
|
for client_scope in default_client_scopes_add:
|
||||||
kc.add_default_clientscope(clientscope["id"], realm, client_id)
|
kc.add_default_client_scope(client_scope["id"], realm, client_id)
|
||||||
for clientscope in optional_clientscopes_add:
|
for client_scope in optional_client_scopes_add:
|
||||||
kc.add_optional_clientscope(clientscope["id"], realm, client_id)
|
kc.add_optional_client_scope(client_scope["id"], realm, client_id)
|
||||||
|
|
||||||
result["end_state"].update(
|
result["end_state"].update(
|
||||||
{
|
{
|
||||||
"default_clientscopes": extract_field(kc.get_default_clientscopes(realm, client_id)),
|
"default_client_scopes": extract_field(kc.get_default_client_scopes(realm, client_id)),
|
||||||
"optional_clientscopes": extract_field(kc.get_optional_clientscopes(realm, client_id)),
|
"optional_client_scopes": extract_field(kc.get_optional_client_scopes(realm, client_id)),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2,4 +2,14 @@
|
|||||||
collections:
|
collections:
|
||||||
- name: middleware_automation.common
|
- name: middleware_automation.common
|
||||||
version: ">=1.2.4"
|
version: ">=1.2.4"
|
||||||
|
- name: middleware_automation.infinispan
|
||||||
|
- name: community.general
|
||||||
- name: ansible.posix
|
- name: ansible.posix
|
||||||
|
- name: community.docker
|
||||||
|
version: ">=3.8.0"
|
||||||
|
- name: containers.podman
|
||||||
|
version: ">=1.8.1"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- name: elan.simple_nginx_reverse_proxy
|
||||||
|
version: "0.2.1"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Role Defaults
|
|||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|:---------|:------------|:--------|
|
|:---------|:------------|:--------|
|
||||||
|`keycloak_quarkus_version`| keycloak.org package version | `26.4.7` |
|
|`keycloak_quarkus_version`| keycloak.org package version | `26.6.2` |
|
||||||
|`keycloak_quarkus_offline_install` | Perform an offline install | `False`|
|
|`keycloak_quarkus_offline_install` | Perform an offline install | `False`|
|
||||||
|`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` |
|
|`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` |
|
||||||
|`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` |
|
|`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
### Configuration specific to keycloak
|
### Configuration specific to keycloak
|
||||||
keycloak_quarkus_version: 26.4.7
|
keycloak_quarkus_version: 26.6.2
|
||||||
keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip"
|
keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip"
|
||||||
keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}"
|
keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}"
|
||||||
keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}"
|
keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ argument_specs:
|
|||||||
main:
|
main:
|
||||||
options:
|
options:
|
||||||
keycloak_quarkus_version:
|
keycloak_quarkus_version:
|
||||||
default: "26.4.7"
|
default: "26.6.2"
|
||||||
description: "keycloak.org package version"
|
description: "keycloak.org package version"
|
||||||
type: "str"
|
type: "str"
|
||||||
keycloak_quarkus_archive:
|
keycloak_quarkus_archive:
|
||||||
@@ -519,7 +519,7 @@ argument_specs:
|
|||||||
downstream:
|
downstream:
|
||||||
options:
|
options:
|
||||||
rhbk_version:
|
rhbk_version:
|
||||||
default: "26.4.7"
|
default: "26.4.11"
|
||||||
description: "Red Hat Build of Keycloak version"
|
description: "Red Hat Build of Keycloak version"
|
||||||
type: "str"
|
type: "str"
|
||||||
rhbk_archive:
|
rhbk_archive:
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
- name: Validate admin console password
|
- name: Validate admin console password
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
- keycloak_quarkus_bootstrap_admin_password is defined
|
||||||
|
- keycloak_quarkus_bootstrap_admin_password is not none
|
||||||
- keycloak_quarkus_bootstrap_admin_password | length > 12
|
- keycloak_quarkus_bootstrap_admin_password | length > 12
|
||||||
quiet: true
|
quiet: true
|
||||||
fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_bootstrap_admin_password to a 12+ char long string"
|
fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_bootstrap_admin_password to a 12+ char long string"
|
||||||
|
|||||||
15
roles/keycloak_quarkus/templates/rhbk-sysconfig.j2
Normal file
15
roles/keycloak_quarkus/templates/rhbk-sysconfig.j2
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{{ ansible_managed | comment }}
|
||||||
|
{% if not ansible_local.keycloak.general.bootstrapped | default(false) | bool %}
|
||||||
|
KC_BOOTSTRAP_ADMIN_USERNAME={{ keycloak_quarkus_bootstrap_admin_user }}
|
||||||
|
KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}'
|
||||||
|
{% else %}
|
||||||
|
{{ keycloak.bootstrap_mnemonic }}
|
||||||
|
{% endif %}
|
||||||
|
PATH="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
JAVA_HOME="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}"
|
||||||
|
JAVA_OPTS="{{ keycloak_quarkus_java_opts }}"
|
||||||
|
|
||||||
|
# Custom ENV variables
|
||||||
|
{% for env in keycloak_quarkus_additional_env_vars %}
|
||||||
|
{{ env.key }}={{ env.value }}
|
||||||
|
{% endfor %}
|
||||||
110
roles/keycloak_quarkus/templates/rhbk.conf.j2
Normal file
110
roles/keycloak_quarkus/templates/rhbk.conf.j2
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
{% if keycloak_quarkus_db_enabled %}
|
||||||
|
# Database
|
||||||
|
db={{ keycloak_quarkus_db_engine }}
|
||||||
|
db-url={{ keycloak_quarkus_db_url }}
|
||||||
|
db-username={{ keycloak_quarkus_db_user }}
|
||||||
|
{% if not keycloak.config_key_store_enabled %}
|
||||||
|
db-password={{ keycloak_quarkus_db_pass }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if keycloak.config_key_store_enabled %}
|
||||||
|
# Config store
|
||||||
|
config-keystore={{ keycloak_quarkus_config_key_store_file }}
|
||||||
|
config-keystore-password={{ keycloak_quarkus_config_key_store_password }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Observability
|
||||||
|
metrics-enabled={{ keycloak_quarkus_metrics_enabled | lower }}
|
||||||
|
health-enabled={{ keycloak_quarkus_health_enabled | lower }}
|
||||||
|
|
||||||
|
# HTTP
|
||||||
|
http-enabled={{ keycloak_quarkus_http_enabled | lower }}
|
||||||
|
{% if keycloak_quarkus_http_enabled %}
|
||||||
|
http-port={{ keycloak_quarkus_http_port }}
|
||||||
|
{% endif %}
|
||||||
|
http-relative-path={{ keycloak_quarkus_http_relative_path }}
|
||||||
|
http-host={{ keycloak_quarkus_http_host }}
|
||||||
|
|
||||||
|
# Management
|
||||||
|
http-management-port={{ keycloak_quarkus_http_management_port }}
|
||||||
|
{% if keycloak_quarkus_http_management_relative_path is defined and keycloak_quarkus_http_management_relative_path | length > 0 %}
|
||||||
|
http-management-relative-path={{ keycloak_quarkus_http_management_relative_path }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# HTTPS
|
||||||
|
https-port={{ keycloak_quarkus_https_port }}
|
||||||
|
{% if keycloak_quarkus_https_key_file_enabled %}
|
||||||
|
https-certificate-file={{ keycloak_quarkus_cert_file}}
|
||||||
|
https-certificate-key-file={{ keycloak_quarkus_key_file }}
|
||||||
|
{% endif %}
|
||||||
|
{% if keycloak_quarkus_https_key_store_enabled %}
|
||||||
|
https-key-store-file={{ keycloak_quarkus_https_key_store_file }}
|
||||||
|
https-key-store-password={{ keycloak_quarkus_https_key_store_password }}
|
||||||
|
{% endif %}
|
||||||
|
{% if keycloak_quarkus_https_trust_store_enabled %}
|
||||||
|
https-trust-store-file={{ keycloak_quarkus_https_trust_store_file }}
|
||||||
|
https-trust-store-password={{ keycloak_quarkus_https_trust_store_password }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Client URL configuration
|
||||||
|
hostname={{ keycloak_quarkus_hostname }}
|
||||||
|
hostname-admin={{ keycloak_quarkus_hostname_admin }}
|
||||||
|
hostname-strict={{ keycloak_quarkus_hostname_strict | lower }}
|
||||||
|
hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic | lower }}
|
||||||
|
|
||||||
|
# Cluster
|
||||||
|
{% if keycloak_quarkus_ha_enabled %}
|
||||||
|
cache=ispn
|
||||||
|
{% if keycloak_quarkus_cache_managed_infinispan_config %}
|
||||||
|
cache-config-file=cache-ispn.xml
|
||||||
|
{% endif %}
|
||||||
|
{% if keycloak_quarkus_cache_remote %}
|
||||||
|
cache-remote-username={{ keycloak_quarkus_cache_remote_username }}
|
||||||
|
cache-remote-password={{ keycloak_quarkus_cache_remote_password }}
|
||||||
|
cache-remote-host={{ keycloak_quarkus_cache_remote_host }}
|
||||||
|
cache-remote-port={{ keycloak_quarkus_cache_remote_port }}
|
||||||
|
cache-remote-tls-enabled={{ keycloak_quarkus_cache_remote_tls_enabled | lower }}
|
||||||
|
{% endif %}
|
||||||
|
{{ keycloak_quarkus_cache_embedded_properties }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if keycloak_quarkus_proxy_headers | length > 0 %}
|
||||||
|
proxy-headers={{ keycloak_quarkus_proxy_headers | lower }}
|
||||||
|
{% elif keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %}
|
||||||
|
# Deprecated Proxy configuration
|
||||||
|
proxy={{ keycloak_quarkus_proxy_mode }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
spi-sticky-session-encoder-infinispan-should-attach-route={{ keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route | d(true) | lower }}
|
||||||
|
|
||||||
|
# Transaction
|
||||||
|
transaction-xa-enabled={{ keycloak_quarkus_transaction_xa_enabled | lower }}
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
#log-format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n
|
||||||
|
log={{ keycloak_quarkus_log }}
|
||||||
|
log-level={{ keycloak.log.level }}
|
||||||
|
log-file={{ keycloak.log.file }}
|
||||||
|
log-file-format={{ keycloak.log.format }}
|
||||||
|
|
||||||
|
# Vault
|
||||||
|
{% if keycloak_quarkus_ks_vault_enabled %}
|
||||||
|
vault=keystore
|
||||||
|
vault-file={{ keycloak_quarkus_ks_vault_file }}
|
||||||
|
vault-type={{ keycloak_quarkus_ks_vault_type }}
|
||||||
|
vault-pass={{ keycloak_quarkus_ks_vault_pass }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
# Providers
|
||||||
|
{% for provider in keycloak_quarkus_providers %}
|
||||||
|
{% if provider.default is defined and provider.default %}
|
||||||
|
spi-{{ provider.spi }}-provider={{ provider.id }}
|
||||||
|
{% endif %}
|
||||||
|
{% if provider.properties is defined %}{% for property in provider.properties %}
|
||||||
|
spi-{{ provider.spi }}-{{ provider.id }}-{{ property.key }}={{ property.value }}
|
||||||
|
{% endfor %}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
2
roles/keycloak_quarkus/templates/rhbk.fact.j2
Normal file
2
roles/keycloak_quarkus/templates/rhbk.fact.j2
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[general]
|
||||||
|
bootstrapped={{ bootstrapped | lower }}
|
||||||
33
roles/keycloak_quarkus/templates/rhbk.service.j2
Normal file
33
roles/keycloak_quarkus/templates/rhbk.service.j2
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{{ ansible_managed | comment }}
|
||||||
|
[Unit]
|
||||||
|
Description=Keycloak Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=-{{ keycloak_quarkus_sysconf_file }}
|
||||||
|
{% if keycloak_quarkus_start_dev %}
|
||||||
|
ExecStart={{ keycloak.home }}/bin/kc.sh start-dev
|
||||||
|
{% else %}
|
||||||
|
ExecStart={{ keycloak.home }}/bin/kc.sh start --optimized
|
||||||
|
{% endif %}
|
||||||
|
User={{ keycloak.service_user }}
|
||||||
|
Group={{ keycloak.service_group }}
|
||||||
|
SuccessExitStatus=0 143
|
||||||
|
{% if keycloak_quarkus_service_restart_always %}
|
||||||
|
Restart=always
|
||||||
|
{% elif keycloak_quarkus_service_restart_on_failure %}
|
||||||
|
Restart=on-failure
|
||||||
|
{% endif %}
|
||||||
|
RestartSec={{ keycloak_quarkus_service_restartsec }}
|
||||||
|
{% if keycloak_quarkus_http_port | int < 1024 or keycloak_quarkus_https_port | int < 1024 %}
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
{% endif %}
|
||||||
|
{% if keycloak_quarkus_systemd_wait_for_port %}
|
||||||
|
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'while ! ss -H -t -l -n sport = :{{ keycloak_quarkus_systemd_wait_for_port_number }} | grep -q "^LISTEN.*:{{ keycloak_quarkus_systemd_wait_for_port_number }}"; do sleep 1; done && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
|
||||||
|
{% endif %}
|
||||||
|
{% if keycloak_quarkus_systemd_wait_for_log %}
|
||||||
|
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'cat {{ keycloak.log.file }} | sed "/Profile.*activated/ q" && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user