Files

87 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Модуль DB-моделей пользователей"""
from datetime import datetime, timezone
from typing import TYPE_CHECKING, List
from sqlmodel import Field, Relationship
from library_service.models.dto.user import UserBase
from library_service.models.db.links import UserRoleLink
if TYPE_CHECKING:
from .role import Role
class User(UserBase, table=True):
"""Модель пользователя в базе данных"""
__tablename__ = "users"
id: int | None = Field(
default=None, primary_key=True, index=True, description="Идентификатор"
)
hashed_password: str = Field(nullable=False, description="Argon2id хэш пароля")
is_2fa_enabled: bool = Field(default=False, description="Включен TOTP 2FA")
totp_secret: str | None = Field(
default=None, max_length=80, description="Зашифрованный секрет TOTP"
)
recovery_code_hashes: str | None = Field(
default=None,
max_length=1500,
description="Argon2id хэши одноразовыхкодов восстановления",
)
recovery_codes_generated_at: datetime | None = Field(
default=None, description="Дата и время создания кодов восстановления"
)
is_active: bool = Field(default=True, description="Не является ли заблокированым")
is_verified: bool = Field(default=False, description="Является ли верифицированным")
created_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc),
description="Дата и время создания",
)
updated_at: datetime | None = Field(
default=None,
sa_column_kwargs={"onupdate": lambda: datetime.now(timezone.utc)},
description="Дата и время последнего обновления",
)
roles: List["Role"] = Relationship(back_populates="users", link_model=UserRoleLink)
loans: List["BookUserLink"] = Relationship( # ty: ignore[unresolved-reference]
sa_relationship_kwargs={"cascade": "all, delete"}
)
@property
def recovery_codes_list(self) -> list[str]:
"""Список хешей"""
if not self.recovery_code_hashes:
return []
return self.recovery_code_hashes.split(" ")
@property
def recovery_codes_total(self) -> int:
"""Общее количество слотов"""
if not self.recovery_code_hashes:
return 0
return len(self.recovery_codes_list)
@property
def recovery_codes_remaining(self) -> int:
"""Количество неиспользованных кодов"""
return sum(1 for h in self.recovery_codes_list if h)
@property
def recovery_codes_used(self) -> int:
"""Количество использованных кодов"""
return self.recovery_codes_total - self.recovery_codes_remaining
def get_recovery_code_positions(self) -> dict[str, list[int]]:
"""Возвращает позиции использованных и оставшихся кодов"""
used = []
remaining = []
for i, h in enumerate(self.recovery_codes_list, start=1):
if h:
remaining.append(i)
else:
used.append(i)
return {"used": used, "remaining": remaining}