2024-01-25 19:41:27 +00:00
|
|
|
|
from functools import wraps
|
2025-05-16 06:23:48 +00:00
|
|
|
|
from typing import Tuple
|
2024-04-08 07:38:58 +00:00
|
|
|
|
|
2024-08-07 05:57:56 +00:00
|
|
|
|
from cache.cache import get_cached_author_by_user_id
|
2024-08-09 06:37:06 +00:00
|
|
|
|
from resolvers.stat import get_with_stat
|
|
|
|
|
from utils.logger import root_logger as logger
|
2025-05-16 06:23:48 +00:00
|
|
|
|
from auth.internal import verify_internal_auth
|
|
|
|
|
from sqlalchemy import exc
|
|
|
|
|
from services.db import local_session
|
|
|
|
|
from auth.orm import Author, Role
|
2024-01-13 08:49:12 +00:00
|
|
|
|
|
2024-12-24 11:04:52 +00:00
|
|
|
|
# Список разрешенных заголовков
|
2025-01-21 07:09:28 +00:00
|
|
|
|
ALLOWED_HEADERS = ["Authorization", "Content-Type"]
|
|
|
|
|
|
2024-02-21 07:27:16 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
async def check_auth(req) -> Tuple[str, list[str]]:
|
2024-12-11 20:49:58 +00:00
|
|
|
|
"""
|
|
|
|
|
Проверка авторизации пользователя.
|
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
Проверяет токен и получает данные из локальной БД.
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
|
|
|
|
Параметры:
|
|
|
|
|
- req: Входящий GraphQL запрос, содержащий заголовок авторизации.
|
|
|
|
|
|
|
|
|
|
Возвращает:
|
2025-05-16 06:23:48 +00:00
|
|
|
|
- user_id: str - Идентификатор пользователя
|
|
|
|
|
- user_roles: list[str] - Список ролей пользователя
|
2024-12-11 20:49:58 +00:00
|
|
|
|
"""
|
2025-05-16 06:23:48 +00:00
|
|
|
|
# Проверяем наличие токена
|
2024-04-17 15:32:23 +00:00
|
|
|
|
token = req.headers.get("Authorization")
|
2025-05-16 06:23:48 +00:00
|
|
|
|
if not token:
|
|
|
|
|
return "", []
|
2025-01-21 07:09:28 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
# Очищаем токен от префикса Bearer если он есть
|
|
|
|
|
if token.startswith("Bearer "):
|
|
|
|
|
token = token.split("Bearer ")[-1].strip()
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
logger.debug(f"Checking auth token: {token[:10]}...")
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
# Проверяем авторизацию внутренним механизмом
|
|
|
|
|
logger.debug("Using internal authentication")
|
|
|
|
|
return await verify_internal_auth(token)
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2023-10-23 14:47:11 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
async def add_user_role(user_id: str, roles: list[str] = None):
|
|
|
|
|
"""
|
|
|
|
|
Добавление ролей пользователю в локальной БД.
|
2024-01-25 19:41:27 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
Args:
|
|
|
|
|
user_id: ID пользователя
|
|
|
|
|
roles: Список ролей для добавления. По умолчанию ["author", "reader"]
|
2024-12-11 20:49:58 +00:00
|
|
|
|
"""
|
2025-05-16 06:23:48 +00:00
|
|
|
|
if not roles:
|
|
|
|
|
roles = ["author", "reader"]
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
logger.info(f"Adding roles {roles} to user {user_id}")
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
logger.debug("Using local authentication")
|
|
|
|
|
with local_session() as session:
|
|
|
|
|
try:
|
|
|
|
|
author = session.query(Author).filter(Author.id == user_id).one()
|
2024-12-11 20:49:58 +00:00
|
|
|
|
|
2025-05-16 06:23:48 +00:00
|
|
|
|
# Получаем существующие роли
|
|
|
|
|
existing_roles = set(role.name for role in author.roles)
|
|
|
|
|
|
|
|
|
|
# Добавляем новые роли
|
|
|
|
|
for role_name in roles:
|
|
|
|
|
if role_name not in existing_roles:
|
|
|
|
|
# Получаем или создаем роль
|
|
|
|
|
role = session.query(Role).filter(Role.name == role_name).first()
|
|
|
|
|
if not role:
|
|
|
|
|
role = Role(id=role_name, name=role_name)
|
|
|
|
|
session.add(role)
|
|
|
|
|
|
|
|
|
|
# Добавляем роль автору
|
|
|
|
|
author.roles.append(role)
|
|
|
|
|
|
|
|
|
|
session.commit()
|
|
|
|
|
return user_id
|
|
|
|
|
|
|
|
|
|
except exc.NoResultFound:
|
|
|
|
|
logger.error(f"Author {user_id} not found")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def login_required(f):
|
|
|
|
|
"""Декоратор для проверки авторизации пользователя."""
|
2024-12-11 22:04:11 +00:00
|
|
|
|
|
2024-01-23 18:59:46 +00:00
|
|
|
|
@wraps(f)
|
2023-10-23 14:47:11 +00:00
|
|
|
|
async def decorated_function(*args, **kwargs):
|
|
|
|
|
info = args[1]
|
2024-04-17 15:32:23 +00:00
|
|
|
|
req = info.context.get("request")
|
2024-04-19 15:22:07 +00:00
|
|
|
|
user_id, user_roles = await check_auth(req)
|
|
|
|
|
if user_id and user_roles:
|
|
|
|
|
logger.info(f" got {user_id} roles: {user_roles}")
|
|
|
|
|
info.context["user_id"] = user_id.strip()
|
|
|
|
|
info.context["roles"] = user_roles
|
2024-05-20 23:01:18 +00:00
|
|
|
|
author = await get_cached_author_by_user_id(user_id, get_with_stat)
|
2024-04-30 13:18:50 +00:00
|
|
|
|
if not author:
|
2024-05-01 01:01:21 +00:00
|
|
|
|
logger.error(f"author profile not found for user {user_id}")
|
2024-04-19 15:22:07 +00:00
|
|
|
|
info.context["author"] = author
|
2024-02-27 11:16:54 +00:00
|
|
|
|
return await f(*args, **kwargs)
|
2023-10-23 14:47:11 +00:00
|
|
|
|
|
|
|
|
|
return decorated_function
|
2024-11-12 14:56:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def login_accepted(f):
|
2025-05-16 06:23:48 +00:00
|
|
|
|
"""Декоратор для добавления данных авторизации в контекст."""
|
2024-12-11 22:04:11 +00:00
|
|
|
|
|
2024-11-12 14:56:20 +00:00
|
|
|
|
@wraps(f)
|
|
|
|
|
async def decorated_function(*args, **kwargs):
|
|
|
|
|
info = args[1]
|
|
|
|
|
req = info.context.get("request")
|
2024-11-18 08:31:19 +00:00
|
|
|
|
|
2024-11-14 11:00:33 +00:00
|
|
|
|
logger.debug("login_accepted: Проверка авторизации пользователя.")
|
2024-11-12 14:56:20 +00:00
|
|
|
|
user_id, user_roles = await check_auth(req)
|
2024-11-14 11:00:33 +00:00
|
|
|
|
logger.debug(f"login_accepted: user_id={user_id}, user_roles={user_roles}")
|
2024-11-18 08:31:19 +00:00
|
|
|
|
|
2024-11-12 14:56:20 +00:00
|
|
|
|
if user_id and user_roles:
|
2024-11-14 11:00:33 +00:00
|
|
|
|
logger.info(f"login_accepted: Пользователь авторизован: {user_id} с ролями {user_roles}")
|
2024-11-12 14:56:20 +00:00
|
|
|
|
info.context["user_id"] = user_id.strip()
|
|
|
|
|
info.context["roles"] = user_roles
|
|
|
|
|
|
|
|
|
|
# Пробуем получить профиль автора
|
|
|
|
|
author = await get_cached_author_by_user_id(user_id, get_with_stat)
|
2024-11-14 11:00:33 +00:00
|
|
|
|
if author:
|
|
|
|
|
logger.debug(f"login_accepted: Найден профиль автора: {author}")
|
|
|
|
|
info.context["author"] = author.dict()
|
|
|
|
|
else:
|
2024-11-18 08:31:19 +00:00
|
|
|
|
logger.error(
|
2024-12-16 16:39:31 +00:00
|
|
|
|
f"login_accepted: Профиль автора не найден для пользователя {user_id}. Используем базовые данные."
|
2025-05-16 06:23:48 +00:00
|
|
|
|
)
|
2024-11-12 14:56:20 +00:00
|
|
|
|
else:
|
2024-11-14 11:00:33 +00:00
|
|
|
|
logger.debug("login_accepted: Пользователь не авторизован. Очищаем контекст.")
|
2024-11-12 14:56:20 +00:00
|
|
|
|
info.context["user_id"] = None
|
|
|
|
|
info.context["roles"] = None
|
|
|
|
|
info.context["author"] = None
|
|
|
|
|
|
|
|
|
|
return await f(*args, **kwargs)
|
2024-11-18 08:31:19 +00:00
|
|
|
|
|
2024-11-12 14:56:20 +00:00
|
|
|
|
return decorated_function
|