--- # # Read-only assertions for LDAP configuration in Keycloak. # Ensures: # - LDAP UserStorageProvider exists # - ldap-roles mapper exists exactly once # - groups.path is consistent # - top-level group for groups.path exists # # This file must NEVER modify state. - name: ✅ Assert | Initialize LDAP assert variables set_fact: ldap_cmp_id_assert: "" desired_group_mapper_raw_assert: {} desired_groups_path_assert: "" desired_groups_top_assert: "" grp_mapper_ids_assert: [] changed_when: false - name: ✅ Assert | Fetch components (JSON) shell: > {{ KEYCLOAK_EXEC_KCADM }} get components -r {{ KEYCLOAK_REALM }} --format json register: kc_components_assert changed_when: false - name: ✅ Assert | Resolve LDAP component id (client-side filter) set_fact: ldap_cmp_id_assert: >- {{ ( kc_components_assert.stdout | kcadm_json | selectattr('providerType','equalto','org.keycloak.storage.UserStorageProvider') | selectattr('providerId','equalto','ldap') | selectattr('name','equalto', KEYCLOAK_LDAP_CMP_NAME) | map(attribute='id') | list | first | default('') ) | string | trim }} changed_when: false - name: ✅ Assert | LDAP component exists assert: that: - ldap_cmp_id_assert not in ["", "null"] fail_msg: "LDAP component '{{ KEYCLOAK_LDAP_CMP_NAME }}' not found in realm '{{ KEYCLOAK_REALM }}'." - name: ✅ Assert | Load desired ldap-roles mapper from dictionary set_fact: desired_group_mapper_raw_assert: >- {{ ( KEYCLOAK_DICTIONARY_REALM.components['org.keycloak.storage.UserStorageProvider'] | selectattr('name','equalto', KEYCLOAK_LDAP_CMP_NAME) | list | first | default({}) ).subComponents['org.keycloak.storage.ldap.mappers.LDAPStorageMapper'] | default([]) | selectattr('name','equalto','ldap-roles') | list | first | default({}) }} changed_when: false - name: ✅ Assert | ldap-roles mapper exists in dictionary assert: that: - desired_group_mapper_raw_assert | length > 0 fail_msg: "'ldap-roles' mapper missing in KEYCLOAK_DICTIONARY_REALM." - name: ✅ Assert | Extract desired groups.path when: desired_group_mapper_raw_assert | length > 0 set_fact: desired_groups_path_assert: "{{ (desired_group_mapper_raw_assert.config['groups.path'] | default([])) | first | default('') | trim }}" desired_groups_top_assert: "{{ desired_groups_path_assert.lstrip('/') | split('/') | first | default('') }}" changed_when: false - name: ✅ Assert | groups.path defined when mapper exists when: desired_group_mapper_raw_assert | length > 0 assert: that: - desired_groups_path_assert != "" fail_msg: "ldap-roles mapper exists but config.groups.path is empty." - name: ✅ Assert | List ldap-roles mappers under LDAP component (client-side) set_fact: grp_mapper_ids_assert: >- {{ ( kc_components_assert.stdout | kcadm_json | selectattr('parentId','equalto', ldap_cmp_id_assert) | selectattr('providerId','equalto','group-ldap-mapper') | selectattr('providerType','equalto','org.keycloak.storage.ldap.mappers.LDAPStorageMapper') | selectattr('name','equalto','ldap-roles') | map(attribute='id') | list ) }} changed_when: false - name: ✅ Assert | Exactly one ldap-roles mapper exists assert: that: - grp_mapper_ids_assert | length == 1 fail_msg: >- Expected exactly ONE 'ldap-roles' mapper under LDAP component, found {{ grp_mapper_ids_assert | length }}: {{ grp_mapper_ids_assert }} - name: ✅ Assert | Verify top-level group exists when groups.path is set when: desired_groups_top_assert | length > 0 block: - name: ✅ Assert | Fetch groups (JSON) shell: > {{ KEYCLOAK_EXEC_KCADM }} get groups -r {{ KEYCLOAK_REALM }} --format json register: kc_groups_assert changed_when: false - name: ✅ Assert | Resolve top-level group id set_fact: groups_top_id_assert: >- {{ ( kc_groups_assert.stdout | kcadm_json | selectattr('name','equalto', desired_groups_top_assert) | map(attribute='id') | list | first | default('') ) | string | trim }} changed_when: false - name: ✅ Assert | Top-level group exists assert: that: - groups_top_id_assert not in ["", "null"] fail_msg: >- groups.path='{{ desired_groups_path_assert }}' requires top-level group '{{ desired_groups_top_assert }}', but it does not exist. - name: ✅ Assert | LDAP summary debug: msg: ldap_component_id: "{{ ldap_cmp_id_assert }}" ldap_roles_mapper_ids: "{{ grp_mapper_ids_assert }}" groups_path: "{{ desired_groups_path_assert }}" top_group: "{{ desired_groups_top_assert }}"