Why: - Wires the ADR-0008 error envelope, per-request correlation id, and the /v1/files routes into the app. Changes: - Extend AppResources/lifespan with the ingestion CapacityLimiter and ObjectStorage adapter. Impact: - /v1 now exposes routes for the first time.
39 lines
1.4 KiB
Python
39 lines
1.4 KiB
Python
"""Auth dependencies (ADR-0008): resolve `AuthContext` from a bearer token,
|
|
then gate routes on scope.
|
|
"""
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
from typing import Annotated
|
|
|
|
from fastapi import Depends
|
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
|
|
from src.application.auth.context import AuthContext
|
|
from src.application.auth.errors import InvalidApiKeyError, MissingScopeError
|
|
from src.application.auth.service import resolve_auth_context
|
|
from src.bootstrap.dependencies import get_sessionmaker
|
|
|
|
_bearer_scheme = HTTPBearer(auto_error=False)
|
|
|
|
|
|
async def get_auth_context(
|
|
credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(_bearer_scheme)],
|
|
sessionmaker: Annotated[async_sessionmaker[AsyncSession], Depends(get_sessionmaker)],
|
|
) -> AuthContext:
|
|
if credentials is None:
|
|
raise InvalidApiKeyError("missing Authorization header")
|
|
return await resolve_auth_context(sessionmaker, credentials.credentials)
|
|
|
|
|
|
AuthContextDep = Annotated[AuthContext, Depends(get_auth_context)]
|
|
|
|
|
|
def require_scope(scope: str) -> Callable[[AuthContext], Awaitable[AuthContext]]:
|
|
async def _dependency(auth: AuthContextDep) -> AuthContext:
|
|
if not auth.has_scope(scope):
|
|
raise MissingScopeError(f"missing required scope '{scope}'")
|
|
return auth
|
|
|
|
return _dependency
|