From 0247f13fb6b9e8bd8d6e57602c297eed85452afb Mon Sep 17 00:00:00 2001 From: Light1YT Date: Wed, 3 Jun 2026 18:29:20 +0500 Subject: [PATCH] test(rbac): test-mode bypass so api/v1 suite can authenticate (CI-rehab 1/3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whole backend api/v1 test suite hits `app` via TestClient mimo Caddy, so rbac_guard (app/main.py) 401'd every authed request (no X-Authenticated-User) — 112 tests red, the suite effectively dead (which is how #994's district 500 shipped uncaught). Add `settings.testing` (default False — prod RBAC untouched) + a strictly-gated bypass at the top of rbac_guard; tests/conftest.py enables it (env TESTING=1 + settings.testing=True before app import). Security: bypass fires ONLY when settings.testing is True, which is set NOWHERE in prod (default False; only tests/conftest.py flips it). RBAC's 401/403 logic stays covered by tests/test_rbac.py (its own middleware copy, ignores the flag). Effect: 112 → 42 failed, 1590 → 1660 passed. Remaining 42 (+5 mv_layout env errors) are stale-mock/assertion drift + env-required tests — fixed in CI-rehab 2/3. Foundation for a real Forgejo pytest gate (3/3). Refs #944. --- backend/app/core/config.py | 4 ++++ backend/app/main.py | 6 ++++++ backend/tests/conftest.py | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 4fea5f2c..76e26058 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -17,6 +17,10 @@ class Settings(BaseSettings): glitchtip_dsn: str | None = None glitchtip_traces_sample_rate: float = 0.05 environment: str = "dev" + # Test-mode flag (env TESTING=1). СТРОГО default False — в проде RBAC-гейт + # (app/main.py rbac_guard) активен. True только в pytest (tests/conftest.py), + # где запросы идут по app мимо Caddy и не несут X-Authenticated-User. + testing: bool = False @model_validator(mode="after") def _promote_legacy_sentry_dsn(self) -> "Settings": diff --git a/backend/app/main.py b/backend/app/main.py index 1648033b..234afe43 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -97,6 +97,12 @@ async def rbac_guard( request: Request, call_next: Callable[[Request], Awaitable[Response]], ) -> Response: + # Test-mode bypass: pytest бьёт по app мимо Caddy → нет X-Authenticated-User. + # СТРОГО gated на settings.testing (default False) — прод RBAC не затронут. + # RBAC-логика покрыта отдельно в tests/test_rbac.py (своя копия middleware). + if settings.testing: + return await call_next(request) + path = request.url.path if path in _PUBLIC_PATHS: return await call_next(request) diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 7b19365f..4a5fb6f2 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -9,8 +9,21 @@ NB: RBAC-гейт (app/main.py `rbac_guard`) требует заголовок ` from __future__ import annotations +import os + import pytest +# Включаем test-mode ДО любого импорта app: rbac_guard (app/main.py) пропускает +# запросы при settings.testing=True (юнит-тесты бьют по app мимо Caddy и не несут +# X-Authenticated-User → иначе 401 на всём api/v1). Ставим и env (на случай +# ре-инстанцирования Settings), и атрибут синглтона напрямую (надёжно независимо +# от порядка импортов). RBAC-логика (401/403) покрыта в test_rbac.py — там своя +# копия middleware, на settings.testing НЕ смотрит, поэтому остаётся валидной. +os.environ.setdefault("TESTING", "1") +from app.core.config import settings + +settings.testing = True + @pytest.fixture(autouse=True) def _clear_dependency_overrides():