from __future__ import annotations import json from typing import Mapping import requests from src import config from src.api_client.auth import AuthApiClient from src.api_client.errors import AuthError from src.api_utils.errors import Unauthorized from src.api_utils.middleware import Middleware from src.api_utils.request import Request from src.api_utils.response import Response from src.constants import DISABLE_AUTH_URLS from src.logger import BoundLogger __all__ = ["AuthMiddleware", "CORSMiddleware"] class AuthMiddleware(Middleware): def handle(self, request: Request, *, logger: "BoundLogger", **match_info) -> Response: def _auth(): for url in DISABLE_AUTH_URLS: if request.path.startswith(url): return token = request.headers.get("Authorization") or request.cookies.get("Authorization") if not token: raise Unauthorized("Authorization header is missing") if token == config.LOCAL_API_KEY: return try: AuthApiClient(logger=logger, auth_url=config.AUTH_URL).authorize(request) except AuthError as e: message = "Can't authorize" if e.response is not None and e.response.headers["content-type"] == "application/json": try: message = e.response.json() except requests.exceptions.JSONDecodeError: pass raise Unauthorized(message) from e if not config.DISABLE_AUTH: _auth() return self.handler(request, logger=logger, **match_info) class CORSMiddleware(Middleware): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._headers = self._get_headers(config.CORS_HEADERS) @staticmethod def _get_headers(raw_headers: str | None) -> Mapping[str, str]: headers: dict[str, str] = {} if not raw_headers: return headers for key, value in json.loads(raw_headers).items(): *_, key = key.split(".") if not key.lower().startswith("access-control"): continue headers[key] = value.strip("'") return headers def handle(self, request: Request, *, logger: "BoundLogger", **match_info) -> Response: response = self.handler(request, logger=logger, **match_info) response.headers.update(self._headers) return response