Files
vidconf/backend/schemas/auth.py
Max Ronzhin 4f82ebe17a
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
feat(auth): согласие на обработку персональных данных при регистрации
Отключаемый модуль (instance_settings.consent_policy): галочка + ссылка на
публичную страницу регламента на форме регистрации, редактируемый в админке
текст с типовым шаблоном по умолчанию (плейсхолдеры под организацию, не
проходил юридическую проверку), версия текста растёт при каждой правке.
Факт согласия хранится в users (consent_version, consent_given_at) — второй
эшелон проверки на сервере, как и для отключаемых модулей ранее. Дефолт
(выключено) сохраняет поведение существующих инсталляций, у уже
зарегистрированных пользователей согласие не запрашивается.
2026-08-04 22:20:00 +03:00

127 lines
5.7 KiB
Python
Raw 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.
"""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