Files
DevOpsLab/app/auth/middleware.py
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

96 lines
4.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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)