diff --git a/roles/web-app-xwiki/config/main.yml b/roles/web-app-xwiki/config/main.yml index 2ed20486..35407ca1 100644 --- a/roles/web-app-xwiki/config/main.yml +++ b/roles/web-app-xwiki/config/main.yml @@ -45,8 +45,8 @@ plugins: enabled: true items: - id: "org.xwiki.contrib.oidc:oidc-authenticator" - version: "2.19.2" - + version: "" + ldap: enabled: false items: diff --git a/roles/web-app-xwiki/filter_plugins/xwiki_filters.py b/roles/web-app-xwiki/filter_plugins/xwiki_filters.py index e28afa34..60de35c8 100644 --- a/roles/web-app-xwiki/filter_plugins/xwiki_filters.py +++ b/roles/web-app-xwiki/filter_plugins/xwiki_filters.py @@ -74,9 +74,35 @@ def xwiki_job_id(response: Any, default: Optional[str] = None, strict: bool = Fa return default +def xwiki_extension_status(raw: str) -> int: + """ + Parse the output of the Groovy CheckExtension page. + + - Strips HTML tags and entities ( ) + - Returns 200 if extension is INSTALLED, otherwise 404 + + Args: + raw: Raw HTTP body from the checker page. + + Returns: + 200 if installed, 404 if missing/unknown. + """ + if raw is None: + return 404 + + text = re.sub(r"<[^>]+>", "", str(raw)) + text = text.replace(" ", " ").replace("\u00A0", " ") + text = text.strip() + + if text.startswith("INSTALLED::"): + return 200 + return 404 + + class FilterModule(object): """Custom filters for XWiki helpers.""" def filters(self): return { "xwiki_job_id": xwiki_job_id, + "xwiki_extension_status": xwiki_extension_status, } diff --git a/roles/web-app-xwiki/tasks/01_core.yml b/roles/web-app-xwiki/tasks/01_core.yml index 42b0b1f4..b8b49501 100644 --- a/roles/web-app-xwiki/tasks/01_core.yml +++ b/roles/web-app-xwiki/tasks/01_core.yml @@ -15,6 +15,21 @@ xwiki_ldap_enabled_switch: false xwiki_superadmin_enabled_switch: true +- name: "ASSERT | superadmin can authenticate (needed for installer)" + uri: + url: "{{ [XWIKI_REST_XWIKI, 'spaces'] | url_join }}" + method: GET + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: true + status_code: [200] + register: _super_check_ext + +- name: "FAIL | superadmin authentication failed (extensions phase)" + fail: + msg: "superadmin authentication failed (check xwiki.cfg in image / password / Dockerfile build)" + when: _super_check_ext.status != 200 + - name: Load setup procedures for admin include_tasks: 03_administrator.yml when: not (XWIKI_SSO_ENABLED | bool) diff --git a/roles/web-app-xwiki/tasks/04_extensions.yml b/roles/web-app-xwiki/tasks/04_extensions.yml index 4ab7d908..e452fdba 100644 --- a/roles/web-app-xwiki/tasks/04_extensions.yml +++ b/roles/web-app-xwiki/tasks/04_extensions.yml @@ -57,6 +57,39 @@ delay: 15 until: _exec_page is succeeded +- name: "XWIKI | Verify requested extensions via Groovy checker" + include_tasks: _check_extension_via_groovy.yml + loop: "{{ XWIKI_PLUGINS }}" + loop_control: + loop_var: plugin + label: "{{ plugin.id }}" + vars: + ext_id: "{{ plugin.id }}" + result_var: "probe_{{ plugin.id | regex_replace('[^A-Za-z0-9_]', '_') }}" + +- name: "XWIKI | Collect probe results" + set_fact: + _xwiki_probe_results: "{{ _xwiki_probe_results | default([]) + [ { + 'id': plugin.id, + 'status': ( + (hostvars[inventory_hostname]['probe_' ~ (plugin.id | regex_replace('[^A-Za-z0-9_]', '_'))] + | default({})).status + | default(404) | int + ) + } ] }}" + loop: "{{ XWIKI_PLUGINS }}" + loop_control: + loop_var: plugin + changed_when: false + +# Fail if any extension is missing +- name: "XWIKI | Assert all requested extensions are installed" + vars: + missing: "{{ _xwiki_probe_results | selectattr('status','equalto',404) | map(attribute='id') | list }}" + fail: + msg: "Missing extensions: {{ missing | join(', ') }}" + when: missing | length > 0 + - name: "XWIKI | Delete installer page" uri: url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}" diff --git a/roles/web-app-xwiki/tasks/_check_extension_via_groovy.yml b/roles/web-app-xwiki/tasks/_check_extension_via_groovy.yml new file mode 100644 index 00000000..d2c4b1ff --- /dev/null +++ b/roles/web-app-xwiki/tasks/_check_extension_via_groovy.yml @@ -0,0 +1,63 @@ +# PUT a temporary Groovy page that checks installed extensions +- name: "XWIKI | PUT checker page XWiki.CheckExtension" + uri: + url: "{{ [XWIKI_REST_XWIKI_PAGES, 'CheckExtension'] | url_join }}" + method: PUT + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: true + status_code: [200, 201, 202, 204] + headers: + Content-Type: "application/xml" + Accept: "application/xml" + body: | + + CheckExtension + + xwiki/2.1 + + register: _put_checker + changed_when: false + +# Call the page to check a single extension +- name: "XWIKI | Check installed via Groovy for {{ ext_id }}" + uri: + url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/bin/view/XWiki/CheckExtension?xpage=plain&id={{ ext_id | urlencode }}" + method: GET + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: true + status_code: [200] + return_content: yes + register: _check_output + changed_when: false + +- name: "XWIKI | Save Groovy check result for {{ ext_id }}" + set_fact: + "{{ result_var }}": + status: "{{ _check_output.content | xwiki_extension_status }}" + raw: "{{ _check_output.content }}" + +# Cleanup (optional; you can leave the page, but we remove it to keep things tidy) +- name: "XWIKI | Delete checker page" + uri: + url: "{{ [XWIKI_REST_XWIKI_PAGES, 'CheckExtension'] | url_join }}" + method: DELETE + user: "{{ XWIKI_SUPERADMIN_USERNAME }}" + password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" + force_basic_auth: true + status_code: [204, 200, 202, 404] + register: _delete_checker + changed_when: _delete_checker.status != 404 diff --git a/roles/web-app-xwiki/tasks/_probe_extension.yml b/roles/web-app-xwiki/tasks/_probe_extension.yml deleted file mode 100644 index dc97cade..00000000 --- a/roles/web-app-xwiki/tasks/_probe_extension.yml +++ /dev/null @@ -1,44 +0,0 @@ -# roles/web-app-xwiki/tasks/_probe_extension.yml -# Probes the 'installed' extension repository to check if a given extension is present. -# Uses the wiki-namespaced REST base (/rest/wikis/xwiki) and falls back to /{id}/{version} if needed. - -- name: "XWIKI | Probe extension {{ ext_id }} (installed repo)" - when: ext_enabled | bool - uri: - url: "{{ [XWIKI_REST_XWIKI, 'repositories/installed/extensions', ext_id | urlencode] | url_join }}?namespace={{ 'wiki:xwiki' | urlencode }}" - method: GET - user: "{{ XWIKI_SUPERADMIN_USERNAME }}" - password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" - force_basic_auth: true - follow_redirects: none - return_content: no - headers: - Accept: "application/xml" - status_code: [200, 401, 404] - register: _probe - changed_when: false - -# Some XWiki builds/versions answer on /{id}/{version}. Try that if plain /{id} returned 404. -- name: "XWIKI | Probe extension {{ ext_id }} with version (fallback)" - when: - - ext_enabled | bool - - (_probe.status | default(404)) | int == 404 - - ext_version is defined - uri: - url: "{{ [XWIKI_REST_XWIKI, 'repositories/installed/extensions', ext_id | urlencode, ext_version] | url_join }}?namespace={{ 'wiki:xwiki' | urlencode }}" - method: GET - user: "{{ XWIKI_SUPERADMIN_USERNAME }}" - password: "{{ XWIKI_SUPERADMIN_PASSWORD }}" - force_basic_auth: true - follow_redirects: none - return_content: no - headers: - Accept: "application/xml" - status_code: [200, 401, 404] - register: _probe_v - changed_when: false - -- name: "XWIKI | Save probe result for {{ ext_id }}" - when: ext_enabled | bool - set_fact: - "{{ result_var }}": "{{ (_probe_v if (_probe_v is defined) else _probe) }}"