Подготовка к переносу классов моделей

This commit is contained in:
2026-07-13 12:43:19 +03:00
parent c9a75b3370
commit c5e4c88588
8 changed files with 338 additions and 27 deletions

View File

@@ -8,15 +8,20 @@
Описание класса SessionManager
"""
from typing import NoReturn
from typing import Type
from django.apps import apps
from django.contrib.auth import authenticate
from environ import ImproperlyConfigured
from net.xeaf.rack.enums import AuthRejectReason
from net.xeaf.rack.managers import ConfigurationManager
from net.xeaf.rack.managers import CryptoManager
from net.xeaf.rack.models import AccountModel
from net.xeaf.rack.models import SessionModel
from net.xeaf.rack.utils.exceptions import UnauthorizedException
from .account_manager import AccountManager
class SessionManager[T:SessionModel]:
@@ -27,21 +32,39 @@ class SessionManager[T:SessionModel]:
DEFAULT_SESSION_TTL = 3600
""" Время жизни сессии по умолчанию """
__model_class: Type[T] | None
""" Хранилище для класса сессии """
@classmethod
def authenticate(cls, username: str, password: str) -> SessionModel:
def authenticate(cls, username: str, password: str, device_id: str | None = None) -> T:
"""
Авторизует новую сессию
:param username: Имя пользователя
:param password: Пароль
:param device_id: Идентификатор устройства
:return: Объект данных сессии
"""
pass
# noinspection PyTypeChecker
account: AccountModel | None = authenticate(username=username, password=password)
# Обрабатываем ошибки
if account is None:
cls._raise_auth_error(AuthRejectReason.INVALID_USERNAME_OR_PASSWORD)
elif not account.is_active:
cls._raise_auth_error(AuthRejectReason.ACCOUNT_IS_NOT_ACTIVE)
elif account.joined_at is None:
cls._raise_auth_error(AuthRejectReason.REGISTRATION_IS_NOT_COMPLETE)
# Создаем и возвращаем объект сессии
AccountManager.restore_account(account)
session = cls.open_session(account, account, device_id)
return session
@classmethod
def find_by_token(cls, token: str) -> SessionModel | None:
def find_by_token(cls, token: str) -> T | None:
"""
Возвращает объект данных сессии по токену
@@ -50,7 +73,8 @@ class SessionManager[T:SessionModel]:
:return: Объект данных сессии или None, если не найдена или просрочена
"""
session = SessionModel.objects.filter(token__exact=token).first()
model = cls._get_session_model_class()
session = model.objects.filter(token__exact=token).first()
if session is not None and not session.is_expired():
return session
@@ -74,7 +98,17 @@ class SessionManager[T:SessionModel]:
return session
@classmethod
def renew_session(cls, session: SessionModel) -> None:
def close_session(cls, session: T) -> None:
"""
Закрывает сессию пользователя
:param session: Модель данных сессии
"""
session.expire(save=True)
@classmethod
def renew_session(cls, session: T) -> None:
"""
Обновляет время существование сессии
@@ -84,16 +118,6 @@ class SessionManager[T:SessionModel]:
ttl = ConfigurationManager.get_int("SESSION_TTL", cls.DEFAULT_SESSION_TTL)
session.renew(ttl, save=True)
@classmethod
def close_session(cls, session: SessionModel) -> None:
"""
Закрывает сессию пользователя
:param session: Модель данных сессии
"""
session.expire(save=True)
@classmethod
def _create_session_model(cls, account: AccountModel, actual_account: AccountModel) -> T:
"""
@@ -118,16 +142,31 @@ class SessionManager[T:SessionModel]:
:return: Класс модели данных сессии
"""
model_path = ConfigurationManager.get_string("AUTH_SESSION_MODEL", "net_xeaf_rack.AccountModel")
app_label, model_name = model_path.split(".")
result = apps.get_model(app_label, model_name)
if cls.__model_class is None:
model_path = ConfigurationManager.get_string("AUTH_SESSION_MODEL", "net_xeaf_rack.SessionModel")
app_label, model_name = model_path.split(".")
model = apps.get_model(app_label, model_name)
# Нет класса
if not result:
raise ImproperlyConfigured("AUTH_SESSION_MODEL must be set in settings.py")
# Нет класса
if not model:
raise ImproperlyConfigured("AUTH_SESSION_MODEL must be set in settings.py")
# Некорректный класс
if not issubclass(result, SessionModel):
raise ImproperlyConfigured(f"AUTH_SESSION_MODEL must be a subclass of {SessionModel.__name__}")
# Некорректный класс
if not issubclass(model, SessionModel):
raise ImproperlyConfigured(f"AUTH_SESSION_MODEL must be a subclass of {SessionModel.__name__}")
return result
cls.__model_class = model
# noinspection PyTypeChecker
return cls.__model_class
@classmethod
def _raise_auth_error(cls, reason: AuthRejectReason) -> NoReturn:
"""
Выбрасывает исключение с ошибкой аутентификации
:param reason: Причина ошибки
"""
detail = AuthRejectReason.get_message(reason)
raise UnauthorizedException(detail=detail, reason=reason)