changelog-restored+internal-auth-fix
All checks were successful
Deploy on push / deploy (push) Successful in 6s

This commit is contained in:
2025-06-30 23:10:48 +03:00
parent ab65fd4fd8
commit b01de1fdc1
7 changed files with 322 additions and 128 deletions

View File

@@ -4,17 +4,15 @@
"""
import time
from typing import Any, Optional
from typing import Optional
from sqlalchemy.orm import exc
from auth.credentials import AuthCredentials
from auth.orm import Author
from auth.state import AuthState
from auth.tokens.storage import TokenStorage as TokenManager
from services.db import local_session
from settings import ADMIN_EMAILS as ADMIN_EMAILS_LIST
from settings import SESSION_COOKIE_NAME, SESSION_TOKEN_HEADER
from utils.logger import root_logger as logger
ADMIN_EMAILS = ADMIN_EMAILS_LIST.split(",")
@@ -90,99 +88,63 @@ async def create_internal_session(author: Author, device_info: Optional[dict] =
)
async def authenticate(request: Any) -> AuthState:
async def authenticate(request) -> AuthState:
"""
Аутентифицирует запрос по токену из разных источников.
Порядок проверки:
1. Проверяет токен в заголовке Authorization
2. Проверяет токен в cookie
Аутентифицирует пользователя по токену из запроса.
Args:
request: Запрос (обычно из middleware)
request: Объект запроса
Returns:
AuthState: Состояние авторизации
AuthState: Состояние аутентификации
"""
state = AuthState()
state.logged_in = False # Изначально считаем, что пользователь не авторизован
token = None
from auth.decorators import get_auth_token
from auth.tokens.sessions import SessionTokenManager
from utils.logger import root_logger as logger
# Проверяем наличие auth в scope (установлено middleware)
if hasattr(request, "scope") and isinstance(request.scope, dict) and "auth" in request.scope:
auth_info = request.scope.get("auth", {})
if isinstance(auth_info, dict) and "token" in auth_info:
token = auth_info["token"]
logger.debug("[auth.authenticate] Извлечен токен из request.scope['auth']")
logger.debug("[authenticate] Начало аутентификации")
# Если токен не найден в scope, проверяем заголовок
# Получаем токен из запроса
token = get_auth_token(request)
if not token:
try:
headers = {}
if hasattr(request, "headers"):
headers = dict(request.headers()) if callable(request.headers) else dict(request.headers)
logger.warning("[authenticate] Токен не найден в запросе")
auth_state = AuthState()
auth_state.logged_in = False
auth_state.author_id = None
auth_state.error = "No authentication token provided"
auth_state.token = None
return auth_state
auth_header = headers.get(SESSION_TOKEN_HEADER, "")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header[7:].strip()
logger.debug(f"[auth.authenticate] Токен получен из заголовка {SESSION_TOKEN_HEADER}")
elif auth_header:
token = auth_header.strip()
logger.debug(f"[auth.authenticate] Прямой токен получен из заголовка {SESSION_TOKEN_HEADER}")
except Exception as e:
logger.error(f"[auth.authenticate] Ошибка при доступе к заголовкам: {e}")
logger.debug(f"[authenticate] Токен найден, длина: {len(token)}")
# Если и в заголовке не найден, проверяем cookie
if not token and hasattr(request, "cookies") and request.cookies:
token = request.cookies.get(SESSION_COOKIE_NAME)
if token:
logger.debug(f"[auth.authenticate] Токен получен из cookie {SESSION_COOKIE_NAME}")
# Проверяем токен
try:
# Создаем экземпляр SessionTokenManager
session_manager = SessionTokenManager()
# Проверяем токен
auth_result = await session_manager.verify_session(token)
# Если токен все еще не найден, возвращаем не авторизованное состояние
if not token:
logger.debug("[auth.authenticate] Токен не найден")
return state
# Проверяем токен через TokenStorage, который теперь совместим с TokenStorage
payload = await TokenManager.verify_session(token)
if not payload:
logger.warning("[auth.authenticate] Токен не валиден: не найдена сессия")
state.error = "Invalid or expired token"
return state
# Создаем успешное состояние авторизации
state.logged_in = True
state.author_id = payload.user_id
state.token = token
state.username = payload.username
# Если запрос имеет атрибут auth, устанавливаем в него авторизационные данные
if hasattr(request, "scope") and isinstance(request.scope, dict):
try:
# Получаем информацию о пользователе для создания AuthCredentials
with local_session() as session:
author = session.query(Author).filter(Author.id == payload.user_id).one_or_none()
if author:
# Получаем разрешения из ролей
scopes = author.get_permissions()
# Создаем объект авторизации
auth_cred = AuthCredentials(
author_id=author.id,
scopes=scopes,
logged_in=True,
email=author.email,
token=token,
error_message="",
)
# Устанавливаем auth в request.scope вместо прямого присваивания к request.auth
request.scope["auth"] = auth_cred
logger.debug(
f"[auth.authenticate] Авторизационные данные установлены в request.scope['auth'] для {payload.user_id}"
)
except Exception as e:
logger.error(f"[auth.authenticate] Ошибка при установке auth в request.scope: {e}")
logger.info(f"[auth.authenticate] Успешная аутентификация пользователя {state.author_id}")
return state
if auth_result and hasattr(auth_result, "user_id"):
logger.debug(f"[authenticate] Успешная аутентификация, user_id: {auth_result.user_id}")
auth_state = AuthState()
auth_state.logged_in = True
auth_state.author_id = auth_result.user_id
auth_state.error = None
auth_state.token = token
return auth_state
error_msg = "Invalid or expired token"
logger.warning(f"[authenticate] Недействительный токен: {error_msg}")
auth_state = AuthState()
auth_state.logged_in = False
auth_state.author_id = None
auth_state.error = error_msg
auth_state.token = None
return auth_state
except Exception as e:
logger.error(f"[authenticate] Ошибка при проверке токена: {e}")
auth_state = AuthState()
auth_state.logged_in = False
auth_state.author_id = None
auth_state.error = f"Authentication error: {e!s}"
auth_state.token = None
return auth_state