feat(api): role-gate handoff claim to engineer-or-admin
POST /ai-sessions/{id}/handoffs/{hid}/claim previously required only an
authenticated user, so a viewer-role account user could claim escalations.
Codex review flagged this as wedge-relevant: the Escalation Mode race-
condition story (two seniors clicking Pick Up simultaneously) depends on
auth gating for audit integrity. Originally captured as a deferred TODO
during /plan-eng-review, then moved in-scope by /codex review.
Swap the dep to require_engineer_or_admin. One-line change. Two new tests:
- viewer_role gets 403 with "Engineer or admin access required"
- engineer/owner role still succeeds and claimed_at + claimed_by populate
Existing handoff create + queue tests unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_current_active_user, get_db
|
||||
from app.api.deps import get_current_active_user, get_db, require_engineer_or_admin
|
||||
from app.models.user import User
|
||||
from app.models.ai_session import AISession
|
||||
from app.models.session_handoff import SessionHandoff
|
||||
@@ -86,10 +86,16 @@ async def list_handoffs(
|
||||
async def claim_handoff(
|
||||
session_id: UUID,
|
||||
handoff_id: UUID,
|
||||
current_user: Annotated[User, Depends(get_current_active_user)],
|
||||
current_user: Annotated[User, Depends(require_engineer_or_admin)],
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
) -> HandoffResponse:
|
||||
"""Claim a handed-off session."""
|
||||
"""Claim a handed-off session.
|
||||
|
||||
Role-gated to engineer/admin/owner — viewers cannot claim. The race-condition
|
||||
story (two seniors clicking Pick Up simultaneously) depends on auth gating
|
||||
for audit integrity. Codex review flagged this as wedge-relevant; locked
|
||||
in-scope for Escalation Mode v1.
|
||||
"""
|
||||
manager = HandoffManager(db)
|
||||
try:
|
||||
handoff = await manager.claim_session(
|
||||
|
||||
Reference in New Issue
Block a user