mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-30 07:18:09 +02:00
keycloak(role): add realm support to generic updater
- Allow kc_object_kind='realm' - Map endpoint to 'realms' and default lookup_field to 'id' - Use realm-specific kcadm GET/UPDATE (no -r flag) - Preserve immutables: id, realm - Guard query-based ID resolution to non-realm objects Context: fixing failure in 'Update REALM mail settings' task. See: https://chatgpt.com/share/68affdb8-3d28-800f-8480-aa6a74000bf8
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
# Generic updater for Keycloak client/component via kcadm.
|
||||
# Generic updater for Keycloak client/component/realm via kcadm.
|
||||
# Flow: resolve ID → read current object → merge with desired → preserve immutable fields → update via stdin.
|
||||
#
|
||||
# Required vars (pass via include):
|
||||
# - kc_object_kind: "client" | "component"
|
||||
# - kc_lookup_value: e.g., KEYCLOAK_CLIENT_ID or KEYCLOAK_LDAP_CMP_NAME
|
||||
# - kc_object_kind: "client" | "component" | "client-scope" | "realm"
|
||||
# - kc_lookup_value: e.g., KEYCLOAK_CLIENT_ID or KEYCLOAK_LDAP_CMP_NAME or KEYCLOAK_REALM
|
||||
# - kc_desired: dict, e.g., KEYCLOAK_DICTIONARY_CLIENT or KEYCLOAK_DICTIONARY_LDAP
|
||||
#
|
||||
# Optional:
|
||||
# - kc_lookup_field: override lookup field (defaults: clientId for client, name for component)
|
||||
# - kc_lookup_field: override lookup field (defaults: clientId for client, name for component, id for realm)
|
||||
# - kc_merge_path: if set (e.g. "config"), only that subkey is merged
|
||||
# - kc_force_attrs: dict to force on the final payload (merged last)
|
||||
|
||||
- name: Assert required vars
|
||||
assert:
|
||||
that:
|
||||
- kc_object_kind in ['client','component','client-scope']
|
||||
- kc_object_kind in ['client','component','client-scope','realm']
|
||||
- kc_lookup_value is defined
|
||||
- kc_desired is defined
|
||||
fail_msg: "kc_object_kind, kc_lookup_value, kc_desired are required."
|
||||
@@ -26,11 +26,13 @@
|
||||
{{ 'clients' if kc_object_kind == 'client'
|
||||
else 'components' if kc_object_kind == 'component'
|
||||
else 'client-scopes' if kc_object_kind == 'client-scope'
|
||||
else 'realms' if kc_object_kind == 'realm'
|
||||
else '' }}
|
||||
kc_lookup_field_eff: >-
|
||||
{{ 'clientId' if kc_object_kind == 'client'
|
||||
else (kc_lookup_field | default('name')) if kc_object_kind == 'component'
|
||||
else 'name' if kc_object_kind == 'client-scope'
|
||||
else 'id' if kc_object_kind == 'realm'
|
||||
else '' }}
|
||||
|
||||
- name: Resolve object id (direct when lookup_field is id)
|
||||
@@ -39,7 +41,7 @@
|
||||
kc_obj_id: "{{ kc_lookup_value | string }}"
|
||||
|
||||
- name: Resolve object id via query
|
||||
when: kc_lookup_field_eff != 'id'
|
||||
when: kc_lookup_field_eff != 'id' and kc_object_kind != 'realm'
|
||||
shell: >
|
||||
{% if kc_object_kind == 'client-scope' -%}
|
||||
{{ KEYCLOAK_EXEC_KCADM }} get client-scopes -r {{ KEYCLOAK_REALM }} --format json
|
||||
@@ -72,8 +74,11 @@
|
||||
|
||||
- name: Read current object
|
||||
shell: >
|
||||
{{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }}
|
||||
-r {{ KEYCLOAK_REALM }} --format json
|
||||
{% if kc_object_kind == 'realm' -%}
|
||||
{{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }} --format json
|
||||
{%- else -%}
|
||||
{{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }} -r {{ KEYCLOAK_REALM }} --format json
|
||||
{%- endif %}
|
||||
register: kc_cur
|
||||
changed_when: false
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
@@ -141,12 +146,23 @@
|
||||
}}
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
# Preserve immutables for client-scope
|
||||
- name: Preserve immutable fields for client-scope
|
||||
when: kc_object_kind == 'client-scope'
|
||||
set_fact:
|
||||
desired_obj: "{{ desired_obj | combine({'id': cur_obj.id, 'name': cur_obj.name}, recursive=True) }}"
|
||||
|
||||
- name: Preserve immutable fields for realm
|
||||
when: kc_object_kind == 'realm'
|
||||
set_fact:
|
||||
desired_obj: >-
|
||||
{{
|
||||
desired_obj
|
||||
| combine({
|
||||
'id': cur_obj.id,
|
||||
'realm': cur_obj.realm
|
||||
}, recursive=True)
|
||||
}}
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
# Optional forced attributes (e.g., frontchannelLogout)
|
||||
- name: Apply forced attributes (optional)
|
||||
@@ -156,8 +172,14 @@
|
||||
|
||||
- name: Update object via stdin
|
||||
shell: |
|
||||
{% if kc_object_kind == 'realm' -%}
|
||||
cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} update {{ kc_api }}/{{ kc_obj_id }} -f -
|
||||
{{ desired_obj | to_json }}
|
||||
JSON
|
||||
{%- else -%}
|
||||
cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} update {{ kc_api }}/{{ kc_obj_id }} -r {{ KEYCLOAK_REALM }} -f -
|
||||
{{ desired_obj | to_json }}
|
||||
JSON
|
||||
{%- endif %}
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
|
Reference in New Issue
Block a user