Отключаемый модуль (instance_settings.consent_policy): галочка + ссылка на публичную страницу регламента на форме регистрации, редактируемый в админке текст с типовым шаблоном по умолчанию (плейсхолдеры под организацию, не проходил юридическую проверку), версия текста растёт при каждой правке. Факт согласия хранится в users (consent_version, consent_given_at) — второй эшелон проверки на сервере, как и для отключаемых модулей ранее. Дефолт (выключено) сохраняет поведение существующих инсталляций, у уже зарегистрированных пользователей согласие не запрашивается.
127 lines
5.7 KiB
Python
127 lines
5.7 KiB
Python
"""Pydantic-схемы для аутентификации и профиля пользователя."""
|
||
|
||
import uuid
|
||
|
||
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
||
|
||
|
||
class RegisterIn(BaseModel):
|
||
"""Тело запроса регистрации нового пользователя.
|
||
|
||
`team_id` допустим только при включённой настройке инстанса
|
||
`registration_team_choice` (см. `GET /auth/registration-options`) и
|
||
существующей команде — иначе `POST /auth/register` вернёт 400.
|
||
`consent_accepted` обязан быть `True`, если в настройках инстанса
|
||
включено `consent_required` (согласие на обработку персональных
|
||
данных) — иначе `POST /auth/register` вернёт 400. Игнорируется, если
|
||
настройка выключена (второй эшелон проверки — фронт тоже блокирует
|
||
кнопку, но сервер не полагается на это).
|
||
"""
|
||
|
||
email: EmailStr
|
||
name_user: str = Field(min_length=1, max_length=255)
|
||
password: str = Field(min_length=8)
|
||
team_id: uuid.UUID | None = None
|
||
consent_accepted: bool = False
|
||
|
||
|
||
class VerifyEmailIn(BaseModel):
|
||
"""Тело запроса подтверждения email."""
|
||
|
||
token: str = Field(min_length=1)
|
||
|
||
|
||
class TokenOut(BaseModel):
|
||
"""Ответ с access-токеном; refresh-токен передаётся отдельно в httpOnly cookie."""
|
||
|
||
access_token: str
|
||
token_type: str = "bearer"
|
||
|
||
|
||
class UserOut(BaseModel):
|
||
"""Публичное представление пользователя (профиль текущего пользователя).
|
||
|
||
`email: str`, а не `EmailStr` — намеренно: это response-схема,
|
||
отражающая уже сохранённые в БД данные, а не принимающая новый ввод.
|
||
`EmailStr` дополнительно отсекает синтаксически валидные, но
|
||
зарезервированные домены (`.local`, `.test` и т.п. из RFC 6761) — валидный
|
||
email на входе (`RegisterIn`, ниже) мог быть заведён напрямую в БД (seed,
|
||
ручная миграция) с таким доменом; строгая `EmailStr` на выходе привела бы
|
||
к 500 `ResponseValidationError` для уже существующих пользователей.
|
||
"""
|
||
|
||
model_config = ConfigDict(from_attributes=True)
|
||
|
||
id: uuid.UUID
|
||
email: str
|
||
name_user: str
|
||
role: str
|
||
|
||
|
||
class UserListItemOut(BaseModel):
|
||
"""Элемент списка пользователей (пикер участников конференции/мультиселект брони)."""
|
||
|
||
id: uuid.UUID
|
||
display_name: str
|
||
avatar_url: str | None = None
|
||
|
||
|
||
class ProfileUpdateIn(BaseModel):
|
||
"""Тело правки профиля текущего пользователя.
|
||
|
||
Email НЕ принимается — read-only поле профиля. `team_id` не различает
|
||
«поле не передано» и «явный `null`» через сравнение с `None` — роутер
|
||
читает `model_fields_set` (тот же приём, что `AdminUserUpdateIn.team_id`).
|
||
"""
|
||
|
||
name_user: str | None = Field(default=None, min_length=1, max_length=255)
|
||
team_id: uuid.UUID | None = None
|
||
|
||
|
||
class UserProfileOut(UserOut):
|
||
"""Профиль пользователя (свой либо открытый администратором) — `UserOut` + аватар/команда."""
|
||
|
||
avatar_url: str | None = None
|
||
team_id: uuid.UUID | None = None
|
||
team_name: str | None = None
|
||
|
||
|
||
class PasswordChangeIn(BaseModel):
|
||
"""Тело смены пароля текущим пользователем (`POST /users/me/password`).
|
||
|
||
Политика сложности `new_password` — та же, что при регистрации
|
||
(`RegisterIn.password`, min 8 символов).
|
||
"""
|
||
|
||
current_password: str
|
||
new_password: str = Field(min_length=8)
|
||
|
||
|
||
class RegistrationTeamOptionOut(BaseModel):
|
||
"""Команда в списке опций публичной карточки регистрации."""
|
||
|
||
id: uuid.UUID
|
||
name: str
|
||
|
||
|
||
class RegistrationOptionsOut(BaseModel):
|
||
"""Публичные опции формы регистрации (`GET /auth/registration-options`).
|
||
|
||
`teams` отдаётся только при `team_choice_enabled=True` — иначе пустой
|
||
список (справочник команд не раскрывается, пока выбор выключен).
|
||
`email_domains` — эталонные домены при включённой верификации регистрации
|
||
по домену email (настройка инстанса `registration_email_domain`), иначе
|
||
пустой список.
|
||
"""
|
||
|
||
team_choice_enabled: bool
|
||
teams: list[RegistrationTeamOptionOut]
|
||
email_domains: list[str] = Field(default_factory=list)
|
||
# Согласие на обработку персональных данных: `consent_required` — обязательна
|
||
# ли галочка на форме регистрации; `consent_text`/`consent_version` отдаются
|
||
# ВСЕГДА, независимо от `consent_required` — той же строкой пользуется
|
||
# публичная страница регламента, доступная и при выключенном модуле.
|
||
consent_required: bool = False
|
||
consent_text: str = ""
|
||
consent_version: int = 1
|