openova/platform/wordpress-tenant/blueprint.yaml
e3mrah 3fe27f625f
feat(bp-wordpress-tenant): wp-cli OIDC bootstrap + oidc.* canonical block (0.2.0, #915) (#927)
Umbrella issue #915 (D1 sub-task). Aligns the chart's post-install OIDC
config Job with the canonical wp-cli flow and the bp-keycloak tenant-
realm contract C1's PR #918 ships.

Chart 0.2.0
-----------
- templates/oidc-config-job.yaml rewritten to use the official
  wordpress:cli-2.12.0-php8.3 image (manifest-list digest pinned per
  Inviolable Principle #4). Replaces direct PHP/SQL UPSERTs against
  wp_options with:
    * wp core install (idempotent: wp core is-installed)
    * wp plugin install openid-connect-generic --activate (idempotent:
      wp plugin is-installed)
    * wp option update openid_connect_generic_settings <json>
    * wp option update default_role
    * wp theme install/activate
    * wp option update siteurl/home
  Going through wp-cli (i.e. WordPress core's own PHP API) is more
  resilient than schema-shape-dependent INSERT statements and survives
  WordPress minor upgrades.

- values.yaml: new canonical oidc.* block —
    oidc.{enabled, issuerURL, clientId, clientSecretName, defaultRole,
          identityKey, roleMapping, cliImage}.
  Default oidc.clientSecretName = "wordpress-oidc-client-secret"
  matches the K8s Secret bp-keycloak's PR #918 emits alongside the
  realm import ConfigMap (so the realm JSON's `secret` field and the
  Secret bytes never drift).

- Legacy keycloak.{realmURL, clientID, clientSecretName} kept as a
  back-compat alias. _helpers.tpl folds it into oidc.* when the
  modern keys are at their values.yaml defaults so chart 0.1.x
  clusters keep reconciling. Removed in chart 0.3.0.

- oidc.defaultRole=subscriber — newly auto-created SSO users land
  with subscriber capability (operator overrides via overlay).

- Redirect URIs: the openid-connect-generic plugin's default callback
  is /wp-admin/admin-ajax.php?action=openid-connect-authorize when
  alternate_redirect_uri=0 (we set 0). bp-keycloak (PR #918)
  registers the same URL plus /wp-login.php and a /* wildcard, so the
  client's allowed-redirect-URI list aligns with what the plugin
  actually issues.

Orchestrator emit
-----------------
- products/catalyst/bootstrap/api/internal/handler/sme_tenant_gitops.go
  smeTenantBPWordPress now emits the canonical oidc.* block AND the
  legacy keycloak.* alias (for chart 0.1.x clusters mid-upgrade).

Tests
-----
- chart/tests/oidc-config.sh — 7 helm-template assertions:
    1. Canonical oidc.* render produces a Job with the required
       wp-cli command flow + wordpress:cli-2.12.0-php8.3 image.
    2. Legacy keycloak.* fold path (chart 0.1.x compat).
    3. oidc.enabled=false short-circuits the Job.
    4. alternate_redirect_uri=0 (so plugin URL matches the realm-
       registered redirect URI from PR #918).
    5. defaultRole rendered + propagated.
    6. Render YAML is parseable and contains all required kinds.
    7. wp-content PVC mounted in the Job (so pg4wp's db.php drop-in
       loads — failure here would silently fall back to mysqli).

- internal/handler/sme_tenant_test.go:
    * TestRenderSMETenantOverlay_WordPressEmitsOIDC — pins the
      canonical oidc.* block + legacy keycloak.* alias the
      orchestrator emits for the alice@omantel test fixture.
    * TestRenderSMETenantOverlay_WordPressOIDC_BYOMode — BYO domain
      mode renders wordpress.<byo-domain> as the ingress host.

Verification
------------
- helm lint clean
- helm template smoke green for: oidc.* canonical, keycloak.* legacy
  fold, oidc.enabled=false short-circuit
- chart/tests/oidc-config.sh: 7/7 PASS
- chart/tests/observability-toggle.sh: 2/2 PASS (regression)
- go test ./internal/handler/ -run "SMETenant|TestRenderSME": all
  green (TestAuthHandover_HappyPath failure is pre-existing on main,
  unrelated to this change)

Closes (D1 sub-task) of #915.

Co-authored-by: hatiyildiz <hatice@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:10:41 +04:00

174 lines
5.9 KiB
YAML

apiVersion: catalyst.openova.io/v1alpha1
kind: Blueprint
metadata:
name: bp-wordpress-tenant
labels:
catalyst.openova.io/category: tenant-app
catalyst.openova.io/section: pts-7-sme-tenant
spec:
version: 0.2.0
card:
title: WordPress Tenant
summary: |
Turnkey, SSO-pre-wired WordPress per SME tenant. Runs in the SME's
vcluster, namespace = SME tenant namespace. SSO via the SME-vcluster
Keycloak realm (openid-connect-generic plugin), Postgres via bp-cnpg
(Cluster CR in tenant ns), persistent /var/www/html/wp-content via
PVC (default 10Gi), ingress at https://wordpress.<sme-domain> with
cert-manager TLS. Default theme + admin user pre-seeded at install
time so the SME admin's first browser hit lands on /wp-admin
authenticated. No install wizard, no manual config.
icon: wordpress.svg
category: tenant-app
tags: [wordpress, cms, sme, tenant, sso, keycloak, oidc, postgres]
documentation: https://wordpress.org/documentation/
license: GPL-2.0-or-later
visibility: listed
owner:
team: platform
contact: catalyst@openova.io
configSchema:
type: object
required: [smeDomain, oidc, adminUser]
properties:
smeDomain:
type: string
description: |
The SME tenant's domain (e.g. `acme.<otech-fqdn>` for free-
subdomain tenants, or `acme.com` for BYO). Used to derive the
default ingress host as `wordpress.<smeDomain>`. Per
docs/INVIOLABLE-PRINCIPLES.md #4 there is no default — the
tenant-provisioning pipeline supplies this at install time.
replicas:
type: integer
default: 1
minimum: 1
maximum: 8
oidc:
type: object
required: [issuerURL, clientSecretName]
properties:
enabled:
type: boolean
default: true
description: Master toggle — when false the post-install Job is rendered but short-circuits.
issuerURL:
type: string
description: |
Discovery URL of the per-tenant Keycloak realm. Example:
`https://keycloak.<sme-domain>/realms/sme-<subdomain>`.
The openid-connect-generic plugin uses this to derive the
token / userinfo / authorization / end-session endpoints.
clientId:
type: string
default: wordpress
description: OIDC client ID registered in the per-tenant realm.
clientSecretName:
type: string
default: wordpress-oidc-client-secret
description: |
K8s Secret carrying the OIDC client secret (key
`client-secret`). Provisioned by the bp-keycloak chart
(PR #918) at the same time as the realm import.
defaultRole:
type: string
default: subscriber
description: |
WordPress role assigned to a brand-new SSO user when
`create_if_does_not_exist=1`. Set to empty string to
create SSO users with no role.
identityKey:
type: string
default: preferred_username
description: Keycloak claim used to match id_token back to a WP user.
keycloak:
type: object
description: |
DEPRECATED — preserved as a backward-compat alias for chart
0.1.x clusters whose orchestrator overlays still emit
`keycloak.{realmURL,clientID,clientSecretName}`. New overlays
MUST emit `oidc.*`. Folded into oidc.* by _helpers.tpl when
oidc.* is at its values.yaml defaults.
properties:
realmURL:
type: string
clientID:
type: string
clientSecretName:
type: string
database:
type: object
properties:
cnpgClusterName:
type: string
default: wordpress-db
description: |
Name of the `Cluster.postgresql.cnpg.io` provisioned for
this WordPress instance. Per-tenant unique within the SME
namespace.
adminUser:
type: object
required: [email]
properties:
email:
type: string
description: |
Email of the SME admin (must match the `email` claim
Keycloak issues). The admin-user Job pre-seeds a wp_user
row with this email and the administrator role.
displayName:
type: string
description: Display name shown in the WP admin bar. Defaults to the local-part of the email.
defaultTheme:
type: string
default: twentytwentyfive
description: WordPress theme slug installed + activated at first install.
persistence:
type: object
properties:
wpContent:
type: object
properties:
size:
type: string
default: 10Gi
storageClass:
type: string
default: local-path
ingress:
type: object
properties:
host:
type: string
description: Override the derived `wordpress.<smeDomain>` ingress host.
tls:
type: object
properties:
issuer:
type: string
default: letsencrypt-prod
description: cert-manager ClusterIssuer name.
placementSchema:
modes: [single-region]
default: single-region
manifests:
chart: ./chart
depends:
- blueprint: bp-cnpg
version: ^1.0
alias: db
- blueprint: bp-keycloak
version: ^1.0
alias: idp
- blueprint: bp-reflector
version: ^1.0
alias: reflector
- blueprint: bp-cert-manager
version: ^1.0
alias: tls
upgrades:
from: ["0.x"]
observability:
metrics: prometheus
logs: stdout