angeetoile's picture
feat(opportunities): add protected batch synchronization endpoint
f55a6b5
Raw
History Blame Contribute Delete
1.73 kB
import secrets
from typing import Annotated
from fastapi import Depends, HTTPException, status
from fastapi.security import APIKeyHeader
from app.core.config import settings
sync_token_header = APIKeyHeader(
name="X-Sync-Token",
scheme_name="OpportunitySyncToken",
description=(
"Token interne autorisant la synchronisation "
"automatique des opportunités."
),
auto_error=False,
)
async def require_sync_token(
provided_token: Annotated[
str | None,
Depends(sync_token_header),
],
) -> None:
"""
Protège les opérations internes de synchronisation.
Le token reçu dans l’en-tête X-Sync-Token est comparé
au secret SYNC_API_KEY configuré sur le serveur.
"""
configured_secret = settings.sync_api_key
if configured_secret is None:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Opportunity synchronization is not configured.",
)
expected_token = configured_secret.get_secret_value()
if not expected_token:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Opportunity synchronization is not configured.",
)
if (
provided_token is None
or not secrets.compare_digest(
provided_token,
expected_token,
)
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing synchronization token.",
headers={
"WWW-Authenticate": "ApiKey",
},
)
SyncTokenDependency = Annotated[
None,
Depends(require_sync_token),
]