credential-change.py generates sys/capabilities-self (update) when openbao.token_capabilities_self is true. Proposed CCR-2026-0045 sets it for workload-kv-read-reuse-surface-runtime so the ops-mason proof stops calling sys/capabilities with a root token. The OpenBao login page keeps its platform-admin button and adds an allow-listed workload role choice. Repo only; nothing applied to OpenBao or the cluster. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 1041694@bnt-lap001 Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
95 lines
4.1 KiB
Python
95 lines
4.1 KiB
Python
"""NK-WP-0051-T06: the OpenBao login page can choose the workload role.
|
|
|
|
The main button stays role platform-admin. Workload roles come only from the
|
|
presets allow-list. The script runs in node against a minimal DOM stub.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_DIR = Path(__file__).resolve().parents[1]
|
|
OVERLAY = REPO_DIR / "helm/openbao-ui-overlay"
|
|
WORKLOAD_ROLE = "reuse-surface-hub-writer-workload-kv-read"
|
|
|
|
HARNESS = r"""
|
|
const fs = require("fs");
|
|
const [script, presetsPath, clickId, selectValue] = process.argv.slice(1);
|
|
const presets = JSON.parse(fs.readFileSync(presetsPath, "utf8"));
|
|
presets.workloadRoles = (presets.workloadRoles || []).concat([{ role: "Bad Role!" }]);
|
|
const els = {};
|
|
function el(id) {
|
|
const node = { id, hidden: false, disabled: false, textContent: "", value: "",
|
|
children: [], listeners: {}, classList: { add() {} },
|
|
appendChild(c) { this.children.push(c); if (!this.value) this.value = c.value; },
|
|
addEventListener(t, f) { this.listeners[t] = f; } };
|
|
els[id] = node; return node;
|
|
}
|
|
["login-title","login-banner","login-submit","login-error","login-workload",
|
|
"login-workload-summary","login-workload-hint","login-workload-role","login-workload-submit"].forEach(el);
|
|
els["login-workload"].hidden = true;
|
|
const posted = [];
|
|
global.document = { getElementById: (id) => els[id] || null,
|
|
createElement: () => ({ value: "", textContent: "" }) };
|
|
global.window = { location: { origin: "http://127.0.0.1:18200", assign() {} } };
|
|
global.fetch = async (url, opts) => {
|
|
if (url.endsWith("presets.json")) return { ok: true, json: async () => presets };
|
|
posted.push({ url, body: JSON.parse(opts.body) });
|
|
return { ok: true, json: async () => ({ data: { auth_url: "https://kc.example/authorize" } }) };
|
|
};
|
|
eval(fs.readFileSync(script, "utf8"));
|
|
setTimeout(async () => {
|
|
if (selectValue) els["login-workload-role"].value = selectValue;
|
|
await els[clickId].listeners.click();
|
|
console.log(JSON.stringify({ posted,
|
|
options: els["login-workload-role"].children.map((c) => c.value),
|
|
workloadHidden: els["login-workload"].hidden,
|
|
error: els["login-error"].textContent }));
|
|
}, 20);
|
|
"""
|
|
|
|
|
|
@unittest.skipUnless(shutil.which("node"), "node is not installed")
|
|
class OpenBaoUiRoleSelectTests(unittest.TestCase):
|
|
def run_page(self, click_id: str, select_value: str = "") -> dict:
|
|
proc = subprocess.run(
|
|
["node", "-e", HARNESS, str(OVERLAY / "login.js"), str(OVERLAY / "presets.json"),
|
|
click_id, select_value],
|
|
capture_output=True, text=True, check=True, timeout=30,
|
|
)
|
|
return json.loads(proc.stdout.strip().splitlines()[-1])
|
|
|
|
def test_presets_keep_platform_admin_and_list_workload_role(self) -> None:
|
|
presets = json.loads((OVERLAY / "presets.json").read_text(encoding="utf-8"))
|
|
self.assertEqual(presets["role"], "platform-admin")
|
|
self.assertEqual(presets["mount"], "netkingdom")
|
|
self.assertIn(WORKLOAD_ROLE, [entry["role"] for entry in presets["workloadRoles"]])
|
|
|
|
def test_main_button_stays_platform_admin(self) -> None:
|
|
result = self.run_page("login-submit")
|
|
self.assertEqual(len(result["posted"]), 1)
|
|
self.assertEqual(result["posted"][0]["url"], "/v1/auth/netkingdom/oidc/auth_url")
|
|
self.assertEqual(result["posted"][0]["body"]["role"], "platform-admin")
|
|
self.assertEqual(
|
|
result["posted"][0]["body"]["redirect_uri"],
|
|
"http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback",
|
|
)
|
|
|
|
def test_workload_button_uses_selected_listed_role(self) -> None:
|
|
result = self.run_page("login-workload-submit", WORKLOAD_ROLE)
|
|
self.assertFalse(result["workloadHidden"])
|
|
self.assertEqual(result["options"], [WORKLOAD_ROLE])
|
|
self.assertEqual(result["posted"][0]["body"]["role"], WORKLOAD_ROLE)
|
|
|
|
def test_unlisted_role_is_refused(self) -> None:
|
|
result = self.run_page("login-workload-submit", "platform-admin")
|
|
self.assertEqual(result["posted"], [])
|
|
self.assertIn("listed workload role", result["error"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|