[auth] Omit UNSIGNED self-call token in dev mode + video-engine test mypy fix

_agent_api_headers sent the UNSIGNED sentinel when ROBOCO_AGENT_AUTH_SECRET
was unset, but the dev-mode middleware rejects a presented-but-unverifiable
token with 401 signature mismatch (while accepting a missing one). The
cell-PM auto-submit self-call 401'd in every dev run, regressing
test_auto_submit_cuts_the_pm_turn. Attach the token only when a secret is
set. Also fix the FromClause.update mypy error in the per-project
video-engine opt-out test (ORM row load + flush).
This commit is contained in:
Renn F
2026-07-06 05:42:45 +02:00
parent 53391f2248
commit 2e938d20d6
3 changed files with 28 additions and 7 deletions
+9 -2
View File
@@ -169,14 +169,21 @@ def _agent_api_headers(agent_uuid: str, role: str) -> dict[str, str]:
gate a hand-built ``{X-Agent-ID, X-Agent-Role}`` dict 401s with
"Missing X-Agent-Token" under auth-required (F038/F039 the same gap the
system-headers helper closes for the system identity).
The token is attached only when ``ROBOCO_AGENT_AUTH_SECRET`` is set: the
dev-mode middleware rejects a presented-but-unverifiable token (the
``UNSIGNED`` sentinel) with 401 "signature mismatch" while accepting a
missing token, so sending ``UNSIGNED`` would turn a clean dev self-call
into a 401. With the secret armed the token is signed and verifies.
"""
from roboco.agents_config import issue_agent_token
from roboco.agents_config import _auth_secret, issue_agent_token
team = get_agent_team(agent_uuid) or ""
headers = {"X-Agent-ID": agent_uuid, "X-Agent-Role": role}
if team:
headers["X-Agent-Team"] = team
headers["X-Agent-Token"] = issue_agent_token(agent_uuid, role, team)
if _auth_secret():
headers["X-Agent-Token"] = issue_agent_token(agent_uuid, role, team)
return headers
@@ -82,3 +82,18 @@ def test_agent_api_headers_carry_signed_token_and_team(
token = headers["X-Agent-Token"]
assert token and token != "UNSIGNED"
assert verify_agent_token(token, be_pm_uuid, role, team)
def test_agent_api_headers_omit_token_when_secret_unset(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Dev mode: with no secret set, issue_agent_token returns the UNSIGNED
# sentinel, but the dev-mode middleware rejects a presented-but-unverifiable
# token with 401 "signature mismatch" while accepting a missing token.
# Sending UNSIGNED would 401 the cell-PM auto-submit self-call in every dev
# run (the e2e test_auto_submit_cuts_the_pm_turn regression), so the token
# header is omitted entirely when the secret is unset.
monkeypatch.delenv("ROBOCO_AGENT_AUTH_SECRET", raising=False)
be_pm = _foundation.AGENTS["be-pm"]
headers = _agent_api_headers(str(be_pm.uuid), be_pm.role.value)
assert "X-Agent-Token" not in headers
+4 -5
View File
@@ -221,11 +221,10 @@ async def test_open_video_task_no_op_when_project_not_opted_in(
) -> None:
await _seed(db_session)
# Flip the per-project opt-in back off — the global flag stays on.
await db_session.execute(
ProjectTable.__table__.update()
.where(ProjectTable.__table__.c.slug == SLUG)
.values(video_engine_enabled=False)
)
project = (
await db_session.execute(select(ProjectTable).where(ProjectTable.slug == SLUG))
).scalar_one()
project.video_engine_enabled = False
await db_session.flush()
_enable(monkeypatch)
engine = video_engine_module.VideoEngine(db_session)