--- application_id: "web-app-keycloak" entity_name: "{{ application_id | get_entity_name }}" domain: "{{ lookup('domain',application_id) }}" KEYCLOAK_CONTAINER_HOSTNAME: "{{ entity_name }}" KEYCLOAK_DOMAIN: "{{ domain }}" KEYCLOAK_REALM: "{{ OIDC.CLIENT.REALM }}" KEYCLOAK_URL: "{{ lookup('tls', application_id, 'url.base') | regex_replace('/+$', '') }}" KEYCLOAK_REALM_URL: "{{ KEYCLOAK_URL }}/realms/{{ KEYCLOAK_REALM }}" KEYCLOAK_DEBUG_ENABLED: "{{ MODE_DEBUG }}" KEYCLOAK_CLIENT_ID: "{{ OIDC.CLIENT.ID }}" KEYCLOAK_SERVER_INTERNAL_URL: "http://127.0.0.1:8080" KEYCLOAK_RBAC_GROUP_CLAIM: "{{ RBAC.GROUP.CLAIM }}" KEYCLOAK_RBAC_GROUP_NAME: "{{ RBAC.GROUP.NAME }}" KEYCLOAK_RBAC_ALL_APP_IDS: "{{ lookup('deployment').groups | rbac_app_ids }}" KEYCLOAK_RBAC_RUN_APP_IDS: "{{ lookup('deployment').running | rbac_app_ids }}" KEYCLOAK_PER_APP_LDAP_ROLES_MAPPER: >- {{ ((KEYCLOAK_DICTIONARY_REALM.components['org.keycloak.storage.UserStorageProvider'] | selectattr('name','equalto', KEYCLOAK_LDAP_CMP_NAME) | list | first ).subComponents['org.keycloak.storage.ldap.mappers.LDAPStorageMapper'] | selectattr('name','equalto','ldap-roles') | list | first | dict2items | rejectattr('key','in',['subComponents','id']) | list | items2dict) | combine({ 'name': kc_app_id | kc_per_app_mapper_name, 'parentId': ldap_cmp_id | trim, 'providerId': 'group-ldap-mapper', 'providerType': 'org.keycloak.storage.ldap.mappers.LDAPStorageMapper', 'config': { 'preserve.group.inheritance': ['false'], 'group.name.ldap.attribute': ['description'], 'groups.ldap.filter': [kc_app_id | kc_per_app_ldap_filter], 'groups.path': ['/' ~ (KEYCLOAK_RBAC_GROUP_NAME | trim('/')) ~ '/' ~ kc_app_id], 'groups.path.group': [kc_app_group_id], 'drop.non.existing.groups.during.sync': ['true'], } }, recursive=True) }} KEYCLOAK_HEALTH_ENABLED: true KEYCLOAK_REALM_IMPORT_ENABLED: "{{ lookup('config', application_id, 'services.keycloak.realm.import_enabled') }}" KEYCLOAK_REALM_IMPORT_DIR_HOST: "{{ [lookup('container', application_id, 'directories.volumes'),'import'] | path_join }}" KEYCLOAK_REALM_IMPORT_DIR_DOCKER: "{{ lookup('volume', 'web-app-keycloak', 'realm_import').target }}" KEYCLOAK_REALM_IMPORT_FILE_SRC: "import/realm.json.j2" KEYCLOAK_REALM_IMPORT_FILENAME: "realm.json" KEYCLOAK_REALM_IMPORT_FILE_DST: "{{ [KEYCLOAK_REALM_IMPORT_DIR_HOST,KEYCLOAK_REALM_IMPORT_FILENAME] | path_join }}" KEYCLOAK_REALM_IMPORT_FILE_DOCKER: "{{ [KEYCLOAK_REALM_IMPORT_DIR_DOCKER,KEYCLOAK_REALM_IMPORT_FILENAME] | path_join }}" KEYCLOAK_REALM_TOTP_ENABLED: "{{ lookup('config', application_id, 'services.keycloak.realm.totp_enabled') | bool }}" KEYCLOAK_BOOTSTRAP_ADMIN_USERNAME: "{{ lookup('config', application_id, 'services.keycloak.accounts.bootstrap.username') }}" KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD: "{{ lookup('config', application_id, 'secrets.credentials.administrator_password') }}" KEYCLOAK_PERMANENT_ADMIN_USERNAME: "{{ lookup('config', application_id, 'services.keycloak.accounts.system.username') }}" KEYCLOAK_PERMANENT_ADMIN_PASSWORD: "{{ lookup('config', application_id, 'secrets.credentials.administrator_password') }}" KEYCLOAK_CONTAINER: "{{ lookup('config', application_id, 'services.keycloak.name') }}" KEYCLOAK_CONTAINER_ADDRESS: "{{ lookup('container_address', application_id, entity_name) }}" # Exception: KEYCLOAK_LOCAL_CID is set by utils/login.yml after resolving the swarm-hosting # node; fall back to the compose-mode container_address when the fact is not yet set # so vars resolve cleanly before the manager-side ops kick in. KEYCLOAK_TARGET_CONTAINER: "{{ KEYCLOAK_LOCAL_CID | default(KEYCLOAK_CONTAINER_ADDRESS) }}" # nocheck: container-var-name-lookup references the consumer's container, not this role's service KEYCLOAK_EXEC_CONTAINER: "container exec -i {{ KEYCLOAK_TARGET_CONTAINER }}" # nocheck: container-exec-resolver # nocheck: container-var-name-lookup references the consumer's container, not this role's service KEYCLOAK_KCADM: "/opt/keycloak/bin/kcadm.sh" KEYCLOAK_EXEC_KCADM: "{{ KEYCLOAK_EXEC_CONTAINER }} {{ KEYCLOAK_KCADM }}" KEYCLOAK_CID_RESOLVE_CMD: >- container ps --filter "label=com.docker.swarm.service.name={{ lookup('container_service', application_id, entity_name) }}" --filter status=running --format '{{ "{{.ID}}" }}' | head -1 KEYCLOAK_IMAGE: "{{ lookup('config', application_id, 'services.keycloak.image') }}" KEYCLOAK_VERSION: "{{ lookup('config', application_id, 'services.keycloak.version') }}" KEYCLOAK_REPLICAS: "{{ groups[application_id] | length }}" KEYCLOAK_REPLICAS_WANT: "{{ KEYCLOAK_REPLICAS }}/{{ KEYCLOAK_REPLICAS }}" KEYCLOAK_DB_NETWORK_ARG: "{% if lookup('database', application_id, 'shared') | bool %}--network {{ lookup('database', application_id, 'network') | quote }}{% endif %}" KEYCLOAK_FRONTCHANNEL_LOGOUT_URL: "{{ lookup('tls', 'web-svc-logout', 'url.base') }}" KEYCLOAK_LOGOUT_PANEL_FILE: "{{ lookup('nginx', 'directories.configuration.lua') }}{{ application_id }}-logout-panel.js" KEYCLOAK_LOGOUT_PANEL_ENABLED: "{{ lookup('config', application_id, 'services.javascript.enabled') | bool }}" KEYCLOAK_ADMIN_NETWORKS: "{{ lookup('config', application_id, 'services.keycloak.admin.allowed_networks') | kc_admin_networks }}" KEYCLOAK_REDIRECT_URIS: "{{ lookup('redirect_uris') }}" KEYCLOAK_SAML_APPS: >- {{ lookup('applications') | dict2items | selectattr('value.services', 'defined') | selectattr('value.services.sso', 'defined') | selectattr('value.services.sso.flavor', 'defined') | selectattr('value.services.sso.flavor', 'equalto', 'saml') | map(attribute='key') | select('in', group_names) | list }} KEYCLOAK_WEB_ORIGINS: >- {{ KEYCLOAK_REDIRECT_URIS | map('regex_replace','/\\*$','') | map('regex_search','^(https?://[^/]+)') | select('string') | list | unique }} KEYCLOAK_POST_LOGOUT_URIS: "+" KEYCLOAK_LDAP_ENABLED: "{{ lookup('config', application_id, 'services.ldap.enabled') }}" KEYCLOAK_LDAP_SHARED: "{{ lookup('config', application_id, 'services.ldap.shared') }}" KEYCLOAK_LDAP_CMP_NAME: "{{ LDAP.SERVER.DOMAIN }}" # Name of the LDAP User Federation component in Keycloak (as shown in UI) KEYCLOAK_LDAP_BIND_DN: "{{ LDAP.DN.ADMINISTRATOR.DATA }}" KEYCLOAK_LDAP_BIND_PW: "{{ LDAP.BIND_CREDENTIAL }}" KEYCLOAK_LDAP_URL: "{{ LDAP.SERVER.URI }}" KEYCLOAK_LDAP_CONNECTION_POOLING: "{{ lookup('config', application_id, 'services.ldap.connection_pooling') | bool }}" KEYCLOAK_LDAP_SYNC_CHANGES_PERIOD: "{{ lookup('config', application_id, 'services.ldap.sync.period.changes') }}" KEYCLOAK_LDAP_SYNC_FULL_PERIOD: "{{ lookup('config', application_id, 'services.ldap.sync.period.full') }}" KEYCLOAK_LDAP_USER_OBJECT_CLASSES: > {{ ( (LDAP.USER.OBJECTS.STRUCTURAL | reject('equalto','posixAccount') | list) + (LDAP.USER.OBJECTS.AUXILIARY | dict2items | map(attribute='value') | list) ) | join(', ') }} KEYCLOAK_USER_EMAILS_VERIFIED: "{{ lookup('config', application_id, 'services.keycloak.users.emails_verified') }}" KEYCLOAK_MOODLE_ENABLED: "{{ 'web-app-moodle' in group_names }}" KEYCLOAK_DECLARED_CLIENT_APPS: "{{ lookup('applications') | kc_declared_client_apps(lookup('deployment').groups) }}" KEYCLOAK_DECLARED_CLIENT_IDS: "{{ lookup('applications') | kc_declared_client_ids(lookup('deployment').groups) }}" KEYCLOAK_RECAPTCHA_ENABLED: "{{ lookup('config', application_id, 'services.recaptcha.enabled') }}" KEYCLOAK_RECAPTCHA_KEY: "{{ lookup('config', application_id, 'secrets.credentials.recaptcha.key') }}" KEYCLOAK_RECAPTCHA_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.recaptcha.secret') }}" KEYCLOAK_COMMAND_PERMANENT_ADMIN_LOGIN: |- {{ KEYCLOAK_EXEC_CONTAINER }} sh -lc ' set -e {{ KEYCLOAK_KCADM }} config credentials \ --server {{ KEYCLOAK_SERVER_INTERNAL_URL }} \ --realm master \ --user "$KEYCLOAK_PERMANENT_ADMIN_USERNAME" \ --password "$KEYCLOAK_PERMANENT_ADMIN_PASSWORD" '