diff --git a/api/app/auth.py b/api/app/auth.py index bb41cb4..110fdef 100644 --- a/api/app/auth.py +++ b/api/app/auth.py @@ -48,3 +48,18 @@ def verify_token( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token", ) + + +def _admin_emails() -> frozenset[str]: + return frozenset( + e.strip() for e in settings.admin_user_emails.split(",") if e.strip() + ) + + +def require_admin(token_data: dict = Depends(verify_token)) -> dict: + if token_data.get("email") not in _admin_emails(): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Admin access required", + ) + return token_data diff --git a/api/app/config.py b/api/app/config.py index 56d091d..8a55b25 100644 --- a/api/app/config.py +++ b/api/app/config.py @@ -8,6 +8,7 @@ class Settings(BaseSettings): deepl_api_key: str deepgram_api_key: str gemini_api_key: str + admin_user_emails: str = "" # comma-separated list of admin email addresses storage_endpoint_url: str storage_access_key: str storage_secret_key: str diff --git a/api/app/routers/api/generation.py b/api/app/routers/api/generation.py index 41f4705..1320122 100644 --- a/api/app/routers/api/generation.py +++ b/api/app/routers/api/generation.py @@ -10,7 +10,7 @@ from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from ...languages import SUPPORTED_LANGUAGES, SUPPORTED_LEVELS -from ...auth import verify_token +from ...auth import require_admin from ...storage import upload_audio from ...outbound.postgres.database import get_db, AsyncSessionLocal from ...outbound.postgres.repositories import summarise_job_repository @@ -125,7 +125,7 @@ async def _run_generation(job_id: uuid.UUID, request: GenerationRequest) -> None async def create_generation_job( request: GenerationRequest, db: AsyncSession = Depends(get_db), - token_data: dict = Depends(verify_token), + token_data: dict = Depends(require_admin), ) -> GenerationResponse: if request.target_language not in SUPPORTED_LANGUAGES: raise HTTPException( diff --git a/api/app/routers/api/jobs.py b/api/app/routers/api/jobs.py index 8cd1798..02ee821 100644 --- a/api/app/routers/api/jobs.py +++ b/api/app/routers/api/jobs.py @@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession -from ...auth import verify_token +from ...auth import require_admin from ...outbound.postgres.database import get_db, AsyncSessionLocal from ...outbound.postgres.repositories import summarise_job_repository from ...outbound.gemini.gemini_client import GeminiClient @@ -14,7 +14,7 @@ from ...storage import upload_audio from ...config import settings from ... import worker -router = APIRouter(prefix="/jobs", dependencies=[Depends(verify_token)]) +router = APIRouter(prefix="/jobs", dependencies=[Depends(require_admin)]) class JobResponse(BaseModel): @@ -124,7 +124,7 @@ async def _run_regenerate_audio(job_id: uuid.UUID) -> None: async def regenerate_audio( job_id: str, db: AsyncSession = Depends(get_db), - token_data: dict = Depends(verify_token), + token_data: dict = Depends(require_admin), ) -> dict: try: uid = uuid.UUID(job_id)