From f576b4257973d74d0ff26d2bb59a06a3e4204d7d Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 12 Sep 2025 14:04:02 +0200 Subject: [PATCH] XWiki: two-phase bootstrap + extension install before enabling auth; add XOR validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 02_validation.yml to prevent OIDC+LDAP enabled simultaneously - Introduce _flush_config.yml with switches (OIDC/LDAP/superadmin) - Bootstrap with native+superadmin → create admin → install extensions (superadmin) → enable final auth - Refactor REST vars (XWIKI_REST_BASE, XWIKI_REST_XWIKI, XWIKI_REST_EXTENSION_INSTALL) - Update templates to use switch vars; gate OIDC block in properties - Idempotent REST readiness waits Conversation: https://chatgpt.com/share/68c40c1e-2b3c-800f-b59f-8d37baa9ebb2 --- roles/web-app-xwiki/tasks/01_core.yml | 78 +++++-------------- roles/web-app-xwiki/tasks/02_validation.yml | 9 +++ ...otstrap_admin.yml => 03_administrator.yml} | 6 +- roles/web-app-xwiki/tasks/04_extensions.yml | 38 +++++++++ roles/web-app-xwiki/tasks/_flush_config.yml | 25 ++++++ roles/web-app-xwiki/templates/xwiki.cfg.j2 | 10 +-- .../templates/xwiki.properties.j2 | 2 +- roles/web-app-xwiki/vars/main.yml | 5 +- 8 files changed, 103 insertions(+), 70 deletions(-) create mode 100644 roles/web-app-xwiki/tasks/02_validation.yml rename roles/web-app-xwiki/tasks/{02_bootstrap_admin.yml => 03_administrator.yml} (88%) create mode 100644 roles/web-app-xwiki/tasks/04_extensions.yml create mode 100644 roles/web-app-xwiki/tasks/_flush_config.yml diff --git a/roles/web-app-xwiki/tasks/01_core.yml b/roles/web-app-xwiki/tasks/01_core.yml index 5c2c26bc..2fef94f5 100644 --- a/roles/web-app-xwiki/tasks/01_core.yml +++ b/roles/web-app-xwiki/tasks/01_core.yml @@ -1,70 +1,30 @@ +- name: Validate XWiki variables + include_tasks: 02_validation.yml + - 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: Deploy Bootstrap Config + include_tasks: _flush_config.yml + vars: + xwiki_oidc_enabled_switch: false + xwiki_ldap_enabled_switch: false + xwiki_superadmin_enabled_switch: true -- name: "Render xwiki.properties" - template: - src: "xwiki.properties.j2" - dest: "{{ XWIKI_HOST_PROPERTIES_PATH }}" - notify: docker compose up +- name: Load setup procedures for admin + include_tasks: 03_administrator.yml -- name: "flush docker compose for '{{ application_id }}'" - meta: flush_handlers +- name: Load setup procedures for extensions + include_tasks: 04_extensions.yml -- name: "Wait until XWiki REST is ready" - uri: - url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/" - status_code: [200, 401, 302] - return_content: no - register: xwiki_rest_up - retries: 60 - delay: 5 - until: xwiki_rest_up is succeeded - -- include_tasks: 02_bootstrap_admin.yml - -- name: "Check if OIDC extension installed" - uri: - url: "{{ XWIKI_REST_GENERAL }}/extensions/{{ XWIKI_EXT_OIDC_ID | urlencode }}" - method: GET - user: "{{ XWIKI_ADMIN_USER }}" - password: "{{ XWIKI_ADMIN_PASS }}" - force_basic_auth: yes - status_code: [200, 404, 302] - register: xwiki_oidc_ext - when: XWIKI_OIDC_ENABLED | bool - -- name: "Check if LDAP extension installed" - uri: - url: "{{ XWIKI_REST_GENERAL }}/extensions/{{ XWIKI_EXT_LDAP_ID | urlencode }}" - method: GET - user: "{{ XWIKI_ADMIN_USER }}" - password: "{{ XWIKI_ADMIN_PASS }}" - force_basic_auth: yes - status_code: [200, 404, 302] - 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) +- name: Deploy Final Config + include_tasks: _flush_config.yml + vars: + xwiki_oidc_enabled_switch: "{{ XWIKI_OIDC_ENABLED | bool }}" + xwiki_ldap_enabled_switch: "{{ XWIKI_LDAP_ENABLED | bool }}" + xwiki_superadmin_enabled_switch: false - include_tasks: utils/run_once.yml diff --git a/roles/web-app-xwiki/tasks/02_validation.yml b/roles/web-app-xwiki/tasks/02_validation.yml new file mode 100644 index 00000000..28124c5b --- /dev/null +++ b/roles/web-app-xwiki/tasks/02_validation.yml @@ -0,0 +1,9 @@ +- name: "ASSERT | Only one auth backend (OIDC or LDAP) may be enabled" + assert: + that: + - not ((XWIKI_OIDC_ENABLED | bool) and (XWIKI_LDAP_ENABLED | bool)) + fail_msg: >- + Invalid auth configuration: both OIDC and LDAP are enabled + (features.oidc={{ XWIKI_OIDC_ENABLED }}, features.ldap={{ XWIKI_LDAP_ENABLED }}). + Enable only one, or disable both to use native/superadmin login. + success_msg: "Auth config OK: OIDC={{ XWIKI_OIDC_ENABLED }}, LDAP={{ XWIKI_LDAP_ENABLED }}." diff --git a/roles/web-app-xwiki/tasks/02_bootstrap_admin.yml b/roles/web-app-xwiki/tasks/03_administrator.yml similarity index 88% rename from roles/web-app-xwiki/tasks/02_bootstrap_admin.yml rename to roles/web-app-xwiki/tasks/03_administrator.yml index 01b03e09..b970cee1 100644 --- a/roles/web-app-xwiki/tasks/02_bootstrap_admin.yml +++ b/roles/web-app-xwiki/tasks/03_administrator.yml @@ -2,7 +2,7 @@ # Wait until REST endpoint is available (01_core usually ensures this, but add safety) - name: "XWIKI | Wait until REST answers" uri: - url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/" + url: "{{ XWIKI_REST_BASE }}" status_code: [200, 401] register: _rest_ping retries: 60 @@ -13,7 +13,7 @@ # 404 => missing, 302 => DW redirect (treat as missing for bootstrap) - name: "XWIKI | Check if target admin user exists" uri: - url: "{{ XWIKI_REST_GENERAL }}/users/{{ XWIKI_ADMIN_USER | urlencode }}" + url: "{{ XWIKI_REST_XWIKI }}/users/{{ XWIKI_ADMIN_USER | urlencode }}" method: GET user: "{{ XWIKI_SUPERADMIN_USERNAME }}" password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" @@ -24,7 +24,7 @@ # Create admin user if not existing (or DW still redirecting) - name: "XWIKI | Create admin user via REST" uri: - url: "{{ XWIKI_REST_GENERAL }}/users" + url: "{{ XWIKI_REST_XWIKI }}/users" method: POST user: "{{ XWIKI_SUPERADMIN_USERNAME }}" password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" diff --git a/roles/web-app-xwiki/tasks/04_extensions.yml b/roles/web-app-xwiki/tasks/04_extensions.yml new file mode 100644 index 00000000..c140c099 --- /dev/null +++ b/roles/web-app-xwiki/tasks/04_extensions.yml @@ -0,0 +1,38 @@ +- name: "Check if OIDC extension installed" + uri: + url: "{{ XWIKI_REST_XWIKI }}/extensions/{{ XWIKI_EXT_OIDC_ID | urlencode }}" + method: GET + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: yes + status_code: [200, 404, 302] + register: xwiki_oidc_ext + when: XWIKI_OIDC_ENABLED | bool + no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" + +- name: "Check if LDAP extension installed" + uri: + url: "{{ XWIKI_REST_XWIKI }}/extensions/{{ XWIKI_EXT_LDAP_ID | urlencode }}" + method: GET + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: yes + status_code: [200, 404, 302] + register: xwiki_ldap_ext + when: XWIKI_LDAP_ENABLED | bool + no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" + +- name: "Install LDAP and/or OIDC extensions" + uri: + url: "{{ XWIKI_REST_EXTENSION_INSTALL }}" + method: PUT + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + 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) + no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" \ No newline at end of file diff --git a/roles/web-app-xwiki/tasks/_flush_config.yml b/roles/web-app-xwiki/tasks/_flush_config.yml new file mode 100644 index 00000000..2ef129f2 --- /dev/null +++ b/roles/web-app-xwiki/tasks/_flush_config.yml @@ -0,0 +1,25 @@ +- name: "Render xwiki.cfg" + template: + src: "xwiki.cfg.j2" + dest: "{{ XWIKI_HOST_CONF_PATH }}" + notify: docker compose up + +- name: "Deploy 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: "{{ XWIKI_REST_BASE }}" + status_code: [200, 401, 302] + return_content: no + changed_when: false + register: xwiki_rest_up + retries: 60 + delay: 5 + until: xwiki_rest_up is succeeded \ No newline at end of file diff --git a/roles/web-app-xwiki/templates/xwiki.cfg.j2 b/roles/web-app-xwiki/templates/xwiki.cfg.j2 index 69f8d43f..3d817e01 100644 --- a/roles/web-app-xwiki/templates/xwiki.cfg.j2 +++ b/roles/web-app-xwiki/templates/xwiki.cfg.j2 @@ -1,7 +1,7 @@ # ---- Authentication selection -{% if XWIKI_OIDC_ENABLED | bool %} +{% if xwiki_oidc_enabled_switch | bool %} xwiki.authentication.authclass=org.xwiki.contrib.oidc.auth.OIDCAuthServiceImpl -{% elif XWIKI_LDAP_ENABLED | bool %} +{% elif xwiki_ldap_enabled_switch | bool %} xwiki.authentication.authclass=org.xwiki.contrib.ldap.XWikiLDAPAuthServiceImpl xwiki.authentication.ldap=1 xwiki.authentication.ldap.trylocal={{ (XWIKI_LDAP_TRYLOCAL | bool) | ternary(1, 0) }} @@ -15,9 +15,9 @@ 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 +xwiki.authentication.authclass=com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl {% endif %} - +{% if xwiki_superadmin_enabled_switch | bool %} # ---- Superadmin must live in xwiki.cfg (not in xwiki.properties) xwiki.superadminpassword={{ XWIKI_SUPERADMIN_PASSWORD }} +{% endif %} diff --git a/roles/web-app-xwiki/templates/xwiki.properties.j2 b/roles/web-app-xwiki/templates/xwiki.properties.j2 index e310a103..9e67f7a8 100644 --- a/roles/web-app-xwiki/templates/xwiki.properties.j2 +++ b/roles/web-app-xwiki/templates/xwiki.properties.j2 @@ -1,6 +1,6 @@ ############################################ # OIDC -{% if XWIKI_OIDC_ENABLED | bool %} +{% if xwiki_oidc_enabled_switch | bool %} oidc.provider={{ XWIKI_OIDC_PROVIDER }} oidc.endpoint.authorization={{ XWIKI_OIDC_AUTHORIZATION }} oidc.endpoint.token={{ XWIKI_OIDC_TOKEN }} diff --git a/roles/web-app-xwiki/vars/main.yml b/roles/web-app-xwiki/vars/main.yml index b96470fc..050a7ea9 100644 --- a/roles/web-app-xwiki/vars/main.yml +++ b/roles/web-app-xwiki/vars/main.yml @@ -37,8 +37,9 @@ XWIKI_SUPERADMIN_PASSWORD: "{{ applications | get_app_conf(applicatio XWIKI_SUPERADMIN_USERNAME: "superadmin" # REST endpoint (local inside container) -XWIKI_REST_BASE: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/jobs?jobType=install&async=false" -XWIKI_REST_GENERAL: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/wikis/xwiki" +XWIKI_REST_BASE: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/xwiki/rest/" +XWIKI_REST_EXTENSION_INSTALL: "{{ XWIKI_REST_BASE }}jobs?jobType=install&async=false" +XWIKI_REST_XWIKI: "{{ XWIKI_REST_BASE }}wikis/xwiki" # Extension IDs + Versions (pin versions explicitly) XWIKI_EXT_LDAP_ID: "org.xwiki.contrib.ldap:ldap-authenticator"