Commit Graph
19 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 4491ef688c fix(web-app-joomla): let the admin log in after a fresh install
Two installers write the same database and neither reconciles the admin
credential, so `/administrator` answered every login with "Username and
password do not match" — six POSTs across two orchestrator passes, all 303, in
the only CI row that exercises the local-login hatch. The green sibling row
proves nothing: its JUnit report lists that test as skipped.

The image entrypoint auto-installs Joomla on first boot. The role waited for
`plugins/actionlog/joomla/joomla.xml`, a file copied early in that boot, then
removed configuration.php and ran its own CLI install against the same
database. Whichever finished last owned the admin row, and the run's artifacts
show the entrypoint finishing one second after the play ended.

So the role now waits for the entrypoint to be done before deciding anything.
The marker it already writes, `.infinito-init.ready`, is that signal; the
`|| test -e configuration.php` arm covers re-deploys, where the entrypoint's
setup block never runs and the marker is therefore never touched.

With the state settled, the password reset no longer needs a gate. It was
skipped on fresh installs on the assumption that the CLI installer had set the
password — an assumption this run falsifies, since that installer exits 1 and
is swallowed as a soft success. The race it was written to avoid ("The user
does not exist!" right after install) is absorbed inside the reset itself,
which retries six times at five-second intervals and whose header documents
exactly that symptom.

The marker's name and path now live only in vars/main.yml and reach the
container through the env file. The entrypoint requires it without a default,
matching svc-runner and svc-db-openldap: a fallback would be the second
definition this change exists to remove.

Verified: the guarded expansion executed in both env states (127 with a legible
message, 0 with the path), the retry-exhaustion path executed against localhost
to confirm `failed_when: false` lets the play continue, plus shellcheck,
ansible-lint on the production profile, and 201 Jinja/ansible lint tests.

Not verified: no deploy was run. Whether the barrier holds on a real fresh
install is unmeasured.

Still open, both separate defects: 03_install.yml swallows the installer's
rc 1 as success, and test-oidc-login.js passes on the plugin's catch path
because it only asserts the URL prefix and a visible body.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 01:31:11 +02:00
kevinveenbirkenbachandClaude Opus 5 238b8b41fa fix(joomla): route server-side OIDC discovery through the Tor SOCKS proxy
Joomla's Keycloak plugin builds a jumbojett OpenIDConnectClient, which bottoms
out in libcurl — and libcurl refuses a .onion host at name resolution, before it
ever consults /etc/hosts, extra_hosts or DNS (RFC 7686). 20ddda8c1 measured that
verbatim: `curl --noproxy '*'` exits 6, and even
`--resolve auth.<onion>:80:127.0.0.1` exits 6. Only a configured proxy makes curl
skip the check.

So the `onion_oidc_socks.yml.j2` include the role already carries is a no-op for
this client: its extra_hosts entry can never be reached. Server-side discovery
against an onion IdP could not work, whatever the scheme — this class is
unaffected by TLS on the onion, which is why it needs its own fix.

buildOidcClient now calls setHttpProxy when JOOMLA_OIDC_SOCKS_PROXY is set, and
env.j2 sets it only when the issuer is an .onion, so a clearnet deployment is
untouched and never routes discovery through Tor. web-app-nextcloud is the
working reference for the same construction. The swarm side needs nothing: the
onion_sso overlay is declared provider-side in svc-net-tor/meta/networks.yml and
attaches to every app with sso enabled.

The six narration comments that fell out of Keycloak.php were pre-existing and
lost their grandfathering when the file was touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 21:55:45 +02:00
kevinveenbirkenbachandClaude Opus 4.8 15e7647d2f refactor(env): central TZ+LANG base included by every role env.j2
New roles/sys-svc-container/templates/env.j2 sets TZ={{ HOST_TIMEZONE }} and LANG=C.UTF-8. Every role env.j2 includes it at the top; it renders via stack_host_template, the same engine that resolves the includes in compose.yml.j2, so it works in both compose and swarm. TZ/LANG are removed from the individual role env.j2 so each value stays single-source.

New lint tests/lint/ansible/test_env_base_include.py verifies every role env.j2 includes the base and never re-sets a base-owned key (keys derived from the base file).

mattermost and gitea meta/volumes.yml drop the /etc/timezone host-path bind: swarm hard-rejects a task whose bind source is missing on the node (Debian-trixie nodes have no /etc/timezone). TZ now comes from the base env. /etc/localtime bind also dropped; the container timezone follows TZ.

Non-directive comments were removed from the touched env.j2 (banners, restatements, reference URLs, narration) so the changed files pass the comment-lint; only tool directives remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 22:13:57 +02:00
kevinveenbirkenbachandClaude Opus 4.8 62ecbafecb feat(joomla): migrate keycloak plugin to meta/addons (req 026)
plg_system_keycloak declared as mechanism: plugin, source: built, bridges [sso]; env.j2 reads the addon config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:30:46 +02:00
kevinveenbirkenbachandClaude Opus 4.7 24d4ab8e4a refactor(sso)\!: collapse oauth2 + oidc into unified services.sso block (req 21)
Implements docs/requirements/021-sso-flavor-migration.md atomically:

* Schema: every role's meta/services.yml replaces the parallel
  `oauth2:` / `oidc:` top-level blocks with a single `sso:` block
  carrying `enabled`, `shared`, and a `flavor` discriminator
  (`oidc` | `oauth2` | `saml`; default `oidc`). Flavor-specific
  sub-keys live under `sso.<flavor>.*` (`sso.oauth2.{origin,acl,
  allowed_groups}`, `sso.oidc.plugin`).
* Provider role: roles/web-app-oauth2-proxy/ is deleted in its
  entirety; its 5 sidecar templates move under
  roles/web-app-keycloak/templates/sso_proxy/, and the per-consumer
  rendering task lives at roles/web-app-keycloak/tasks/sso_proxy.yml.
  web-app-keycloak now declares `provides: sso`.
* Runtime: new resolver utils/roles/applications/services/sso.py
  centralises the SSO predicates (`is_enabled`, `is_proxy_gated`,
  `is_oidc_native`) and the oauth2 sub-values
  (`oauth2_origin_{host,port}`, `oauth2_acl`, `oauth2_allowed_groups`).
  Three lookup plugins consume the resolver: `sso` (single-app
  predicate access), `sso_proxy_consumers` (enumerates oauth2-flavored
  consumers), `sso_oidc_plugin` (renamed from `oidc_flavor`, Nextcloud
  plugin selector). sys-stk-backend, sys-svc-compose, sys-svc-proxy
  and plugins/filter/compose_volumes.py all gate on
  `lookup('sso', ..., 'is_proxy_gated')` instead of manually combining
  `enabled` and `flavor`.
* Identifier renames repo-wide: `services.<entity>.ports.local.oauth2`
  → `ports.local.sso`; `PORT_BANDS.local.oauth2` → `PORT_BANDS.local.sso`;
  `OAUTH2_PROXY_*` env/Jinja vars → `SSO_PROXY_*`;
  `oauth2_proxy_cookie_secret` → `sso_proxy_cookie_secret`;
  `OAUTH2_SERVICE_ENABLED` + `OIDC_SERVICE_ENABLED` collapse into
  `SSO_SERVICE_ENABLED`; Playwright gating helpers
  (isServiceEnabled/skipUnlessServiceEnabled/requireService/safeIsEnabled/
  safeSkipUnlessEnabled) move from `oidc`/`oauth2` to `sso`.
* Dual-block resolution: per req's Background-Table, web-app-bookwyrm
  is `flavor: oauth2`; web-app-gitea and web-app-friendica keep
  `flavor: oauth2` with their existing `oauth2.acl.blacklist`
  (/user/login) and `oauth2.acl.whitelist` (federation + discovery
  endpoints) — preserving the operative oauth2-proxy gating.
* Tests: the two mutual-exclusion guards
  (tests/integration/iam/oauth2_oidc/test_mutual_exclusive.py and
  test_acl_mutual_exclusive.py) are deleted per Decision #10;
  test_oauth2_contract.py is renamed and rewritten to
  test_sso_contract.py, asserting that `flavor: oauth2` roles carry
  `oauth2.origin.{host,port}` plus `ports.local.sso`; test_sso.py
  (web-app integration) now requires the unified `sso:` block with an
  explicit flavor or a `# nocheck: sso` opt-out; new
  tests/lint/repository/no_legacy_sso_paths/ guards the source tree
  against re-introducing the legacy strings.
* Docs: req 21 itself has every AC checked off, and reqs 006, 008,
  009, 013, 015, 016, 019, 020 plus 10+ role READMEs and
  docs/contributing/actions/testing/suppression.md are updated to the
  unified SSO naming.

Empirically validated end-to-end against the validation app set
(web-app-keycloak, web-app-nextcloud, web-app-bookwyrm, web-app-gitea,
web-app-friendica, web-app-dashboard) in matrix variants 0 and 1; v2
also passes the 4 target roles' Playwright after a documented
pre-existing KC_HOSTNAME-drift workaround (separate concern, not
caused by this refactor).

BREAKING CHANGE: every consumer of `services.oauth2.*`,
`services.oidc.*`, `OAUTH2_PROXY_*`, `OAUTH2_SERVICE_ENABLED`,
`OIDC_SERVICE_ENABLED`, `oauth2_proxy_cookie_secret`,
`oauth2_proxy_application_id`, `ports.local.oauth2`, the
`web-app-oauth2-proxy` role, or the legacy gating helper service names
(`oidc`/`oauth2`) must migrate to the unified `sso` shape documented
in docs/requirements/021-sso-flavor-migration.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 12:59:59 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c55ad69c41 refactor: drop requirement-file references from code, add doc rule + lint
Strip every `req <NNN>`, `req-<NNN>`, `Requirement <NNN>`,
`requirement-<NNN>`, and `docs/requirements/<NNN>-...md` mention from
non-Markdown source files. Comments anchored to requirement numbers
rot when requirements are renumbered or rewritten and conflate
present-state behaviour (which the comment should describe) with the
audit trail of how the code got there (which belongs in commit
messages, PR descriptions, and the requirement file itself).

* Document the rule in `docs/contributing/documentation.md` under
  "Comments". Code MUST describe the *why* in present-state language;
  contract enforcement lives in tests, not folklore.
* Clean 254 source files across `roles/`, `tests/`, `plugins/`,
  `cli/`, `scripts/`, `utils/`, `group_vars/`, and the repo-root
  `eslint.config.js`. Pure comment / docstring / log-message edits;
  no behavioural change.
* Add `tests/lint/repository/test_no_req_references_in_code.py` to
  enforce the rule going forward across `.js`, `.py`, `.yml`,
  `.yaml`, `.j2`, and `.sh` files. The legacy
  `test_no_stale_requirement_refs.py` is exempt as the only file
  whose purpose is to detect these very tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:25:22 +02:00
kevinveenbirkenbachandClaude Opus 4.7 084e19202d feat(req-013): native OIDC for web-app-joomla via in-role plg_system_keycloak
Promotes web-app-joomla's OIDC integration from "documented future
work" to a working native plugin shipped inside the role.

* New `plg_system_keycloak` Joomla 6 plugin under
  `roles/web-app-joomla/files/joomla-oidc-plugin/`. The plugin's PHP
  dependency `jumbojett/openid-connect-php` is vendored at deploy time
  inside the Joomla container (Composer is baked into the role's
  custom image), then packaged as a ZIP and installed via the Joomla
  CLI. The repo working tree carries no `vendor/` tree.
* Modus 3 per requirement 013 (Force-Frontend, Local-Backup-Backend):
  every visit to `/` and `/administrator` redirects to Keycloak
  unless the request explicitly carries `?fallback=local` AND the
  env-var `JOOMLA_OIDC_FALLBACK_ENABLED` is `true`. The fallback hatch
  is env-toggleable so high-security deployments can flip to Modus 1
  (no local form).
* RBAC group mapping: the plugin maps the Keycloak `groups` claim
  onto Joomla's standard usergroup IDs:
    /roles/web-app-joomla/administrator -> Super Users (id 8)
    /roles/web-app-joomla/editor        -> Editor      (id 4)
    /roles/web-app-joomla               -> Registered  (id 2)
  Users whose Keycloak groups match none of these paths are refused.
* Matrix-deploy variants flip the auth-flow gate: variant 0 is the
  canonical OIDC variant (this commit's primary path), variant 1
  flips OIDC off and LDAP on so Joomla's core LDAP authentication
  plugin against svc-db-openldap is the exercised auth path.
* Role wiring:
  - `tasks/07_oidc_plugin.yml` builds + installs the plugin idempotently.
  - `templates/env.j2` propagates JOOMLA_OIDC_* env into the container.
  - `meta/services.yml` enables `oidc` with `fallback_enabled: true`.
  - `meta/rbac.yml` declares the `editor` role.
  - `templates/playwright.env.j2` keys `OIDC_SERVICE_ENABLED` off
    `services.oidc.enabled` for the Playwright test gate.
* Playwright e2e covers three scenarios:
  - native OIDC redirect from `/` to Keycloak and back to Joomla,
  - `/administrator?fallback=local` hatch bypassing Keycloak,
  - LDAP variant against the core Joomla LDAP plugin.
* Documentation:
  - `roles/web-app-joomla/README.md` rewritten per documentation.md
    and readme_md.md (software-focused description, no em-dashes,
    canonical Credits format).
  - `roles/web-app-joomla/files/joomla-oidc-plugin/README.md` covers
    plugin build, env contract, claimed routes, and watch points.
  - `docs/requirements/013-alpha-to-beta-promotion.md` joomla
    per-role-notes updated to reference the in-role plugin and the
    Modus 3 fallback contract.
* `.github/dependabot.yml` adds the composer ecosystem entry that
  covers the plugin's `composer.json`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 17:42:35 +02:00
kevinveenbirkenbach 5e33e70892 refactor(database lookup usage): switch conditionals to enabled flag
Replace indirect type!='' checks with explicit database.enabled lookups
to improve semantics, readability, and alignment with strict lookup API.

https://chatgpt.com/share/6985d642-b40c-800f-9cdf-ed54fc484a92
2026-02-06 13:25:37 +01:00
kevinveenbirkenbach f6f96a11ac Refactor: use database lookup helper everywhere
https://chatgpt.com/share/6983bcf6-85bc-800f-a0fc-5684b9078469
2026-02-04 22:52:08 +01:00
kevinveenbirkenbach 8b1ae03e77 fix(env): prevent dotenv_quote double-quoting in env.j2 templates
- Replace `"{{ ... | dotenv_quote }}"` with `{{ ... | dotenv_quote }}` across roles
- Update integration test to enforce pure Jinja + dotenv_quote and forbid wrapped quotes

https://chatgpt.com/share/6974e6c6-51b8-800f-80a1-a76f885cf57e
2026-01-24 16:35:27 +01:00
kevinveenbirkenbach fdb2683ddb test(env): enforce dotenv_quote + double quotes for password vars in env.j2 templates
https://chatgpt.com/share/6974cbac-023c-800f-8198-fd85df2aa7bc
2026-01-24 15:32:33 +01:00
kevinveenbirkenbach aa7d0c642c feat(dotenv): add dotenv_quote filter and enforce quoted password vars in env.j2
https://chatgpt.com/share/6974cbac-023c-800f-8198-fd85df2aa7bc
2026-01-24 15:22:19 +01:00
kevinveenbirkenbach 9f2cfe65af Remove non-functional Joomla LDAP integration
- Disabled LDAP feature flag (set to false by default, with comment)
- Removed ldapautocreate plugin (PHP + XML)
- Deleted LDAP helper tasks (01_ldap_files.yml, 05_ldap.yml, 07_diagnose.yml)
- Deleted LDAP CLI helper scripts (cli.php, diagnose.php, plugins.php, auth-trace.php)
- Removed LDAP configuration variables from vars/main.yml
- Removed LDAP environment variables from env.j2
- Removed LDAP-specific mounts from docker-compose.yml.j2
- Dropped php-ldap installation from Dockerfile
- Renamed task files for consistent numbering (02->01_install, 03->02_debug, 04->03_patch, 06->04_assert)

Reason: LDAP integration was removed because it was not functional.

Conversation: https://chatgpt.com/share/68b09373-7aa8-800f-8f2c-11e27123bad1
2025-08-28 19:36:12 +02:00
kevinveenbirkenbach fe399c3967 Added all LDAP changes before removing, because it doesn't work. Will trty to replace it by OIDC 2025-08-28 19:22:37 +02:00
kevinveenbirkenbach ef801aa498 Joomla: Add LDAP autocreate plugin support
- Introduced autocreate_users feature flag in config/main.yml
- Added ldapautocreate.php and ldapautocreate.xml plugin files
- Implemented tasks/01_ldap_files.yml for plugin deployment
- Added tasks/05_ldap.yml to configure LDAP plugin and register ldapautocreate
- Renamed tasks for better structure (01→02, 02→03, etc.)
- Updated cli-ldap.php.j2 for clean parameter handling
- Mounted ldapautocreate plugin via docker-compose.yml.j2
- Extended vars/main.yml with LDAP autocreate configuration

Ref: https://chatgpt.com/share/68b0802f-bfd4-800f-b10a-57cf0c091f7e
2025-08-28 18:13:53 +02:00
kevinveenbirkenbach 18f3b1042f feat(web-app-joomla): reliable first-run install, safe debug toggler, DB patching, LDAP scaffolding
Why
- Fix flaky first-run installs and make config edits idempotent.
- Prepare LDAP support and allow optional inline CSP for UI.
- Improve observability and guard against broken configuration.php.

What
- config/main.yml: enable features.ldap; add CSP flags (allow inline style/script elem); minor spacing.
- tasks/: split into 01_install (wait for core, absolute CLI path), 02_debug (toggle $debug/$error_reporting safely), 03_patch (patch DB creds in configuration.php), 04_ldap (configure plugin via helper), 05_assert (optional php -l).
- templates/Dockerfile.j2: conditionally install/compile php-ldap (fallback to docker-php-ext-install with libsasl2-dev).
- templates/cli-ldap.php.j2: idempotently enable & configure Authentication - LDAP from env.
- templates/docker-compose.yml.j2: build custom image when LDAP is enabled; mount cli-ldap.php; pull_policy: never.
- templates/env.j2: add site/admin vars, MariaDB connector/env, full LDAP env.
- vars/main.yml: default to MariaDB (mysqli), add JOOMLA_* vars incl. JOOMLA_CONFIG_FILE.

Notes
- LDAP path implemented but NOT yet tested end-to-end.
- Ref: https://chatgpt.com/share/68b068a8-2aa4-800f-8cd1-56383561a9a8.
2025-08-28 16:33:45 +02:00
kevinveenbirkenbach 03564b34bb Optimized reset routine for docker images and specially discourse 2025-08-15 21:35:45 +02:00
kevinveenbirkenbach 3ac9bd9f90 Optimized variable typos 2025-08-15 18:43:42 +02:00
kevinveenbirkenbach 563d5fd528 Huge role refactoring/cleanup. Other commits will propably follow. Because some bugs will exist. Still important for longrun and also for auto docs/help/slideshow generation 2025-07-08 23:43:13 +02:00