fix(agents): isolate human subject reads and reject inactive authentication writes

This commit is contained in:
Joshua Valluru 2026-09-26 12:57:37 -07:00
parent dd2cfa2643
commit c8e1fc003d
2 changed files with 27 additions and 4 deletions

View file

@ -98,9 +98,6 @@ class AgentIdentityStore:
row: Final = await self.identities.table.find_unique(where=where)
except Exception:
return AgentIdentityFailure(code="policy_unavailable", message="Agent identity could not be loaded")
proven: Final = await self.subject(issuer, tenant, claims.get("oid"))
if isinstance(proven, AgentIdentityFailure):
return proven
if row is None:
return await self.unbound_client(where)
agent: Final = await self.agent(row.agent_id)
@ -124,6 +121,9 @@ class AgentIdentityStore:
mode=subject.mode,
subject_oid=subject.oid,
)
proven: Final = await self.subject(issuer, tenant, claims.get("oid"))
if isinstance(proven, AgentIdentityFailure):
return proven
human: Final = (
VerifiedHumanSubject.model_validate(proven.model_dump())
if proven is not None
@ -170,6 +170,8 @@ class AgentIdentityStore:
where: Final[LiteLLM_AgentIdentityWhereInput] = {
"agent_id": context.agent_id,
"revision": context.binding_revision,
"active": True,
"agent": {"is": {"enabled": True, "identity_managed": True}},
}
data: Final[LiteLLM_AgentIdentityUpdateManyMutationInput] = {
"last_authenticated_at": datetime.now(timezone.utc)

View file

@ -158,7 +158,12 @@ async def test_rebinding_during_authentication_does_not_mark_new_identity_verifi
result: Final = await store.record_authentication(context)
assert isinstance(result, AgentIdentityFailure)
assert "changed" in result.message
assert identities.update_many.call_args.kwargs["where"] == {"agent_id": "agent-one", "revision": "old-revision"}
assert identities.update_many.call_args.kwargs["where"] == {
"agent_id": "agent-one",
"revision": "old-revision",
"active": True,
"agent": {"is": {"enabled": True, "identity_managed": True}},
}
@pytest.mark.asyncio
@ -379,3 +384,19 @@ async def test_resolver_preserves_unconfigured_and_unrelated_authentication() ->
store, _, identities, _ = setup_store()
identities.find_unique.return_value = None
assert await store.resolve_verified_claims(CLAIMS) is None
@pytest.mark.asyncio
@pytest.mark.parametrize("registered", [True, False])
async def test_application_and_unregistered_clients_do_not_depend_on_human_subject_storage(registered: bool) -> None:
store, _, identities, humans = setup_store()
identities.find_unique.return_value = BINDING if registered else None
humans.find_unique.side_effect = RuntimeError("subject database unavailable")
result: Final = await store.resolve_verified_claims(CLAIMS)
if registered:
assert isinstance(result, ManagedAgentContext)
assert result.mode == "autonomous"
assert result.user_id is None
else:
assert result is None
humans.find_unique.assert_not_awaited()