openova/platform/keycloak/blueprint.yaml
e3mrah 7f859dbb4b
feat(bp-keycloak): tenant-mode realm with wordpress/openclaw/stalwart OIDC clients (1.4.0, #915) (#918)
PR #911 wired the SME tenant orchestrator to emit
realmConfig.tenant.enabled=true on the per-tenant bp-keycloak
HelmRelease — but the chart had no template that consumed those values,
so the WordPress / OpenClaw / Stalwart OIDC integrations had no client
registered in the tenant realm and SSO failed end-to-end.

This change adds the chart-side template the orchestrator was already
emitting for. When realmConfig.tenant.enabled=true:

  * configmap-sovereign-realm.yaml SKIPS (mutual-exclusion guard added
    on the existing template) so only one realm CM is rendered.
  * NEW templates/configmap-tenant-realm.yaml renders a realm import
    ConfigMap (same name `<release>-sovereign-realm-config` so the
    upstream keycloak-config-cli existingConfigmap reference still
    resolves) carrying the tenant realm + 3 OIDC clients:
      - wordpress  (confidential, auth-code; redirect URIs cover the
                    openid-connect-generic plugin's admin-ajax.php
                    callback + /wp-login.php fallback)
      - openclaw   (confidential, auth-code; redirect URI /oauth/callback
                    per #915 spec)
      - stalwart   (confidential, serviceAccountsEnabled=true so the
                    directory.keycloak type=oidc backend can use
                    client_credentials to introspect IMAP/SMTP tokens;
                    standardFlowEnabled=true for webmail UI auth-code)
  * NEW per-app Secrets emitted in the same template scope as the realm
    ConfigMap so the realm JSON's `secret` field and the K8s Secret
    bytes never drift:
      - wordpress-oidc-client-secret
      - openclaw-oidc-client-secret
      - stalwart-oidc-client-secret  (carries BOTH client-secret AND
                                      OIDC_CLIENT_SECRET keys for the
                                      two consumer paths)
  * Each per-app secret persists across helm upgrade via
    lookup-or-generate (mirrors marketplace-api/secret.yaml pattern from
    issue #887 and the existing catalyst-api-server secret in
    configmap-sovereign-realm.yaml). helm.sh/resource-policy: keep so
    bytes outlive uninstall.
  * Fail-closed validation when realmConfig.tenant.enabled=true and
    any of realmName / parentDomain / subdomain is unset (Inviolable
    Principle #4).

NEW tests/tenant-realm-oidc-clients.sh covers 6 cases:
  1. Sovereign-mode default render unchanged (kubectl + catalyst-ui +
     catalyst-api-server clients present, no tenant artefacts leak).
  2. Tenant-mode render produces exactly ONE realm CM under the
     expected name + zero leaked Sovereign-only resources.
  3. Tenant realm JSON parses + 3 OIDC clients present with the
     redirect-URI / publicClient / serviceAccountsEnabled shape per
     #915 spec; Secret bytes match realm JSON's `secret` fields.
  4. Fail-closed validation when tenant fields missing.
  5. keycloak-config-cli post-install Job projects the realm CM by
     SAME name in BOTH modes.
  6. Operator-supplied per-app clientSecret overrides the
     lookup-or-generate path.

Existing tests/observability-toggle.sh + tests/oidc-kubectl-client.sh
still pass.

Sovereign-mode unchanged. The chart now consumes the values the
orchestrator (PR #911) was already emitting; no orchestrator change
needed.

Closes #915 (C1 sub-task) and unblocks #899 (per-tenant Keycloak
realm-config materialisation).

Co-authored-by: hatiyildiz <hatiyildiz@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 13:29:40 +04:00

16 lines
513 B
YAML

apiVersion: catalyst.openova.io/v1alpha1
kind: Blueprint
metadata:
name: bp-keycloak
labels:
catalyst.openova.io/section: pts-2-3-per-sovereign-supporting-services
spec:
version: 1.4.0
card:
title: keycloak
summary: Keycloak — user identity. Topology decided by Sovereign CRD spec.keycloakTopology (per-organization for SME, shared-sovereign for corporate).
visibility: unlisted # mandatory infra, auto-installed by bootstrap kit
manifests:
chart: ./chart
depends: []