diff --git a/net/xeaf/rack/managers/account_manager.py b/net/xeaf/rack/managers/account_manager.py new file mode 100644 index 0000000..1284627 --- /dev/null +++ b/net/xeaf/rack/managers/account_manager.py @@ -0,0 +1,17 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса AccountManager +""" + + +class AccountManager: + """ + Реализует методы работы с Аккаунтами + """ + + pass diff --git a/net/xeaf/rack/managers/session_manager.py b/net/xeaf/rack/managers/session_manager.py new file mode 100644 index 0000000..135cf56 --- /dev/null +++ b/net/xeaf/rack/managers/session_manager.py @@ -0,0 +1,133 @@ +# DRF Rack +# Библиотека классов расширений для Django REST Framework +# +# Автор: Николай В. Анохин +# Все права защищены. Лицензия: MIT + +""" +Описание класса SessionManager +""" + +from typing import Type + +from django.apps import apps +from environ import ImproperlyConfigured + +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 + + +class SessionManager[T:SessionModel]: + """ + Реализует методы работы с Сессиями + """ + + DEFAULT_SESSION_TTL = 3600 + """ Время жизни сессии по умолчанию """ + + @classmethod + def authenticate(cls, username: str, password: str) -> SessionModel: + """ + Авторизует новую сессию + + :param username: Имя пользователя + :param password: Пароль + + :return: Объект данных сессии + """ + + pass + + @classmethod + def find_by_token(cls, token: str) -> SessionModel | None: + """ + Возвращает объект данных сессии по токену + + :param token: Токен + + :return: Объект данных сессии или None, если не найдена или просрочена + """ + + session = SessionModel.objects.filter(token__exact=token).first() + if session is not None and not session.is_expired(): + return session + + return None + + @classmethod + def open_session(cls, account: AccountModel, actual_account: AccountModel, device_id: str | None = None) -> T: + """ + Открывает сессию для аккаунта + + :param account: Модель данных Аккаунта пользователя + :param actual_account: Актуальный Аккаунт пользователя + :param device_id: Идентификатор устройства + + :return: Информация о сессии + """ + + session = cls._create_session_model(account, actual_account) + session.device_id = device_id + cls.renew_session(session) + return session + + @classmethod + def renew_session(cls, session: SessionModel) -> None: + """ + Обновляет время существование сессии + + :param session: Модель данных сессии + """ + + 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: + """ + Создает новую модель Сессии + + :param account: Аккаунт пользователя + :param actual_account: Актуальный Аккаунт пользователя + """ + + model = cls._get_session_model_class() + return model.objects.create( + token=CryptoManager.gen_token(), + account=account, + actual_account=actual_account + ) + + @classmethod + def _get_session_model_class(cls) -> Type[T]: + """ + Возвращает класс модели данных сессии + + :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 not result: + 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__}") + + return result