net-kingdom/tools/service-identity/tests/test_keycape_alias_disable.py
tegwick 4d70743252 NK-WP-0054-T04: keycape alias helper reads bao list's bare-array JSON
Founder dry run 2 (receipt 20261107T140929Z): session, mounts and envelope
checks passed; the role list failed with TypeError. Fake bao now prints the
real CLI shape. Read-only; no live change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 149903@bnt-lap001
Assistant-Session: 4c20a536-1452-4a85-bc09-e1987d9817d6
2026-11-07 15:11:56 +01:00

119 lines
5.1 KiB
Python

"""keycape alias disable helper against a fake bao (NK-WP-0054-T04). Synthetic only."""
from __future__ import annotations
import json
import os
import pathlib
import subprocess
import sys
HERE = pathlib.Path(__file__).resolve().parent
SCRIPT = HERE.parent / "keycape-alias-disable.sh"
FAKE_BAO = r'''#!/usr/bin/env python3
import json, os, pathlib, sys
state_file = pathlib.Path(os.environ["FAKE_BAO_STATE"])
state = json.loads(state_file.read_text())
args = [a for a in sys.argv[1:] if a != "-format=json"]
state.setdefault("calls", []).append(args)
def out(data): print(json.dumps({"data": data}))
if args == ["read", "sys/auth"]:
out({m: {"type": t, "accessor": "auth_x"} for m, t in state["mounts"].items()})
elif args == ["read", "auth/token/lookup-self"]:
if state["session"] not in [m.rstrip("/") for m in state["mounts"]]:
sys.exit(2)
out({"path": "auth/%s/oidc/callback" % state["session"], "policies": ["default", "platform-admin"]})
elif args == ["list", "auth/keycape/role"]:
print(json.dumps(["platform-admin"])) # the real CLI prints a bare array for list
elif args == ["read", "auth/keycape/config"]:
out({"oidc_client_id": "openbao-admin", "default_role": "platform-admin", "oidc_client_secret": "never"})
elif args[:2] == ["auth", "disable"]:
state["mounts"].pop(args[2] + "/")
else:
sys.exit(3)
state_file.write_text(json.dumps(state))
'''
def prepare(tmp_path, session="netkingdom", mounts=None, envelope=True):
bindir = tmp_path / "bin"
bindir.mkdir()
(bindir / "bao").write_text(FAKE_BAO)
(bindir / "bao").chmod(0o755)
state = tmp_path / "state.json"
state.write_text(json.dumps({"session": session, "mounts": mounts or
{"keycape/": "oidc", "netkingdom/": "oidc", "token/": "token"}}))
home = tmp_path / (".warden-attended-login" if envelope else "plain") / "h"
home.mkdir(parents=True)
env = {k: v for k, v in os.environ.items() if k not in ("BAO_TOKEN", "VAULT_TOKEN")}
env.update(PATH=f"{bindir}:{env['PATH']}", HOME=str(home), FAKE_BAO_STATE=str(state))
return env, state
def run(tmp_path, env, *extra):
receipt = tmp_path / f"receipt{len(list(tmp_path.glob('receipt*')))}.json"
done = subprocess.run([str(SCRIPT), "--receipt", str(receipt), *extra], env=env, capture_output=True, text=True)
assert done.stdout == "" and done.stderr == "" # silent
body = json.loads(receipt.read_text())
assert oct(receipt.stat().st_mode & 0o777) == "0o600"
assert "never" not in receipt.read_text()
return done.returncode, body
def test_dry_run_reads_only(tmp_path):
env, state = prepare(tmp_path)
code, body = run(tmp_path, env)
assert code == 0 and body["status"] == "dry_run_ok" and body["applied"] is False
assert body["alias"] == {"roles": ["platform-admin"], "oidc_client_id": "openbao-admin",
"default_role": "platform-admin"}
assert not any(c[:2] == ["auth", "disable"] for c in json.loads(state.read_text())["calls"])
def test_apply_disables_and_verifies(tmp_path):
env, state = prepare(tmp_path)
code, body = run(tmp_path, env, "--apply")
assert code == 0 and body["status"] == "disabled_verified" and body["session_after_ok"]
assert "keycape/" not in body["mounts_after"] and "netkingdom/" in body["mounts_after"]
code, body = run(tmp_path, env, "--apply")
assert code == 0 and body["status"] == "already_disabled"
def test_refuses_session_from_the_alias(tmp_path):
env, state = prepare(tmp_path, session="keycape")
code, body = run(tmp_path, env, "--apply")
assert code == 1 and body["status"] == "session_not_from_auth_netkingdom"
assert "keycape/" in json.loads(state.read_text())["mounts"]
def test_refuses_outside_the_attended_envelope(tmp_path):
env, state = prepare(tmp_path, envelope=False)
code, body = run(tmp_path, env, "--apply")
assert code == 1 and body["status"] == "attended_envelope_required"
def test_refuses_existing_receipt(tmp_path):
env, _ = prepare(tmp_path)
receipt = tmp_path / "exists.json"
receipt.write_text("{}")
done = subprocess.run([str(SCRIPT), "--receipt", str(receipt)], env=env)
assert done.returncode != 0 and receipt.read_text() == "{}"
def test_unexpected_bao_output_still_leaves_a_receipt(tmp_path):
env, state = prepare(tmp_path)
bao = pathlib.Path(env["PATH"].split(":")[0]) / "bao"
bao.write_text("#!/bin/sh\necho 'not json'\n")
code, body = run(tmp_path, env)
assert code == 1 and body["status"] == "helper_failed" and body["step"] == "read auth/token/lookup-self"
assert body["applied"] is False
def test_bao_error_is_summarised_without_tokens(tmp_path):
env, state = prepare(tmp_path)
bao = pathlib.Path(env["PATH"].split(":")[0]) / "bao"
bao.write_text("#!/bin/sh\necho 'Error reading: Code: 403. Errors:' >&2\n"
"echo '* permission denied hvs.ABCDEFGHIJKLMNOPQRSTUVWX' >&2\nexit 2\n")
code, body = run(tmp_path, env)
assert body["status"] == "bao_failed" and body["exit"] == 2
assert "hvs.ABCDEFGHIJKLMNOPQRSTUVWX" not in json.dumps(body) and body["detail"]