main.ymlΒΆ

---
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"
  '