feat(auth): несколько эталонных mail-доменов для верификации регистрации
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled

Настройка «Эталон mail-домена» теперь хранит список доменов вместо
одного — email при регистрации принимается, если совпадает с любым из
них. Старое значение в БД ({"domain": str|None}) читается прозрачно
(обратная совместимость без Alembic-миграции) и переписывается в новую
форму ({"domains": [...]}) при первом же сохранении настроек. В админке
добавление/удаление доменов — списком чипов; на экране регистрации
подсказка о несовпадении домена перечисляет все эталонные варианты.
This commit is contained in:
2026-07-27 20:40:23 +03:00
parent e55a6aebe7
commit 25ffd9e678
13 changed files with 273 additions and 89 deletions

View File

@@ -238,7 +238,7 @@ async def test_registration_options_disabled_by_default(
body = response.json()
assert body["team_choice_enabled"] is False
assert body["teams"] == []
assert body["email_domain"] is None
assert body["email_domains"] == []
async def test_registration_options_enabled_returns_teams_sorted_by_name(
@@ -343,29 +343,30 @@ async def test_register_with_unknown_team_id_returns_400(
# --- Верификация регистрирующихся по домену email -------------------------------------
async def test_registration_options_returns_email_domain_when_enabled(
async def test_registration_options_returns_email_domains_when_enabled(
client: httpx.AsyncClient, db_session: AsyncSession
) -> None:
"""При включённой верификации домена `registration-options` отдаёт эталонный домен."""
"""При включённой верификации домена `registration-options` отдаёт все эталонные домены."""
await InstanceSettingsService(db_session).update(
SettingsUpdateIn(
registration_email_domain_enabled=True, registration_email_domain="example.com"
registration_email_domain_enabled=True,
registration_email_domains=["example.com", "corp.example"],
)
)
await db_session.commit()
response = await client.get("/api/v1/auth/registration-options")
assert response.status_code == 200, response.text
assert response.json()["email_domain"] == "example.com"
assert response.json()["email_domains"] == ["example.com", "corp.example"]
async def test_registration_options_email_domain_null_when_disabled(
async def test_registration_options_email_domains_empty_when_disabled(
client: httpx.AsyncClient, db_session: AsyncSession
) -> None:
"""Выключенная верификация домена — `email_domain` всегда `null`, даже если домен сохранён."""
"""Выключенная верификация домена — `email_domains` всегда пуст, даже если домены сохранены."""
await InstanceSettingsService(db_session).update(
SettingsUpdateIn(
registration_email_domain_enabled=True, registration_email_domain="example.com"
registration_email_domain_enabled=True, registration_email_domains=["example.com"]
)
)
await InstanceSettingsService(db_session).update(
@@ -375,7 +376,7 @@ async def test_registration_options_email_domain_null_when_disabled(
response = await client.get("/api/v1/auth/registration-options")
assert response.status_code == 200, response.text
assert response.json()["email_domain"] is None
assert response.json()["email_domains"] == []
async def test_register_with_foreign_domain_when_verification_enabled_returns_400(
@@ -383,7 +384,8 @@ async def test_register_with_foreign_domain_when_verification_enabled_returns_40
) -> None:
await InstanceSettingsService(db_session).update(
SettingsUpdateIn(
registration_email_domain_enabled=True, registration_email_domain="example.com"
registration_email_domain_enabled=True,
registration_email_domains=["example.com", "corp.example"],
)
)
await db_session.commit()
@@ -405,7 +407,7 @@ async def test_register_with_matching_domain_case_insensitive_succeeds(
) -> None:
await InstanceSettingsService(db_session).update(
SettingsUpdateIn(
registration_email_domain_enabled=True, registration_email_domain="example.com"
registration_email_domain_enabled=True, registration_email_domains=["example.com"]
)
)
await db_session.commit()
@@ -421,6 +423,29 @@ async def test_register_with_matching_domain_case_insensitive_succeeds(
assert response.status_code == 201, response.text
async def test_register_with_second_domain_in_list_succeeds(
client: httpx.AsyncClient, db_session: AsyncSession, email_backend: _CapturingEmailBackend
) -> None:
"""Email подходит, если совпадает с ЛЮБЫМ доменом из списка — не только с первым."""
await InstanceSettingsService(db_session).update(
SettingsUpdateIn(
registration_email_domain_enabled=True,
registration_email_domains=["example.com", "corp.example"],
)
)
await db_session.commit()
response = await client.post(
"/api/v1/auth/register",
json={
"email": "dave@corp.example",
"name_user": "Dave",
"password": "supersecret1",
},
)
assert response.status_code == 201, response.text
async def test_register_any_domain_allowed_when_verification_disabled(
client: httpx.AsyncClient, email_backend: _CapturingEmailBackend
) -> None: