This commit is contained in:
@@ -44,12 +44,6 @@ class EnhancedGraphQLHTTPHandler(GraphQLHTTPHandler):
|
||||
except Exception as e:
|
||||
logger.debug(f"[graphql] Ошибка при получении заголовков: {e}")
|
||||
|
||||
logger.debug(f"[graphql] Заголовки в get_context_for_request: {list(headers.keys())}")
|
||||
if "authorization" in headers:
|
||||
logger.debug(f"[graphql] Authorization header найден: {headers['authorization'][:50]}...")
|
||||
else:
|
||||
logger.debug("[graphql] Authorization header НЕ найден")
|
||||
|
||||
# Получаем стандартный контекст от базового класса
|
||||
context = await super().get_context_for_request(request, data)
|
||||
|
||||
@@ -67,15 +61,6 @@ class EnhancedGraphQLHTTPHandler(GraphQLHTTPHandler):
|
||||
auth_cred: Any | None = request.scope.get("auth")
|
||||
context["auth"] = auth_cred
|
||||
# Безопасно логируем информацию о типе объекта auth
|
||||
logger.debug(f"[graphql] Добавлены данные авторизации в контекст из scope: {type(auth_cred).__name__}")
|
||||
|
||||
# Проверяем, есть ли токен в auth_cred
|
||||
if auth_cred is not None and hasattr(auth_cred, "token") and auth_cred.token:
|
||||
token_val = auth_cred.token
|
||||
token_len = len(token_val) if hasattr(token_val, "__len__") else 0
|
||||
logger.debug(f"[graphql] Токен найден в auth_cred: {token_len}")
|
||||
else:
|
||||
logger.debug("[graphql] Токен НЕ найден в auth_cred")
|
||||
|
||||
# Добавляем author_id в контекст для RBAC
|
||||
author_id = None
|
||||
@@ -89,16 +74,8 @@ class EnhancedGraphQLHTTPHandler(GraphQLHTTPHandler):
|
||||
try:
|
||||
author_id_int = int(str(author_id).strip())
|
||||
context["author"] = {"id": author_id_int}
|
||||
logger.debug(f"[graphql] Добавлен author_id в контекст: {author_id_int}")
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.error(f"[graphql] Ошибка преобразования author_id {author_id}: {e}")
|
||||
context["author"] = {"id": author_id}
|
||||
logger.debug(f"[graphql] Добавлен author_id как строка: {author_id}")
|
||||
else:
|
||||
logger.debug("[graphql] author_id не найден в auth_cred")
|
||||
else:
|
||||
logger.debug("[graphql] Данные авторизации НЕ найдены в scope")
|
||||
|
||||
logger.debug("[graphql] Подготовлен расширенный контекст для запроса")
|
||||
|
||||
return context
|
||||
|
||||
@@ -28,7 +28,6 @@ from settings import (
|
||||
SESSION_TOKEN_HEADER,
|
||||
)
|
||||
from storage.db import local_session
|
||||
from storage.redis import redis as redis_adapter
|
||||
from utils.logger import root_logger as logger
|
||||
|
||||
ADMIN_EMAILS = ADMIN_EMAILS_LIST.split(",")
|
||||
@@ -82,7 +81,6 @@ class AuthMiddleware:
|
||||
async def authenticate_user(self, token: str) -> tuple[AuthCredentials, AuthenticatedUser | UnauthenticatedUser]:
|
||||
"""Аутентифицирует пользователя по токену"""
|
||||
if not token:
|
||||
logger.debug("[auth.authenticate] Токен отсутствует")
|
||||
return AuthCredentials(
|
||||
author_id=None, scopes={}, logged_in=False, error_message="no token", email=None, token=None
|
||||
), UnauthenticatedUser()
|
||||
@@ -202,24 +200,6 @@ class AuthMiddleware:
|
||||
scope_headers = scope.get("headers", [])
|
||||
if scope_headers:
|
||||
headers.update({k.decode("utf-8").lower(): v.decode("utf-8") for k, v in scope_headers})
|
||||
logger.debug(f"[middleware] Получены заголовки из scope: {len(headers)}")
|
||||
# Проверяем наличие authorization заголовка
|
||||
if "authorization" in headers:
|
||||
logger.debug(f"[middleware] Authorization заголовок найден: {headers['authorization'][:50]}...")
|
||||
else:
|
||||
logger.debug("[middleware] Authorization заголовок НЕ найден в scope headers")
|
||||
else:
|
||||
logger.debug("[middleware] Заголовки scope отсутствуют")
|
||||
|
||||
# Логируем все заголовки для диагностики
|
||||
logger.debug(f"[middleware] Все заголовки: {list(headers.keys())}")
|
||||
|
||||
# Логируем конкретные заголовки для диагностики
|
||||
auth_header_value = headers.get("authorization", "")
|
||||
logger.debug(f"[middleware] Authorization header: {auth_header_value[:50]}...")
|
||||
|
||||
session_token_value = headers.get(SESSION_TOKEN_HEADER.lower(), "")
|
||||
logger.debug(f"[middleware] {SESSION_TOKEN_HEADER} header: {session_token_value[:50]}...")
|
||||
|
||||
# Используем тот же механизм получения токена, что и в декораторе
|
||||
token = None
|
||||
@@ -227,100 +207,31 @@ class AuthMiddleware:
|
||||
# 0. Проверяем сохраненный токен в scope (приоритет)
|
||||
if "auth_token" in scope:
|
||||
token = scope["auth_token"]
|
||||
logger.debug(f"[middleware] Токен получен из scope.auth_token: {len(token)}")
|
||||
else:
|
||||
logger.debug("[middleware] scope.auth_token НЕ найден")
|
||||
|
||||
# Стандартная система сессий уже обрабатывает кэширование
|
||||
# Дополнительной проверки Redis кэша не требуется
|
||||
|
||||
# Отладка: детальная информация о запросе без Authorization
|
||||
if not token:
|
||||
# Проверяем, есть ли активные сессии в Redis
|
||||
try:
|
||||
# Получаем все активные сессии
|
||||
session_keys = await redis_adapter.keys("session:*")
|
||||
logger.debug(f"[middleware] Найдено активных сессий в Redis: {len(session_keys)}")
|
||||
|
||||
if session_keys:
|
||||
# Пытаемся найти токен через активные сессии
|
||||
for session_key in session_keys[:3]: # Проверяем первые 3 сессии
|
||||
try:
|
||||
session_data = await redis_adapter.hgetall(session_key)
|
||||
if session_data:
|
||||
logger.debug(f"[middleware] Найдена активная сессия: {session_key}")
|
||||
# Извлекаем user_id из ключа сессии
|
||||
user_id = (
|
||||
session_key.decode("utf-8").split(":")[1]
|
||||
if isinstance(session_key, bytes)
|
||||
else session_key.split(":")[1]
|
||||
)
|
||||
logger.debug(f"[middleware] User ID из сессии: {user_id}")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.debug(f"[middleware] Ошибка чтения сессии {session_key}: {e}")
|
||||
else:
|
||||
logger.debug("[middleware] Активных сессий в Redis не найдено")
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"[middleware] Ошибка проверки сессий: {e}")
|
||||
|
||||
# 1. Проверяем заголовок Authorization
|
||||
if not token:
|
||||
auth_header = headers.get("authorization", "")
|
||||
if auth_header:
|
||||
if auth_header.startswith("Bearer "):
|
||||
token = auth_header[7:].strip()
|
||||
logger.debug(f"[middleware] Токен получен из заголовка Authorization: {len(token)}")
|
||||
else:
|
||||
token = auth_header.strip()
|
||||
logger.debug(f"[middleware] Прямой токен получен из заголовка Authorization: {len(token)}")
|
||||
token = auth_header[7:].strip() if auth_header.startswith("Bearer ") else auth_header.strip()
|
||||
|
||||
# 2. Проверяем основной заголовок авторизации, если Authorization не найден
|
||||
if not token:
|
||||
auth_header = headers.get(SESSION_TOKEN_HEADER.lower(), "")
|
||||
if auth_header:
|
||||
if auth_header.startswith("Bearer "):
|
||||
token = auth_header[7:].strip()
|
||||
logger.debug(f"[middleware] Токен получен из заголовка {SESSION_TOKEN_HEADER}: {len(token)}")
|
||||
else:
|
||||
token = auth_header.strip()
|
||||
logger.debug(f"[middleware] Прямой токен получен из заголовка {SESSION_TOKEN_HEADER}: {len(token)}")
|
||||
token = auth_header[7:].strip() if auth_header.startswith("Bearer ") else auth_header.strip()
|
||||
|
||||
# 3. Проверяем cookie
|
||||
if not token:
|
||||
cookies = headers.get("cookie", "")
|
||||
logger.debug(f"[middleware] Проверяем cookies: {cookies[:100]}...")
|
||||
logger.debug(f"[middleware] Ищем cookie с именем: '{SESSION_COOKIE_NAME}'")
|
||||
|
||||
# 🔍 Диагностика cookies (только для debug уровня)
|
||||
if not cookies:
|
||||
logger.debug("[middleware] Cookie заголовок отсутствует")
|
||||
logger.debug(f"[middleware] Доступные заголовки: {list(headers.keys())}")
|
||||
# 💋 OAuth не использует cookies - это нормальное поведение
|
||||
logger.debug("[middleware] OAuth система работает без cookies - токены передаются через заголовки")
|
||||
|
||||
cookie_items = cookies.split(";")
|
||||
found_cookies = []
|
||||
for item in cookie_items:
|
||||
if "=" in item:
|
||||
name, value = item.split("=", 1)
|
||||
cookie_name = name.strip()
|
||||
found_cookies.append(cookie_name)
|
||||
if cookie_name == SESSION_COOKIE_NAME:
|
||||
token = value.strip()
|
||||
logger.debug(
|
||||
f"[middleware] ✅ Токен получен из cookie {SESSION_COOKIE_NAME}: {len(token)} символов"
|
||||
)
|
||||
break
|
||||
logger.debug(f"[middleware] Найденные cookies: {found_cookies}")
|
||||
if not token:
|
||||
logger.debug(f"[middleware] ❌ Cookie '{SESSION_COOKIE_NAME}' не найден среди: {found_cookies}")
|
||||
|
||||
if token:
|
||||
logger.debug(f"[middleware] Токен найден: {len(token)} символов")
|
||||
else:
|
||||
logger.debug("[middleware] Токен не найден")
|
||||
if cookies:
|
||||
cookie_items = cookies.split(";")
|
||||
for item in cookie_items:
|
||||
if "=" in item:
|
||||
name, value = item.split("=", 1)
|
||||
cookie_name = name.strip()
|
||||
if cookie_name == SESSION_COOKIE_NAME:
|
||||
token = value.strip()
|
||||
break
|
||||
|
||||
# Аутентифицируем пользователя
|
||||
auth, user = await self.authenticate_user(token or "")
|
||||
@@ -332,21 +243,12 @@ class AuthMiddleware:
|
||||
# Сохраняем токен в scope для использования в последующих запросах
|
||||
if token:
|
||||
scope["auth_token"] = token
|
||||
logger.debug(f"[middleware] Токен сохранен в scope.auth_token: {len(token)}")
|
||||
logger.debug(f"[middleware] Пользователь аутентифицирован: {user.is_authenticated}")
|
||||
|
||||
# Токен уже сохранен в стандартной системе сессий через SessionTokenManager
|
||||
# Дополнительного кэширования не требуется
|
||||
logger.debug("[middleware] Токен обработан стандартной системой сессий")
|
||||
else:
|
||||
logger.debug("[middleware] Токен не найден, пользователь неаутентифицирован")
|
||||
|
||||
await self.app(scope, receive, send)
|
||||
|
||||
def set_context(self, context) -> None:
|
||||
"""Сохраняет ссылку на контекст GraphQL запроса"""
|
||||
self._context = context
|
||||
logger.debug(f"[middleware] Установлен контекст GraphQL: {bool(context)}")
|
||||
|
||||
def set_cookie(self, key: str, value: str, **options: Any) -> None:
|
||||
"""
|
||||
@@ -363,7 +265,6 @@ class AuthMiddleware:
|
||||
if self._context and "response" in self._context and hasattr(self._context["response"], "set_cookie"):
|
||||
try:
|
||||
self._context["response"].set_cookie(key, value, **options)
|
||||
logger.debug(f"[middleware] Установлена cookie {key} через response")
|
||||
success = True
|
||||
except Exception as e:
|
||||
logger.error(f"[middleware] Ошибка при установке cookie {key} через response: {e!s}")
|
||||
@@ -372,7 +273,6 @@ class AuthMiddleware:
|
||||
if not success and hasattr(self, "_response") and self._response and hasattr(self._response, "set_cookie"):
|
||||
try:
|
||||
self._response.set_cookie(key, value, **options)
|
||||
logger.debug(f"[middleware] Установлена cookie {key} через _response")
|
||||
success = True
|
||||
except Exception as e:
|
||||
logger.error(f"[middleware] Ошибка при установке cookie {key} через _response: {e!s}")
|
||||
@@ -390,7 +290,6 @@ class AuthMiddleware:
|
||||
if self._context and "response" in self._context and hasattr(self._context["response"], "delete_cookie"):
|
||||
try:
|
||||
self._context["response"].delete_cookie(key, **options)
|
||||
logger.debug(f"[middleware] Удалена cookie {key} через response")
|
||||
success = True
|
||||
except Exception as e:
|
||||
logger.error(f"[middleware] Ошибка при удалении cookie {key} через response: {e!s}")
|
||||
@@ -399,7 +298,6 @@ class AuthMiddleware:
|
||||
if not success and hasattr(self, "_response") and self._response and hasattr(self._response, "delete_cookie"):
|
||||
try:
|
||||
self._response.delete_cookie(key, **options)
|
||||
logger.debug(f"[middleware] Удалена cookie {key} через _response")
|
||||
success = True
|
||||
except Exception as e:
|
||||
logger.error(f"[middleware] Ошибка при удалении cookie {key} через _response: {e!s}")
|
||||
@@ -427,9 +325,6 @@ class AuthMiddleware:
|
||||
# Проверяем наличие response в контексте
|
||||
if "response" not in context or not context["response"]:
|
||||
context["response"] = JSONResponse({})
|
||||
logger.debug("[middleware] Создан новый response объект в контексте GraphQL")
|
||||
|
||||
logger.debug("[middleware] GraphQL resolve: контекст подготовлен, добавлены расширения для работы с cookie")
|
||||
|
||||
return await next_resolver(root, info, *args, **kwargs)
|
||||
except Exception as e:
|
||||
@@ -457,16 +352,8 @@ class AuthMiddleware:
|
||||
data = await request.json()
|
||||
op_name = data.get("operationName", "").lower()
|
||||
|
||||
# 💋 OAuth НЕ использует cookies - токены передаются только через заголовки/localStorage
|
||||
# Убираем автоматическую установку cookies для login/refreshtoken/getSession
|
||||
if op_name in ["login", "refreshtoken", "getsession"]:
|
||||
logger.debug(f"[graphql_handler] Операция {op_name}: токены передаются БЕЗ cookies")
|
||||
logger.debug(
|
||||
"[graphql_handler] Фронтенд должен извлечь токен из ответа и управлять им самостоятельно"
|
||||
)
|
||||
|
||||
# Если это операция logout, удаляем cookie
|
||||
elif op_name == "logout":
|
||||
if op_name == "logout":
|
||||
response.delete_cookie(
|
||||
key=SESSION_COOKIE_NAME,
|
||||
secure=SESSION_COOKIE_SECURE,
|
||||
@@ -474,9 +361,8 @@ class AuthMiddleware:
|
||||
samesite=SESSION_COOKIE_SAMESITE
|
||||
if SESSION_COOKIE_SAMESITE in ["strict", "lax", "none"]
|
||||
else "none",
|
||||
domain=SESSION_COOKIE_DOMAIN, # ✅ КРИТИЧНО: тот же domain что при установке
|
||||
domain=SESSION_COOKIE_DOMAIN,
|
||||
)
|
||||
logger.debug(f"[graphql_handler] Удалена cookie {SESSION_COOKIE_NAME} для операции {op_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"[process_result] Ошибка при обработке POST запроса: {e!s}")
|
||||
|
||||
|
||||
@@ -342,14 +342,14 @@ def get_shouts_with_links(
|
||||
if has_field(info, "main_topic") or force_topics:
|
||||
main_topic = None
|
||||
if hasattr(row, "main_topic"):
|
||||
logger.debug(f"Raw main_topic for shout#{shout_id}: {row.main_topic}")
|
||||
# logger.debug(f"Raw main_topic for shout#{shout_id}: {row.main_topic}")
|
||||
main_topic = (
|
||||
orjson.loads(row.main_topic) if isinstance(row.main_topic, str) else row.main_topic
|
||||
)
|
||||
logger.debug(f"Parsed main_topic for shout#{shout_id}: {main_topic}")
|
||||
# logger.debug(f"Parsed main_topic for shout#{shout_id}: {main_topic}")
|
||||
|
||||
if not main_topic and topics and len(topics) > 0:
|
||||
logger.info(f"No main_topic found for shout#{shout_id}, using first topic from list")
|
||||
# logger.info(f"No main_topic found for shout#{shout_id}, using first topic from list")
|
||||
main_topic = {
|
||||
"id": topics[0]["id"],
|
||||
"title": topics[0]["title"],
|
||||
@@ -365,7 +365,7 @@ def get_shouts_with_links(
|
||||
"is_main": True,
|
||||
}
|
||||
shout_dict["main_topic"] = main_topic
|
||||
logger.debug(f"Final main_topic for shout#{shout_id}: {main_topic}")
|
||||
# logger.debug(f"Final main_topic for shout#{shout_id}: {main_topic}")
|
||||
|
||||
if has_field(info, "authors") and hasattr(row, "authors"):
|
||||
authors_data = orjson.loads(row.authors) if isinstance(row.authors, str) else row.authors
|
||||
|
||||
Reference in New Issue
Block a user