feat(auth): public self-service registration (ALLOW_REGISTRATION)
Docker Build & Publish / build (push) Successful in 1m8s
Docker Build & Publish / push (push) Failing after 34s
Docker Build & Publish / Prune old image versions (push) Has been skipped

Add POST /auth/register: creates a non-superuser then auto-logs in,
returning the same TokenResponse as login. Gated by the new
allow_registration setting (env ALLOW_REGISTRATION, default true);
when disabled it raises PermissionDeniedError (403). Accounts remain
admin-only for superusers.

Tests cover create+login, duplicate (409), short password (422), and
the disabled (403) path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Senko-san
2026-06-10 14:06:52 +03:00
parent c72d19599a
commit 14c1bc16e0
5 changed files with 83 additions and 2 deletions
+47
View File
@@ -152,6 +152,53 @@ async def test_admin_create_duplicate_conflicts(api: AsyncClient) -> None:
assert dup.status_code == 409
async def test_register_creates_user_and_logs_in(api: AsyncClient) -> None:
resp = await api.post(
"/api/v1/auth/register",
json={"username": "frank", "password": "frankpass1"},
)
assert resp.status_code == 201, resp.text
access = resp.json()["access_token"]
assert resp.json()["refresh_token"]
# The returned access token is immediately usable; account is a regular user.
me = await api.get("/api/v1/auth/me", headers={"Authorization": f"Bearer {access}"})
assert me.status_code == 200
assert me.json()["username"] == "frank"
assert me.json()["is_superuser"] is False
async def test_register_duplicate_conflicts(api: AsyncClient) -> None:
payload = {"username": "grace", "password": "gracepass1"}
first = await api.post("/api/v1/auth/register", json=payload)
assert first.status_code == 201
dup = await api.post("/api/v1/auth/register", json=payload)
assert dup.status_code == 409
async def test_register_short_password_rejected(api: AsyncClient) -> None:
resp = await api.post(
"/api/v1/auth/register",
json={"username": "heidi", "password": "short"},
)
assert resp.status_code == 422
async def test_register_disabled_forbidden(api: AsyncClient) -> None:
from app.core.config import get_settings
settings = get_settings()
settings.allow_registration = False
try:
resp = await api.post(
"/api/v1/auth/register",
json={"username": "ivan", "password": "ivanpass12"},
)
assert resp.status_code == 403
finally:
settings.allow_registration = True
async def test_deactivated_user_cannot_login(api: AsyncClient) -> None:
admin_access, _ = await _login(api, "admin", "adminpass1")
headers = {"Authorization": f"Bearer {admin_access}"}