fix: enforce run ownership on all mutation endpoints

Add require_run_owner helper in auth.py that enforces ownership on
mutation endpoints. Unowned (legacy) runs are now read-only.

Applied ownership checks to:
- All 4 encounter mutation endpoints
- Both boss result mutation endpoints
- Run update/delete endpoints
- All 5 genlocke mutation endpoints (via first leg's run owner)

Also sets owner_id on run creation in genlockes.py (create_genlocke,
advance_leg) and adds 22 comprehensive ownership enforcement tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 13:28:32 +01:00
parent a12958ae32
commit eeb1609452
8 changed files with 660 additions and 39 deletions

View File

@@ -8,6 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.core.database import get_session
from app.models.nuzlocke_run import NuzlockeRun
from app.models.user import User
@@ -105,3 +106,20 @@ async def require_admin(
detail="Admin access required",
)
return user
def require_run_owner(run: NuzlockeRun, user: AuthUser) -> None:
"""
Verify user owns the run. Raises 403 if not owner.
Unowned (legacy) runs are read-only and reject all mutations.
"""
if run.owner_id is None:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="This run has no owner and cannot be modified",
)
if UUID(user.id) != run.owner_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only the run owner can perform this action",
)