feat: add require_admin dependency and protect admin endpoints

Add require_admin FastAPI dependency that checks is_admin column on users
table. Apply it to all admin-facing write endpoints (games, pokemon,
evolutions, bosses, routes CRUD). Run-scoped endpoints remain protected
by require_auth only since they manage user's own data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 11:14:55 +01:00
parent 1042fff2b8
commit 2e66186fac
7 changed files with 226 additions and 27 deletions

View File

@@ -3,6 +3,7 @@ from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from app.core.auth import AuthUser, require_admin
from app.core.database import get_session
from app.models.evolution import Evolution
from app.models.pokemon import Pokemon
@@ -89,7 +90,9 @@ async def list_evolutions(
@router.post("/evolutions", response_model=EvolutionAdminResponse, status_code=201)
async def create_evolution(
data: EvolutionCreate, session: AsyncSession = Depends(get_session)
data: EvolutionCreate,
session: AsyncSession = Depends(get_session),
_user: AuthUser = Depends(require_admin),
):
from_pokemon = await session.get(Pokemon, data.from_pokemon_id)
if from_pokemon is None:
@@ -117,6 +120,7 @@ async def update_evolution(
evolution_id: int,
data: EvolutionUpdate,
session: AsyncSession = Depends(get_session),
_user: AuthUser = Depends(require_admin),
):
evolution = await session.get(Evolution, evolution_id)
if evolution is None:
@@ -150,7 +154,9 @@ async def update_evolution(
@router.delete("/evolutions/{evolution_id}", status_code=204)
async def delete_evolution(
evolution_id: int, session: AsyncSession = Depends(get_session)
evolution_id: int,
session: AsyncSession = Depends(get_session),
_user: AuthUser = Depends(require_admin),
):
evolution = await session.get(Evolution, evolution_id)
if evolution is None:
@@ -164,6 +170,7 @@ async def delete_evolution(
async def bulk_import_evolutions(
items: list[BulkEvolutionItem],
session: AsyncSession = Depends(get_session),
_user: AuthUser = Depends(require_admin),
):
# Build pokeapi_id -> id mapping
result = await session.execute(select(Pokemon.pokeapi_id, Pokemon.id))