From 27656916db3049110cf246afa861698f2a119310 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 9 Aug 2026 01:50:52 +0200 Subject: [PATCH] Add KeyCape client registration for coulomb.social Idempotent patch of sso/keycape-config with public PKCE client and redirect URIs for local :8008 and production coulomb.social callbacks. --- .../k8s/keycape/register-coulomb-social.py | 64 +++++++++++++++++++ .../k8s/keycape/register-coulomb-social.sh | 20 ++++++ 2 files changed, 84 insertions(+) create mode 100755 sso-mfa/k8s/keycape/register-coulomb-social.py create mode 100755 sso-mfa/k8s/keycape/register-coulomb-social.sh diff --git a/sso-mfa/k8s/keycape/register-coulomb-social.py b/sso-mfa/k8s/keycape/register-coulomb-social.py new file mode 100755 index 0000000..044816f --- /dev/null +++ b/sso-mfa/k8s/keycape/register-coulomb-social.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Idempotently register coulomb.social in the live KeyCape Secret. + +The complete Secret travels over stdin/stdout between kubectl and this +process. Secret values are never printed to the terminal or written to disk. +""" + +from __future__ import annotations + +import base64 +import json +import sys + +import yaml + +CLIENT_ID = "coulomb-social" +CLIENT = { + "clientId": CLIENT_ID, + "displayName": "coulomb.social", + "redirectUris": [ + "http://127.0.0.1:8008/auth/callback/", + "http://localhost:8008/auth/callback/", + # Production host (register only when TLS + app deploy ready) + "https://coulomb.social/auth/callback/", + ], + "allowedScopes": ["openid", "profile", "email", "groups"], + "grantTypes": ["authorization_code"], + "clientType": "public", +} + + +def main() -> None: + secret = json.load(sys.stdin) + encoded = secret.get("data", {}).get("config.yaml") + if not encoded: + raise SystemExit("keycape-config does not contain config.yaml") + config = yaml.safe_load(base64.b64decode(encoded)) + clients = config.setdefault("clients", []) + existing = next( + ( + index + for index, client in enumerate(clients) + if client.get("clientId") == CLIENT_ID + ), + None, + ) + if existing is None: + clients.append(CLIENT) + else: + # Preserve any extra fields operators may have set; overwrite known keys. + merged = dict(clients[existing]) + merged.update(CLIENT) + clients[existing] = merged + rendered = yaml.safe_dump(config, sort_keys=False).encode() + secret["data"]["config.yaml"] = base64.b64encode(rendered).decode() + secret.pop("status", None) + metadata = secret.get("metadata", {}) + for key in ("creationTimestamp", "managedFields", "resourceVersion", "uid"): + metadata.pop(key, None) + json.dump(secret, sys.stdout, separators=(",", ":")) + + +if __name__ == "__main__": + main() diff --git a/sso-mfa/k8s/keycape/register-coulomb-social.sh b/sso-mfa/k8s/keycape/register-coulomb-social.sh new file mode 100755 index 0000000..09c6864 --- /dev/null +++ b/sso-mfa/k8s/keycape/register-coulomb-social.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Register coulomb.social as a public KeyCape OIDC client (idempotent). +# Never prints Secret values. +set -euo pipefail + +NAMESPACE="${KEYCAPE_NAMESPACE:-sso}" +SECRET="${KEYCAPE_CONFIG_SECRET:-keycape-config}" +KUBECTL="${KUBECTL:-kubectl}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +"$KUBECTL" get secret "$SECRET" -n "$NAMESPACE" -o json \ + | python3 "$SCRIPT_DIR/register-coulomb-social.py" \ + | "$KUBECTL" apply -f - + +echo "Registered coulomb-social client in $NAMESPACE/$SECRET" + +# KeyCape loads config at process start. +"$KUBECTL" -n "$NAMESPACE" rollout restart deploy/keycape +"$KUBECTL" -n "$NAMESPACE" rollout status deploy/keycape --timeout=120s +echo "KeyCape rolled out."