Close the second round of review findings: GrantedCode, no minting from a name
This commit is contained in:
parent
db9a450f0c
commit
a7968dd4cb
@ -92,25 +92,50 @@ def issue_agent_session(
|
||||
}
|
||||
|
||||
|
||||
def issue_view_session(
|
||||
store: Store,
|
||||
agent: str,
|
||||
scopes: Optional[List[str]] = None,
|
||||
ttl: int = 3600,
|
||||
granted_by: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""Mint a human_via_agent session.
|
||||
class GrantedCode:
|
||||
"""Proof that a one-time view code was redeemed.
|
||||
|
||||
Plumbing primitive, not an entry point: it trusts whoever calls it. Code
|
||||
reached from outside the process must authenticate first. issue_code()
|
||||
enforces that by requiring an agent session, and redeem_code() may present a
|
||||
one-time code as its credential.
|
||||
Constructed only by viewcodes.redeem_code(), and only from a code that
|
||||
actually matched. Minting a human session now requires one of these, so the
|
||||
session minting path cannot be reached by passing an agent name.
|
||||
|
||||
`granted_by` is the agent that approved the code. It is recorded so the
|
||||
audit trail can answer "who let this human in" and not merely "which
|
||||
agent's things did they see".
|
||||
This is a guardrail against wiring mistakes, not a security boundary: any
|
||||
code running in this process can construct it. The boundary that matters is
|
||||
that no network-reachable route reaches this function without either an
|
||||
agent session (issue_code) or a valid code (redeem_code).
|
||||
"""
|
||||
granted = list(scopes if scopes is not None else HUMAN_VIEW_SCOPES)
|
||||
|
||||
__slots__ = ("agent", "scopes", "ttl", "_sigil_internal")
|
||||
|
||||
def __init__(self, agent: str, scopes: List[str], ttl: int, _internal=None):
|
||||
if _internal is not _ISSUER:
|
||||
raise errors.SigilError(
|
||||
"GrantedCode cannot be constructed directly; it comes from"
|
||||
" viewcodes.redeem_code()"
|
||||
)
|
||||
self.agent = agent
|
||||
self.scopes = list(scopes)
|
||||
self.ttl = ttl
|
||||
self._sigil_internal = _internal
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<GrantedCode agent={self.agent!r}>"
|
||||
|
||||
|
||||
_ISSUER = object()
|
||||
|
||||
|
||||
def issue_view_session(store: Store, grant: "GrantedCode") -> dict:
|
||||
"""Mint a human_via_agent session from a redeemed code.
|
||||
|
||||
Takes a GrantedCode rather than an agent name, so the only way to reach a
|
||||
human session is to have redeemed a code. `granted_by` is the agent that
|
||||
approved it, recorded so the audit trail can answer "who let this human in"
|
||||
and not merely "which agent's things did they see".
|
||||
"""
|
||||
agent = grant.agent
|
||||
granted = list(grant.scopes)
|
||||
ttl = grant.ttl
|
||||
extra = [s for s in granted if s not in HUMAN_VIEW_SCOPES]
|
||||
if extra:
|
||||
# Defence in depth: even if a caller tries to widen a human session,
|
||||
@ -125,7 +150,7 @@ def issue_view_session(
|
||||
agent,
|
||||
granted,
|
||||
expires,
|
||||
granted_by=granted_by or agent,
|
||||
granted_by=agent,
|
||||
)
|
||||
store.record(
|
||||
"session.issued",
|
||||
@ -135,7 +160,7 @@ def issue_view_session(
|
||||
"kind": HUMAN_VIA_AGENT,
|
||||
"scopes": granted,
|
||||
"expires_at": expires,
|
||||
"granted_by": granted_by or agent,
|
||||
"granted_by": agent,
|
||||
},
|
||||
)
|
||||
return {
|
||||
@ -143,7 +168,7 @@ def issue_view_session(
|
||||
"kind": HUMAN_VIA_AGENT,
|
||||
"subject": f"{agent}'s human",
|
||||
"on_behalf_of": agent,
|
||||
"granted_by": granted_by or agent,
|
||||
"granted_by": agent,
|
||||
"scopes": granted,
|
||||
"expires_at": expires,
|
||||
"ttl": ttl,
|
||||
@ -198,4 +223,6 @@ def assert_may_act_as_agent(session: dict) -> str:
|
||||
|
||||
def revoke(store: Store, token: str) -> None:
|
||||
store.revoke_session(hash_token(token))
|
||||
store.record("session.revoked", actor="system", detail={"token_prefix": token[:8]})
|
||||
# A digest, not a prefix of the token: the log outlives the session.
|
||||
store.record("session.revoked", actor="system",
|
||||
detail={"token_ref": "tok:" + hash_token(token)[:12]})
|
||||
Loading…
x
Reference in New Issue
Block a user