### 🔄 Изменения - **SQLAlchemy KeyError** - исправление ошибки `KeyError: Reaction` при инициализации - **Исправлена ошибка SQLAlchemy**: Устранена проблема `InvalidRequestError: When initializing mapper Mapper[Shout(shout)], expression Reaction failed to locate a name (Reaction)` ### 🧪 Тестирование - **Исправление тестов** - адаптация к новой структуре моделей - **RBAC инициализация** - добавление `rbac.initialize_rbac()` в `conftest.py` - **Создан тест для getSession**: Добавлен комплексный тест `test_getSession_cookies.py` с проверкой всех сценариев - **Покрытие edge cases**: Тесты проверяют работу с валидными/невалидными токенами, отсутствующими пользователями - **Мокирование зависимостей**: Использование unittest.mock для изоляции тестируемого кода ### 🔧 Рефакторинг - **Упрощена архитектура**: Убраны сложные конструкции с отложенными импортами, заменены на чистую архитектуру - **Перемещение моделей** - `Author` и связанные модели перенесены в `orm/author.py`: Вынесены базовые модели пользователей (`Author`, `AuthorFollower`, `AuthorBookmark`, `AuthorRating`) из `orm.author` в отдельный модуль - **Устранены циклические импорты**: Разорван цикл между `auth.core` → `orm.community` → `orm.author` через реструктуризацию архитектуры - **Создан модуль `utils/password.py`**: Класс `Password` вынесен в utils для избежания циклических зависимостей - **Оптимизированы импорты моделей**: Убран прямой импорт `Shout` из `orm/community.py`, заменен на строковые ссылки ### 🔧 Авторизация с cookies - **getSession теперь работает с cookies**: Мутация `getSession` теперь может получать токен из httpOnly cookies даже без заголовка Authorization - **Убрано требование авторизации**: `getSession` больше не требует декоратор `@login_required`, работает автономно - **Поддержка dual-авторизации**: Токен может быть получен как из заголовка Authorization, так и из cookie `session_token` - **Автоматическая установка cookies**: Middleware автоматически устанавливает httpOnly cookies при успешном `getSession` - **Обновлена GraphQL схема**: `SessionInfo` теперь содержит поля `success`, `error` и опциональные `token`, `author` - **Единообразная обработка токенов**: Все модули теперь используют централизованные функции для работы с токенами - **Улучшена обработка ошибок**: Добавлена детальная валидация токенов и пользователей в `getSession` - **Логирование операций**: Добавлены подробные логи для отслеживания процесса авторизации ### 📝 Документация - **Обновлена схема GraphQL**: `SessionInfo` тип теперь соответствует новому формату ответа - Обновлена документация RBAC - Обновлена документация авторизации с cookies
This commit is contained in:
@@ -7,9 +7,10 @@ from typing import Any
|
||||
from graphql import GraphQLResolveInfo
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from auth.utils import extract_token_from_request, get_auth_token_from_context, get_user_data_by_token
|
||||
from services.auth import auth_service
|
||||
from storage.schema import mutation, query, type_author
|
||||
from settings import SESSION_COOKIE_NAME
|
||||
from storage.schema import mutation, query, type_author
|
||||
from utils.logger import root_logger as logger
|
||||
|
||||
# === РЕЗОЛВЕР ДЛЯ ТИПА AUTHOR ===
|
||||
@@ -121,11 +122,7 @@ async def logout(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dict[str,
|
||||
# Получаем токен
|
||||
token = None
|
||||
if request:
|
||||
token = request.cookies.get(SESSION_COOKIE_NAME)
|
||||
if not token:
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if auth_header and auth_header.startswith("Bearer "):
|
||||
token = auth_header[7:]
|
||||
token = await extract_token_from_request(request)
|
||||
|
||||
result = await auth_service.logout(user_id, token)
|
||||
|
||||
@@ -158,11 +155,7 @@ async def refresh_token(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dic
|
||||
return {"success": False, "token": None, "author": None, "error": "Запрос не найден"}
|
||||
|
||||
# Получаем токен
|
||||
token = request.cookies.get(SESSION_COOKIE_NAME)
|
||||
if not token:
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if auth_header and auth_header.startswith("Bearer "):
|
||||
token = auth_header[7:]
|
||||
token = await extract_token_from_request(request)
|
||||
|
||||
if not token:
|
||||
return {"success": False, "token": None, "author": None, "error": "Токен не найден"}
|
||||
@@ -262,21 +255,25 @@ async def cancel_email_change(_: None, info: GraphQLResolveInfo, **kwargs: Any)
|
||||
|
||||
|
||||
@mutation.field("getSession")
|
||||
@auth_service.login_required
|
||||
async def get_session(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dict[str, Any]:
|
||||
"""Получает информацию о текущей сессии"""
|
||||
try:
|
||||
# Получаем токен из контекста (установлен декоратором login_required)
|
||||
token = info.context.get("token")
|
||||
author = info.context.get("author")
|
||||
token = await get_auth_token_from_context(info)
|
||||
|
||||
if not token:
|
||||
return {"success": False, "token": None, "author": None, "error": "Токен не найден"}
|
||||
logger.debug("[getSession] Токен не найден")
|
||||
return {"success": False, "token": None, "author": None, "error": "Сессия не найдена"}
|
||||
|
||||
if not author:
|
||||
return {"success": False, "token": None, "author": None, "error": "Пользователь не найден"}
|
||||
# Используем DRY функцию для получения данных пользователя
|
||||
success, user_data, error_message = await get_user_data_by_token(token)
|
||||
|
||||
if success and user_data:
|
||||
user_id = user_data.get("id", "NO_ID")
|
||||
logger.debug(f"[getSession] Сессия валидна для пользователя {user_id}")
|
||||
return {"success": True, "token": token, "author": user_data, "error": None}
|
||||
logger.warning(f"[getSession] Ошибка валидации токена: {error_message}")
|
||||
return {"success": False, "token": None, "author": None, "error": error_message}
|
||||
|
||||
return {"success": True, "token": token, "author": author, "error": None}
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка получения сессии: {e}")
|
||||
return {"success": False, "token": None, "author": None, "error": str(e)}
|
||||
|
||||
Reference in New Issue
Block a user