Files
computer-playbook/roles/web-app-wordpress/files/mu-plugins
kevinveenbirkenbachandClaude Opus 4.7 d009a35116 feat(rbac): hierarchical /roles/<app>/<role> + service-gated Playwright + Discourse round-trip
Implements requirements 005, 006, and 007.

005: per-application Keycloak `group-ldap-mapper`s anchor each app's
role groups under `/roles/<application_id>/<role_name>` (and, for
tenant-aware apps, `/roles/<application_id>/<tenant>/<role_name>`).
The flat-CN LDAP layout keeps each CN globally unique so Keycloak's
group-tree resolver never collides; per-app mappers use the LDAP
`description` as the visible Keycloak group name to keep the path
free of redundant prefixes. Migration purges legacy pre-005 entries
and forces a Keycloak `logout-all` so stale tokens drop pre-005
group paths.

006: per-service `<SERVICE>_SERVICE_ENABLED` env flags and the
`service-gating.js` helper let role specs `skipUnlessServiceEnabled`
cleanly when a shared service is missing or `SERVICES_DISABLED` is
set; `MODE_CI` markers (`INFINITO_MAKE_DEPLOY`, `GITHUB_ACTIONS`,
`ACT`) propagate through the deploy CLI into the test stage.

007: WordPress->Discourse round-trip Playwright scenario gated on the
`web-app-discourse` deployment.

WordPress Playwright runs 5/5 green on the single-site flow; the
multisite + Discourse scenarios are correctly skipped when their
service prerequisites are absent. The `rbac_group_path` lookup is the
single sanctioned producer for OIDC group paths and is wired into
every consumer (yourls, lam, minio, openwebui, pgadmin, phpldapadmin,
phpmyadmin, prometheus, xwiki).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 08:51:01 +02:00
..

WordPress Must-Use Plugins 🔒

Scope 🎯

This directory holds PHP files that the web-app-wordpress role copies into the running container at /var/www/html/wp-content/mu-plugins/.

mu-plugins is a WordPress core convention for must-use plugins. WordPress automatically loads every top-level *.php file in that directory before normal plugins, without a database activation record. As a result, code shipped here:

  • MUST be loaded on every request as soon as the file is present.
  • MUST NOT be deactivated from the Plugins -> Must-Use admin screen.
  • MUST NOT be placed in a subdirectory, because WordPress does not recurse into mu-plugins.

See the WordPress Must-Use Plugins handbook for the upstream contract.

When to add code here 📋

Put a file in this directory only when the behavior it implements is part of the security or integration contract of this role and MUST remain in effect for every request. Everyday feature plugins SHOULD stay in normal wp-content/plugins/ and be managed through 03_enable_plugin.yml so operators can disable them per site if needed.

Each file in this directory SHOULD declare its purpose, hooks, and source requirement at the top of the file so readers can understand why it cannot be switched off.

OIDC -> RBAC mapping (infinito-oidc-rbac-mapper.php) 🎫

infinito-oidc-rbac-mapper.php is the only file shipped here today. It implements the OIDC -> WordPress role contract from requirements 004 and 005:

  • Single-Site path: the claim MUST contain /roles/web-app-wordpress/<role> entries. The highest-privilege role across all matches wins (administrator > editor > author > contributor > subscriber). When no entry matches, the user's role is set to subscriber as a deterministic fallback.
  • Multisite path (auto-detected via is_multisite()): per-site roles come from /roles/web-app-wordpress/<canonical-domain>/<role> entries; the super-admin capability comes from /roles/web-app-wordpress/network-administrator. The mapper adds the user to any site they have a role for (add_user_to_blog) and removes them from sites it previously added them to (remove_user_from_blog) when a role for that site disappears from the claim. A user-meta marker (_infinito_oidc_added_blog_ids) records which blog memberships the mapper owns, so memberships added through wp-admin or the REST API outside the OIDC flow are never touched.

For the broader RBAC contract (LDAP layout, rbac.tenancy schema, the rbac_group_path lookup plugin), see rbac.md.

Deployment 🚚

Files in this directory are copied into the running container by 05_mu_plugins.yml. The task runs on every deploy, so dropping a new file here is enough to have it picked up on the next playbook run.

Credits 🙏

Developed and maintained by Kevin Veen-Birkenbach. Learn more at veen.world. Part of the Infinito.Nexus Project. Licensed under the Infinito.Nexus Community License (Non-Commercial).