XWiki: LDAP/OIDC admin mapping, config mounts, and REST installs

- LDAP: move settings to xwiki.cfg; enable trylocal (1/0), group_mapping to XWiki.XWikiAdminGroup,
  and mode_group_sync=always.
- OIDC: add groups claim request (oidc.userinfoclaims), map provider group to XWiki.XWikiAdminGroup,
  and use space-separated scopes.
- Compose: mount xwiki.cfg and xwiki.properties into /usr/local/xwiki.
- Extensions: wait for REST readiness; pre-check OIDC/LDAP extensions (URL-encoded IDs);
  install via REST job only if missing.
- Vars: strict mappings to LDAP.* and OIDC.* (no defaults), add XWIKI_ADMIN_GROUP and derived DNs.
- Config: expose ldap.local_enabled; tidy meta tags; README grammar update.

Conversation: https://chatgpt.com/share/68c2b8ad-4814-800f-b377-065f967998db
This commit is contained in:
2025-09-11 13:55:53 +02:00
parent 8bc6e1f921
commit 6418a462ec
11 changed files with 198 additions and 58 deletions

View File

@@ -6,7 +6,7 @@ Empower your organization with **XWiki**, an open-source enterprise wiki and kno
## Overview
This role deploys XWiki using Docker, automating the installation, configuration, and management of your XWiki server. It integrates with an external PostgreSQL database, Redis for caching and sessions, and an Nginx reverse proxy. The role supports advanced features such as global CSS injection, Matomo analytics, OIDC authentication, and centralized logout, making it a powerful and customizable solution within the Infinito.Nexus ecosystem.
This role deploys XWiki using Docker, automating the installation, configuration, and management of your XWiki server. It integrates with an MariaDB database and an Nginx reverse proxy. The role supports advanced features such as global CSS injection, Matomo analytics, OIDC authentication, and centralized logout, making it a powerful and customizable solution within the Infinito.Nexus ecosystem.
## Features
@@ -17,7 +17,6 @@ This role deploys XWiki using Docker, automating the installation, configuration
- **Office Integration:** Import, export, and collaborate on Office documents (Word, Excel, PDF).
- **Customization & Theming:** Adapt the look and feel of your wiki with skins, CSS, and scripting.
- **Integration Ready:** Connect with external systems such as Keycloak (OIDC), LDAP, or analytics tools like Matomo.
- **Scalability:** Backend support with PostgreSQL and Redis for performance and session handling.
## Further Resources

View File

@@ -29,3 +29,5 @@ server:
- "x.wiki.{{ PRIMARY_DOMAIN }}"
rbac:
roles: {}
ldap:
local_enabled: true # Allows local login if LDAP is down

View File

@@ -8,7 +8,10 @@ galaxy_info:
Kevin Veen-Birkenbach
Consulting & Coaching Solutions
https://www.veen.world
galaxy_tags: [wiki, collaboration, knowledge, documentation, cms]
galaxy_tags:
- wiki
- documentation
- xwiki
repository: "https://s.infinito.nexus/code"
issue_tracker_url: "https://s.infinito.nexus/issues"
documentation: "https://s.infinito.nexus/code/"

View File

@@ -0,0 +1,68 @@
- name: "load docker, db and proxy for {{ application_id }}"
include_role:
name: sys-stk-full-stateful
vars:
docker_compose_flush_handlers: false
- name: "Render xwiki.cfg"
template:
src: "xwiki.cfg.j2"
dest: "{{ XWIKI_HOST_CONF_PATH }}"
notify: docker compose up
- name: "Render xwiki.properties"
template:
src: "xwiki.properties.j2"
dest: "{{ XWIKI_HOST_PROPERTIES_PATH }}"
notify: docker compose up
- name: "flush docker compose for '{{ application_id }}'"
meta: flush_handlers
- name: "Wait until XWiki REST is ready"
uri:
url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/"
status_code: [200, 401]
return_content: no
register: xwiki_rest_up
retries: 60
delay: 5
until: xwiki_rest_up is succeeded
- name: "Check if OIDC extension installed"
uri:
url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/wikis/xwiki/extensions/{{ XWIKI_EXT_OIDC_ID | urlencode }}"
method: GET
user: "{{ XWIKI_ADMIN_USER }}"
password: "{{ XWIKI_ADMIN_PASS }}"
force_basic_auth: yes
status_code: [200,404]
register: xwiki_oidc_ext
when: XWIKI_OIDC_ENABLED | bool
- name: "Check if LDAP extension installed"
uri:
url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/wikis/xwiki/extensions/{{ XWIKI_EXT_LDAP_ID | urlencode }}"
method: GET
user: "{{ XWIKI_ADMIN_USER }}"
password: "{{ XWIKI_ADMIN_PASS }}"
force_basic_auth: yes
status_code: [200,404]
register: xwiki_ldap_ext
when: XWIKI_LDAP_ENABLED | bool
- name: "Install LDAP and/or OIDC extensions"
uri:
url: "{{ XWIKI_REST_BASE }}"
method: PUT
user: "{{ XWIKI_ADMIN_USER }}"
password: "{{ XWIKI_ADMIN_PASS }}"
force_basic_auth: yes
headers: { Content-Type: "text/xml" }
body: "{{ lookup('template', 'installjobrequest.xml.j2') }}"
status_code: 200
when:
- (XWIKI_OIDC_ENABLED | bool and xwiki_oidc_ext.status == 404) or
(XWIKI_LDAP_ENABLED | bool and (xwiki_ldap_ext is not skipped) and xwiki_ldap_ext.status == 404)
- include_tasks: utils/run_once.yml

View File

@@ -1,7 +1,4 @@
---
- block:
- name: "load docker, db and proxy for {{ application_id }}"
include_role:
name: sys-stk-full-stateful
- include_tasks: utils/run_once.yml
- include_tasks: 01_core.yml
when: run_once_web_app_xwiki is not defined

View File

@@ -7,8 +7,10 @@
container_name: "{{ XWIKI_CONTAINER }}"
hostname: '{{ XWIKI_HOSTNAME}}'
ports:
- "127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}"
- "127.0.0.1:{{ XWIKI_HOST_PORT }}:{{ container_port }}"
volumes:
- "{{ XWIKI_HOST_CONF_PATH }}:/usr/local/xwiki/xwiki.cfg"
- "{{ XWIKI_HOST_PROPERTIES_PATH }}:/usr/local/xwiki/xwiki.properties"
- 'data:/usr/local/xwiki'
{% include 'roles/docker-container/templates/healthcheck/curl.yml.j2' %}
{% include 'roles/docker-container/templates/base.yml.j2' %}

View File

@@ -3,27 +3,3 @@ DB_PASSWORD="{{ database_password }}"
DB_HOST="{{ database_host }}"
DB_PORT="{{ database_port }}"
DB_DATABASE="{{ database_name }}"
### Pretix core
#XWIKI_XWIKI_INSTANCE_NAME="{{ PRIMARY_DOMAIN | upper }} Tickets"
#XWIKI_XWIKI_ALLOWED_HOSTS="{{ XWIKI_HOSTNAME }},127.0.0.1,localhost"
#XWIKI_XWIKI_URL="{{ XWIKI_URL }}"
#XWIKI_XWIKI_AUTH_BACKENDS="xwiki.base.auth.NativeAuthBackend{% if XWIKI_OIDC_ENABLED %},xwiki_oidc.auth.OIDCAuthBackend{% endif %}"
#
### Locale
#XWIKI_LOCALE_TIMEZONE="{{ HOST_TIMEZONE }}"
#
#{% if XWIKI_OIDC_ENABLED %}
### OIDC (plugin)
#XWIKI_OIDC_TITLE="{{ XWIKI_OIDC_LABEL | replace('\"','\\\"') }}"
#XWIKI_OIDC_ISSUER="{{ XWIKI_OIDC_ISSUER }}"
#XWIKI_OIDC_AUTHORIZATION_ENDPOINT="{{ XWIKI_OIDC_AUTH_URL }}"
#XWIKI_OIDC_TOKEN_ENDPOINT="{{ XWIKI_OIDC_TOKEN_URL }}"
#XWIKI_OIDC_USERINFO_ENDPOINT="{{ XWIKI_OIDC_USERINFO_URL }}"
#XWIKI_OIDC_END_SESSION_ENDPOINT="{{ XWIKI_OIDC_LOGOUT_URL }}"
#XWIKI_OIDC_JWKS_URI="{{ XWIKI_OIDC_JWKS_URL }}"
#XWIKI_OIDC_CLIENT_ID="{{ XWIKI_OIDC_CLIENT_ID }}"
#XWIKI_OIDC_CLIENT_SECRET="{{ XWIKI_OIDC_CLIENT_SECRET }}"
#XWIKI_OIDC_SCOPES="{{ XWIKI_OIDC_SCOPES }}"
#XWIKI_OIDC_UNIQUE_ATTRIBUTE="{{ XWIKI_OIDC_UNIQUE_ATTRIBUTE }}"
#{% endif %}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<jobRequest xmlns="http://www.xwiki.org">
<id>install-extensions</id>
<jobType>install</jobType>
<request>
<namespace>wiki:xwiki</namespace>
<interactive>false</interactive>
<verbose>true</verbose>
<installDependencies>true</installDependencies>
<extensions>
{% if XWIKI_LDAP_ENABLED | bool %}
<extension>
<id>{{ XWIKI_EXT_LDAP_ID }}</id>
<version>{{ XWIKI_EXT_LDAP_VERSION }}</version>
<namespace>wiki:xwiki</namespace>
</extension>
{% endif %}
{% if XWIKI_OIDC_ENABLED | bool %}
<extension>
<id>{{ XWIKI_EXT_OIDC_ID }}</id>
<version>{{ XWIKI_EXT_OIDC_VERSION }}</version>
<namespace>wiki:xwiki</namespace>
</extension>
{% endif %}
</extensions>
</request>
</jobRequest>

View File

@@ -0,0 +1,20 @@
# ---- Authentication selection
{% if XWIKI_OIDC_ENABLED | bool %}
xwiki.authentication.authclass=org.xwiki.contrib.oidc.auth.OIDCAuthServiceImpl
{% elif XWIKI_LDAP_ENABLED | bool %}
xwiki.authentication.authclass=org.xwiki.contrib.ldap.XWikiLDAPAuthServiceImpl
xwiki.authentication.ldap=1
xwiki.authentication.ldap.trylocal={{ (XWIKI_LDAP_TRYLOCAL | bool) | ternary(1, 0) }}
xwiki.authentication.ldap.group_mapping=XWiki.XWikiAdminGroup={{ XWIKI_LDAP_ADMIN_GROUP_DN }}
xwiki.authentication.ldap.mode_group_sync=always
xwiki.authentication.ldap.server={{ XWIKI_LDAP_SERVER }}
xwiki.authentication.ldap.port={{ XWIKI_LDAP_PORT }}
xwiki.authentication.ldap.base_DN={{ XWIKI_LDAP_BASE_DN }}
xwiki.authentication.ldap.bind_DN={{ XWIKI_LDAP_BIND_DN }}
xwiki.authentication.ldap.bind_pass={{ XWIKI_LDAP_BIND_PASS }}
xwiki.authentication.ldap.fields_mapping={{ XWIKI_LDAP_FIELDS_MAPPING }}
xwiki.authentication.ldap.update_user=1
{% else %}
# Fallback: Native XWiki Auth
# xwiki.authentication.authclass=com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl
{% endif %}

View File

@@ -0,0 +1,16 @@
############################################
# OIDC
{% if XWIKI_OIDC_ENABLED | bool %}
oidc.provider={{ XWIKI_OIDC_PROVIDER }}
oidc.endpoint.authorization={{ XWIKI_OIDC_AUTHORIZATION }}
oidc.endpoint.token={{ XWIKI_OIDC_TOKEN }}
oidc.endpoint.userinfo={{ XWIKI_OIDC_USERINFO }}
oidc.endpoint.logout={{ XWIKI_OIDC_LOGOUT }}
oidc.clientid={{ XWIKI_OIDC_CLIENT_ID }}
oidc.secret={{ XWIKI_OIDC_CLIENT_SECRET }}
oidc.scope={{ XWIKI_OIDC_SCOPES }}
oidc.enableUser=true
oidc.userinfoclaims={{ XWIKI_OIDC_GROUPS_CLAIM }}
oidc.groups.claim={{ XWIKI_OIDC_GROUPS_CLAIM }}
oidc.groups.mapping=XWiki.XWikiAdminGroup={{ XWIKI_OIDC_ADMIN_PROVIDER_GROUP }}
{% endif %}

View File

@@ -1,33 +1,63 @@
# General
application_id: "web-app-xwiki"
database_type: "mariadb"
container_port: 8080
container_hostname: "{{ domains | get_domain(application_id) }}"
application_id: "web-app-xwiki"
database_type: "mariadb"
container_port: 8080
container_hostname: "{{ domains | get_domain(application_id) }}"
# Pretix
# XWiki
XWIKI_HOST_PORT: "{{ ports.localhost.http[application_id] }}"
## URLs
XWIKI_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
XWIKI_HOSTNAME: "{{ container_hostname }}"
XWIKI_HOSTNAME: "{{ container_hostname }}"
## OIDC (mirrors GitLabs pattern)
XWIKI_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}"
XWIKI_OIDC_LABEL: "{{ OIDC.BUTTON_TEXT }}"
XWIKI_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}"
XWIKI_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}"
XWIKI_OIDC_ISSUER: "{{ OIDC.CLIENT.ISSUER_URL }}"
XWIKI_OIDC_AUTH_URL: "{{ OIDC.CLIENT.AUTHORIZE_URL }}"
XWIKI_OIDC_TOKEN_URL: "{{ OIDC.CLIENT.TOKEN_URL }}"
XWIKI_OIDC_USERINFO_URL: "{{ OIDC.CLIENT.USER_INFO_URL }}"
XWIKI_OIDC_LOGOUT_URL: "{{ OIDC.CLIENT.LOGOUT_URL }}"
XWIKI_OIDC_JWKS_URL: "{{ OIDC.CLIENT.CERTS }}"
XWIKI_OIDC_SCOPES: "openid,email,profile"
# Use Keycloak username claim by default (plugin default is 'sub')
XWIKI_OIDC_UNIQUE_ATTRIBUTE: "{{ OIDC.ATTRIBUTES.USERNAME }}"
## Paths
XWIKI_HOST_CONF_PATH: "{{ [docker_compose.directories.config, 'xwiki.cfg'] | path_join }}"
XWIKI_HOST_PROPERTIES_PATH: "{{ [docker_compose.directories.config, 'xwiki.properties'] | path_join }}"
## Docker
XWIKI_IMAGE_CUSTOM: "xwiki_custom"
XWIKI_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.image') }}"
XWIKI_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.version') }}"
XWIKI_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.name') }}"
XWIKI_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
XWIKI_IMAGE_CUSTOM: "xwiki_custom"
XWIKI_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.image') }}"
XWIKI_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.version') }}"
XWIKI_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.xwiki.name') }}"
XWIKI_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
# Feature toggles (must be set in config/main.yml -> features)
XWIKI_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap') }}"
XWIKI_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}"
# Admin credentials (must be provided via inventory/vault)
XWIKI_ADMIN_USER: "{{ users.administrator.username }}"
XWIKI_ADMIN_PASS: "{{ users.administrator.password }}"
XWIKI_ADMIN_GROUP: "{{ application_id }}-administrator"
# REST endpoint (local inside container)
XWIKI_REST_BASE: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/jobs?jobType=install&async=false"
# Extension IDs + Versions (pin versions explicitly)
XWIKI_EXT_LDAP_ID: "org.xwiki.contrib.ldap:ldap-authenticator"
XWIKI_EXT_LDAP_VERSION: "9.15.7"
XWIKI_EXT_OIDC_ID: "org.xwiki.contrib.oidc:oidc-authenticator"
XWIKI_EXT_OIDC_VERSION: "2.19.2"
# LDAP configuration (mapped to LDAP.* context)
XWIKI_LDAP_SERVER: "{{ LDAP.SERVER.DOMAIN }}"
XWIKI_LDAP_PORT: "{{ LDAP.SERVER.PORT }}"
XWIKI_LDAP_BASE_DN: "{{ LDAP.DN.ROOT }}"
XWIKI_LDAP_BIND_DN: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
XWIKI_LDAP_BIND_PASS: "{{ LDAP.BIND_CREDENTIAL }}"
XWIKI_LDAP_TRYLOCAL: "{{ applications | get_app_conf(application_id, 'ldap.local_enabled') }}"
XWIKI_LDAP_FIELDS_MAPPING: "last_name={{ LDAP.USER.ATTRIBUTES.SURNAME }},first_name={{ LDAP.USER.ATTRIBUTES.FIRSTNAME }},email={{ LDAP.USER.ATTRIBUTES.MAIL }}"
XWIKI_LDAP_ADMIN_GROUP_DN: "cn={{ XWIKI_ADMIN_GROUP ~ ',' ~ LDAP.DN.OU.GROUPS }}"
# OIDC configuration (must exist in OIDC.* context)
XWIKI_OIDC_PROVIDER: "{{ OIDC.CLIENT.ISSUER_URL }}"
XWIKI_OIDC_AUTHORIZATION: "{{ OIDC.CLIENT.AUTHORIZE_URL }}"
XWIKI_OIDC_TOKEN: "{{ OIDC.CLIENT.TOKEN_URL }}"
XWIKI_OIDC_USERINFO: "{{ OIDC.CLIENT.USER_INFO_URL }}"
XWIKI_OIDC_LOGOUT: "{{ OIDC.CLIENT.LOGOUT_URL }}"
XWIKI_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}"
XWIKI_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}"
XWIKI_OIDC_SCOPES: "openid email profile {{ RBAC.GROUP.CLAIM }}"
XWIKI_OIDC_GROUPS_CLAIM: "{{ RBAC.GROUP.CLAIM }}"
XWIKI_OIDC_ADMIN_PROVIDER_GROUP: "{{ [RBAC.GROUP.NAME, XWIKI_ADMIN_GROUP] | path_join }}"