feat(WARDEN-WP-0014): T4 — key-cape login orchestration lane

Adds a lane: secret|login field to RouteEntry. The login lane is an
interactive auth bootstrap: it skips the caller-auth precheck (no token
yet — that's the point) and the secret-read gate (it establishes the
identity the gate needs), runs the owner's login command interactively
as the caller via inherited stdio, and rejects --exec. The token stays
in the caller's own store; warden never captures it (G2 holds). Audited
as action: login. key-cape-oidc-login populated as the reference login
entry. Advisory proxy hint updated now that T3 has shipped.

172 passed, lint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 17:31:55 +02:00
parent 1a02ec6753
commit 1c3d1b4d52
6 changed files with 136 additions and 32 deletions

View File

@@ -180,3 +180,59 @@ def test_cli_proxy_requires_caller_auth(monkeypatch, tmp_path):
"--path", "platform/x/y/z", "--fetch", "--no-policy"],
)
assert r.exit_code == 3
# --- T4: login lane --------------------------------------------------------
def test_cli_login_lane_runs_without_token_or_policy_ack(monkeypatch, tmp_path):
"""Login lane skips the caller-auth precheck and the secret-read gate."""
_proxy_env(monkeypatch, tmp_path)
monkeypatch.delenv("VAULT_TOKEN", raising=False)
monkeypatch.delenv("BAO_TOKEN", raising=False)
monkeypatch.setattr(Path, "home", lambda: tmp_path) # no ~/.vault-token
ran = {}
def fake_run(argv, **kw):
ran["argv"] = argv
ran["stdout"] = kw.get("stdout")
return subprocess.CompletedProcess(argv, 0)
monkeypatch.setattr("warden.proxy.subprocess.run", fake_run)
r = runner.invoke(app, ["access", "login oidc", "--domain", "coulomb_social", "--fetch"])
assert r.exit_code == 0
assert ran["argv"][:2] == ["bao", "login"] # interactive login ran
assert ran["stdout"] is None # inherited stdio — token not captured
def test_cli_login_lane_rejects_exec(monkeypatch, tmp_path):
_proxy_env(monkeypatch, tmp_path)
monkeypatch.setattr(
"warden.proxy.subprocess.run",
lambda *a, **k: (_ for _ in ()).throw(AssertionError("should not run")),
)
r = runner.invoke(
app, ["access", "login oidc", "--domain", "coulomb_social", "--exec", "--", "true"]
)
assert r.exit_code == 2
def test_real_catalog_login_entry_is_login_lane():
from warden.routing import load_catalog
e = load_catalog(_repo_catalog()).get("key-cape-oidc-login")
assert e is not None and e.lane == "login" and e.exec_capable
def test_invalid_lane_rejected(tmp_path):
import yaml
from warden.routing import CatalogError, load_catalog
entry = dict(
id="x", title="t", need_keywords=["k"], owner_repo="o", subsystem="s",
warden_executes=False, wiki_ref="w", canon_ref="c", reviewed="2026-06-27",
status="active", lane="bogus",
)
p = tmp_path / "c.yaml"
p.write_text(yaml.dump({"version": 1, "entries": [entry]}))
import pytest
with pytest.raises(CatalogError, match="invalid lane"):
load_catalog(p)