from dataclasses import dataclass from secrets import compare_digest from fastapi import Header, HTTPException, status from app.core.config import get_settings from app.core.constants import HttpHeader @dataclass(frozen=True) class ApiPrincipal: """Authenticated service principal derived from server-side configuration.""" actor: str def require_api_key( x_api_key: str | None = Header(default=None, alias=HttpHeader.X_API_KEY), ) -> ApiPrincipal: """Validate the internal API key header and return its service principal.""" settings = get_settings() if not settings.api_key: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="API_KEY is required", ) if not x_api_key or not compare_digest(x_api_key, settings.api_key): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key") return ApiPrincipal(actor=settings.api_actor) def require_approval_api_key( x_approval_api_key: str | None = Header( default=None, alias=HttpHeader.X_APPROVAL_API_KEY, ), ) -> ApiPrincipal: """Validate the approval API key and return the approval principal.""" settings = get_settings() if not settings.approval_api_key: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="APPROVAL_API_KEY is required", ) if ( not x_approval_api_key or not compare_digest(x_approval_api_key, settings.approval_api_key) ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid approval API key", ) return ApiPrincipal(actor=settings.approval_api_actor) def require_audit_api_key( x_audit_api_key: str | None = Header( default=None, alias=HttpHeader.X_AUDIT_API_KEY, ), ) -> ApiPrincipal: """Validate the audit API key and return the audit principal.""" settings = get_settings() if not settings.audit_api_key: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="AUDIT_API_KEY is required", ) if not x_audit_api_key or not compare_digest(x_audit_api_key, settings.audit_api_key): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid audit API key", ) return ApiPrincipal(actor=settings.audit_api_actor)