Files
railiance-platform/helm/openbao-ui-overlay/overlay.js
tegwick cb45f29fb2 Fix OpenBao login falling back to token auth
Add synchronous redirect-bootstrap, direct KeyCape OIDC on sign-in, and mount
watching so the UI no longer lands on ?with=token when netkingdom is hidden
from unauthenticated mount listing. Document listing_visibility tune helper.
2026-06-19 21:04:31 +02:00

316 lines
8.3 KiB
JavaScript

(function () {
"use strict";
const PRESETS_URL = "/ui/platform-overlay/presets.json";
const MAX_APPLY_ATTEMPTS = 40;
const APPLY_INTERVAL_MS = 250;
const MOUNT_WATCH_MS = 500;
const MOUNT_WATCH_MAX = 24;
const DEFAULT_PRESETS = {
namespace: "",
method: "oidc",
mount: "netkingdom",
role: "platform-admin",
title: "Sign in with KeyCape",
signInLabel: "Sign in with KeyCape",
banner:
"Platform operators authenticate through KeyCape at kc.coulomb.social.",
};
let presets = { ...DEFAULT_PRESETS };
let applyAttempts = 0;
let applyTimer = null;
let mountWatchTimer = null;
let overlayApplied = false;
let signInHandlerInstalled = false;
function isAuthPage() {
const path = window.location.pathname;
return (
/\/ui\/vault\/auth(?:\/|$)/.test(path) ||
/\/ui\/?$/.test(path)
);
}
function normalizedMount(value) {
return (value || "").replace(/\/$/, "");
}
function desiredMount() {
return normalizedMount(presets.mount || "netkingdom");
}
function currentMountFromQuery() {
return normalizedMount(
new URLSearchParams(window.location.search).get("with") || ""
);
}
function ensureAuthMountSelected() {
const mount = desiredMount();
const current = currentMountFromQuery();
if (current === mount || current === "keycape") {
return false;
}
if (!isAuthPage() || window.location.pathname.includes("/oidc/")) {
return false;
}
const params = new URLSearchParams(window.location.search);
params.set("with", `${mount}/`);
window.location.replace(
`${window.location.pathname}?${params.toString()}`
);
return true;
}
function hideNode(node) {
if (!node || node.dataset.keycapeOverlayHidden === "true") return;
const field =
node.closest(".field.is-horizontal") ||
node.closest(".field") ||
node.closest(".box") ||
node;
if (field.dataset.keycapeOverlayHidden === "true") return;
field.style.display = "none";
field.setAttribute("aria-hidden", "true");
field.dataset.keycapeOverlayHidden = "true";
}
function setInputValue(input, value) {
if (!input || input.dataset.keycapeOverlayPreset === value) return;
input.value = value;
input.dataset.keycapeOverlayPreset = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
input.dispatchEvent(new Event("change", { bubbles: true }));
}
async function redirectToKeyCape() {
const mount = presets.mount || "netkingdom";
const role = presets.role || "platform-admin";
const redirectUri = `${window.location.origin}/ui/vault/auth/${mount}/oidc/callback`;
const response = await fetch(`/v1/auth/${mount}/oidc/auth_url`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
role,
redirect_uri: redirectUri,
}),
});
if (!response.ok) {
throw new Error(`OIDC auth_url request failed (${response.status})`);
}
const payload = await response.json();
const authUrl = payload?.data?.auth_url;
if (!authUrl) {
throw new Error("OIDC auth_url missing from OpenBao response");
}
window.location.assign(authUrl);
}
function installKeyCapeSignInHandler() {
if (signInHandlerInstalled) return;
signInHandlerInstalled = true;
document.addEventListener(
"click",
(event) => {
if (!isAuthPage()) return;
const button = event.target.closest(
'#auth-submit, button[data-test="auth-submit"], form#auth-form button[type="submit"]'
);
if (!button) return;
event.preventDefault();
event.stopPropagation();
button.disabled = true;
button.classList.add("is-loading");
redirectToKeyCape().catch(() => {
button.disabled = false;
button.classList.remove("is-loading");
});
},
true
);
}
function loginShellReady() {
return Boolean(
document.querySelector(".login-form") ||
document.querySelector(".auth-form") ||
document.querySelector(".toolbar-namespace-picker")
);
}
function applyDom() {
if (!isAuthPage() || overlayApplied) return false;
hideNode(document.querySelector(".toolbar-namespace-picker"));
document
.querySelectorAll(
'#namespace, input[name="namespace"], label[for="namespace"]'
)
.forEach(hideNode);
document
.querySelectorAll('select[name="auth-method"], #auth-method')
.forEach((el) => hideNode(el.closest(".field") || el));
document
.querySelectorAll('#custom-path, input[name="custom-path"]')
.forEach(hideNode);
document
.querySelectorAll('#role, input[name="role"], label[for="role"]')
.forEach(hideNode);
document
.querySelectorAll(
'#token, input[name="token"], label[for="token"], #username, input[name="username"], #password, input[name="password"]'
)
.forEach(hideNode);
document.querySelectorAll("nav.tabs").forEach((el) => {
if (el.dataset.keycapeOverlayHidden === "true") return;
el.style.display = "none";
el.setAttribute("aria-hidden", "true");
el.dataset.keycapeOverlayHidden = "true";
});
document
.querySelectorAll(".auth-form .has-bottom-margin-s")
.forEach(hideNode);
document.querySelectorAll("h1.title.is-3").forEach((heading) => {
if (
/Sign in to OpenBao|Authenticate/.test(heading.textContent) &&
heading.textContent !== presets.title
) {
heading.textContent = presets.title;
}
});
document
.querySelectorAll('#auth-submit, button[data-test="auth-submit"]')
.forEach((button) => {
if (button.textContent !== presets.signInLabel) {
button.textContent = presets.signInLabel;
}
});
document
.querySelectorAll('#namespace, input[name="namespace"]')
.forEach((input) => setInputValue(input, presets.namespace || ""));
document
.querySelectorAll('#role, input[name="role"]')
.forEach((input) =>
setInputValue(input, presets.role || "platform-admin")
);
if (!document.getElementById("keycape-overlay-banner")) {
const banner = document.createElement("div");
banner.id = "keycape-overlay-banner";
banner.className = "keycape-overlay-banner";
banner.textContent = presets.banner;
const loginForm = document.querySelector(".login-form");
if (loginForm) {
loginForm.prepend(banner);
}
}
document.documentElement.classList.add("keycape-overlay-active");
installKeyCapeSignInHandler();
if (loginShellReady()) {
overlayApplied = true;
return true;
}
return false;
}
function stopApplyLoop() {
if (applyTimer !== null) {
window.clearInterval(applyTimer);
applyTimer = null;
}
}
function stopMountWatch() {
if (mountWatchTimer !== null) {
window.clearInterval(mountWatchTimer);
mountWatchTimer = null;
}
}
function watchAuthMount() {
stopMountWatch();
let checks = 0;
mountWatchTimer = window.setInterval(() => {
checks += 1;
if (ensureAuthMountSelected() || checks >= MOUNT_WATCH_MAX) {
stopMountWatch();
}
}, MOUNT_WATCH_MS);
}
function scheduleApply() {
stopApplyLoop();
applyAttempts = 0;
const tick = () => {
applyAttempts += 1;
if (applyDom() || applyAttempts >= MAX_APPLY_ATTEMPTS) {
stopApplyLoop();
return;
}
};
tick();
applyTimer = window.setInterval(tick, APPLY_INTERVAL_MS);
}
async function loadPresets() {
try {
const response = await fetch(PRESETS_URL, { cache: "no-store" });
if (!response.ok) return;
const data = await response.json();
presets = { ...DEFAULT_PRESETS, ...data };
} catch (_error) {
presets = { ...DEFAULT_PRESETS };
}
}
async function init() {
if (!isAuthPage()) return;
await loadPresets();
if (ensureAuthMountSelected()) {
return;
}
watchAuthMount();
installKeyCapeSignInHandler();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", scheduleApply, {
once: true,
});
} else {
scheduleApply();
}
}
init();
})();