core/auth/middleware.py

112 lines
5.1 KiB
Python
Raw Normal View History

2025-05-16 06:23:48 +00:00
"""
Middleware для обработки авторизации в GraphQL запросах
"""
from starlette.datastructures import Headers
from starlette.types import ASGIApp, Scope, Receive, Send
from utils.logger import root_logger as logger
from settings import SESSION_TOKEN_HEADER, SESSION_COOKIE_NAME
2025-05-19 08:25:41 +00:00
class AuthMiddleware:
2025-05-16 06:23:48 +00:00
"""
2025-05-19 08:25:41 +00:00
Универсальный middleware для обработки авторизации и управления cookies.
Основные функции:
1. Извлечение Bearer токена из заголовка Authorization или cookie
2. Добавление токена в заголовки запроса для обработки AuthenticationMiddleware
3. Предоставление методов для установки/удаления cookies в GraphQL резолверах
2025-05-16 06:23:48 +00:00
"""
def __init__(self, app: ASGIApp):
self.app = app
2025-05-19 08:25:41 +00:00
self._context = None
2025-05-16 06:23:48 +00:00
async def __call__(self, scope: Scope, receive: Receive, send: Send):
2025-05-19 08:25:41 +00:00
"""Обработка ASGI запроса"""
2025-05-16 06:23:48 +00:00
if scope["type"] != "http":
await self.app(scope, receive, send)
return
# Извлекаем заголовки
headers = Headers(scope=scope)
auth_header = headers.get(SESSION_TOKEN_HEADER)
token = None
# Сначала пробуем получить токен из заголовка Authorization
if auth_header:
if auth_header.startswith("Bearer "):
token = auth_header.replace("Bearer ", "", 1).strip()
logger.debug(
f"[middleware] Извлечен Bearer токен из заголовка, длина: {len(token) if token else 0}"
)
# Если токен не получен из заголовка, пробуем взять из cookie
if not token:
cookies = headers.get("cookie", "")
cookie_items = cookies.split(";")
for item in cookie_items:
if "=" in item:
name, value = item.split("=", 1)
if name.strip() == SESSION_COOKIE_NAME:
token = value.strip()
logger.debug(
f"[middleware] Извлечен токен из cookie, длина: {len(token) if token else 0}"
)
break
# Если токен получен, обновляем заголовки в scope
if token:
# Создаем новый список заголовков
new_headers = []
for name, value in scope["headers"]:
# Пропускаем оригинальный заголовок авторизации
if name.decode("latin1").lower() != SESSION_TOKEN_HEADER.lower():
new_headers.append((name, value))
# Добавляем заголовок с чистым токеном
new_headers.append((SESSION_TOKEN_HEADER.encode("latin1"), token.encode("latin1")))
# Обновляем заголовки в scope
scope["headers"] = new_headers
# Также добавляем информацию о типе аутентификации для дальнейшего использования
if "auth" not in scope:
scope["auth"] = {"type": "bearer", "token": token}
await self.app(scope, receive, send)
2025-05-19 08:25:41 +00:00
def set_context(self, context):
"""Сохраняет ссылку на контекст GraphQL запроса"""
self._context = context
2025-05-16 06:23:48 +00:00
def set_cookie(self, key, value, **options):
"""Устанавливает cookie в ответе"""
2025-05-19 08:25:41 +00:00
if self._context and "response" in self._context and hasattr(self._context["response"], "set_cookie"):
self._context["response"].set_cookie(key, value, **options)
2025-05-16 06:23:48 +00:00
def delete_cookie(self, key, **options):
"""Удаляет cookie из ответа"""
2025-05-19 08:25:41 +00:00
if self._context and "response" in self._context and hasattr(self._context["response"], "delete_cookie"):
self._context["response"].delete_cookie(key, **options)
2025-05-16 06:23:48 +00:00
async def resolve(self, next, root, info, *args, **kwargs):
"""
Middleware для обработки запросов GraphQL.
Добавляет методы для установки cookie в контекст.
"""
try:
# Получаем доступ к контексту запроса
context = info.context
2025-05-19 08:25:41 +00:00
2025-05-16 06:23:48 +00:00
# Сохраняем ссылку на контекст
2025-05-19 08:25:41 +00:00
self.set_context(context)
2025-05-16 06:23:48 +00:00
# Добавляем себя как объект, содержащий утилитные методы
context["extensions"] = self
2025-05-19 08:25:41 +00:00
2025-05-16 06:23:48 +00:00
return await next(root, info, *args, **kwargs)
except Exception as e:
2025-05-19 08:25:41 +00:00
logger.error(f"[AuthMiddleware] Ошибка в GraphQL resolve: {str(e)}")
2025-05-16 06:23:48 +00:00
raise