""" Middleware для проверки аутентификации Автор: Сергей Антропов Сайт: https://devops.org.ru """ from fastapi import Request, HTTPException, status from fastapi.responses import RedirectResponse from starlette.middleware.base import BaseHTTPMiddleware from app.auth.security import decode_access_token import logging logger = logging.getLogger(__name__) # Публичные пути, не требующие аутентификации PUBLIC_PATHS = [ "/login", "/api/v1/auth/login", "/logout", "/api/v1/auth/logout", "/health", "/static", "/api/docs", "/api/redoc", "/openapi.json", "/api/v1/stats", # Статистика доступна без аутентификации "/api/v1/dockerfiles/build-logs/webhook", # Webhook для получения логов от builder "/api/v1/dockerfiles/build-logs/recent" # Получение последних логов сборки (может использоваться на странице сборки) ] class AuthMiddleware(BaseHTTPMiddleware): """Middleware для проверки аутентификации""" async def dispatch(self, request: Request, call_next): path = request.url.path # Пропускаем публичные пути (проверяем точное совпадение или начало пути) is_public = False for public_path in PUBLIC_PATHS: if path == public_path or path.startswith(public_path + "/") or path.startswith(public_path): is_public = True break if is_public: return await call_next(request) # Проверка токена из cookie или заголовка token = None # Проверяем cookie if request.cookies and "access_token" in request.cookies: token = request.cookies.get("access_token") # Проверяем заголовок Authorization elif "authorization" in request.headers: auth_header = request.headers.get("authorization", "") if auth_header and auth_header.startswith("Bearer "): token = auth_header.replace("Bearer ", "") # Если токена нет, перенаправляем на страницу входа if not token: if path.startswith("/api/"): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Требуется аутентификация" ) return RedirectResponse(url="/login", status_code=302) # Проверяем токен try: payload = decode_access_token(token) if payload is None: # Токен невалидный или истек if path.startswith("/api/"): # Для API запросов возвращаем JSON с ошибкой raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Токен аутентификации истек или недействителен" ) # Для HTML запросов перенаправляем на страницу входа return RedirectResponse(url="/login?expired=1", status_code=302) except Exception as e: # Ошибка при декодировании токена logger.warning(f"Error decoding token: {e}") if path.startswith("/api/"): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Ошибка проверки токена аутентификации" ) return RedirectResponse(url="/login?expired=1", status_code=302) # Добавляем пользователя в request state request.state.user = payload.get("sub") return await call_next(request)