generated from coulomb/repo-seed
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:
@@ -780,33 +780,50 @@ def _access_proxy(
|
||||
)
|
||||
raise typer.Exit(2)
|
||||
|
||||
# G1 — caller identity. ops-warden adds no token of its own.
|
||||
if not caller_auth_present():
|
||||
err.print(
|
||||
"[red]No caller credential found[/red] (VAULT_TOKEN/BAO_TOKEN or ~/.vault-token). "
|
||||
f"Authenticate first: {entry.auth_method or 'see the owner auth path'}."
|
||||
)
|
||||
raise typer.Exit(3)
|
||||
|
||||
# G3 — policy gate before fetch.
|
||||
is_login = entry.lane == "login"
|
||||
decision_id = None
|
||||
if cfg.policy.enabled:
|
||||
try:
|
||||
decision_id = check_fetch_policy(
|
||||
cfg.policy, need_id=entry.id, owner_repo=entry.owner_repo, domain=domain
|
||||
|
||||
if is_login:
|
||||
# Login lane: interactive auth bootstrap. No caller-auth precheck (you have no
|
||||
# token yet — that's the point) and no secret-read gate (it needs an identity
|
||||
# this flow establishes). --exec is meaningless here.
|
||||
if do_exec:
|
||||
err.print(
|
||||
"[red]--exec is not valid for a login lane[/red] "
|
||||
f"({entry.id!r} is interactive auth). Use --fetch."
|
||||
)
|
||||
except CAError as e:
|
||||
err.print(f"[red]Policy gate denied the fetch:[/red] {e}")
|
||||
raise typer.Exit(4)
|
||||
err.print(f"[green]flex-auth allow[/green] (decision {decision_id}).")
|
||||
elif not no_policy:
|
||||
raise typer.Exit(2)
|
||||
err.print(
|
||||
"[yellow]flex-auth gate is not enforced[/yellow] (policy.enabled=false). "
|
||||
"Re-run with [bold]--no-policy[/bold] to proxy ungated, or enable the gate."
|
||||
"[dim]login lane — interactive auth bootstrap; no secret-read gate, "
|
||||
"token stays in the caller's own store.[/dim]"
|
||||
)
|
||||
raise typer.Exit(4)
|
||||
else:
|
||||
err.print("[yellow]Proxying ungated[/yellow] (--no-policy; gate not enforced).")
|
||||
# G1 — caller identity. ops-warden adds no token of its own.
|
||||
if not caller_auth_present():
|
||||
err.print(
|
||||
"[red]No caller credential found[/red] (VAULT_TOKEN/BAO_TOKEN or ~/.vault-token). "
|
||||
f"Authenticate first: {entry.auth_method or 'see the owner auth path'}."
|
||||
)
|
||||
raise typer.Exit(3)
|
||||
|
||||
# G3 — policy gate before fetch.
|
||||
if cfg.policy.enabled:
|
||||
try:
|
||||
decision_id = check_fetch_policy(
|
||||
cfg.policy, need_id=entry.id, owner_repo=entry.owner_repo, domain=domain
|
||||
)
|
||||
except CAError as e:
|
||||
err.print(f"[red]Policy gate denied the fetch:[/red] {e}")
|
||||
raise typer.Exit(4)
|
||||
err.print(f"[green]flex-auth allow[/green] (decision {decision_id}).")
|
||||
elif not no_policy:
|
||||
err.print(
|
||||
"[yellow]flex-auth gate is not enforced[/yellow] (policy.enabled=false). "
|
||||
"Re-run with [bold]--no-policy[/bold] to proxy ungated, or enable the gate."
|
||||
)
|
||||
raise typer.Exit(4)
|
||||
else:
|
||||
err.print("[yellow]Proxying ungated[/yellow] (--no-policy; gate not enforced).")
|
||||
|
||||
try:
|
||||
argv = resolve_fetch_command(entry, domain=domain, field=field, path=path)
|
||||
@@ -814,7 +831,7 @@ def _access_proxy(
|
||||
err.print(f"[red]{e}[/red]")
|
||||
raise typer.Exit(2)
|
||||
|
||||
action = "exec" if do_exec else "fetch"
|
||||
action = "login" if is_login else ("exec" if do_exec else "fetch")
|
||||
err.print(
|
||||
f"[dim]proxy {action}: {entry.id} → {entry.owner_repo} "
|
||||
f"(caller identity; value not persisted)[/dim]"
|
||||
@@ -946,10 +963,12 @@ def access(
|
||||
proxy = f"warden access {need!r}"
|
||||
if domain:
|
||||
proxy += f" --domain {domain}"
|
||||
console.print(
|
||||
f" proxy : [dim]{proxy} --fetch[/dim] "
|
||||
"[yellow](exec_capable; proxy ships in WP-0014 T3)[/yellow]"
|
||||
hint = (
|
||||
"add --fetch to proxy as the caller"
|
||||
if entry.lane != "login"
|
||||
else "add --fetch to run the interactive login as the caller"
|
||||
)
|
||||
console.print(f" proxy : [dim]{proxy} --fetch[/dim] [yellow]({hint})[/yellow]")
|
||||
if expanded.path_template and "<" in expanded.path_template:
|
||||
console.print(
|
||||
" note : remaining <…> placeholders are owner-confirmed names "
|
||||
|
||||
Reference in New Issue
Block a user