feat: add AUTH_DISABLED no-auth fallback mode
- config.py: AUTH_DISABLED: bool = False setting - middleware/auth.py: bypass all checks when AUTH_DISABLED=True - security.py: require_admin_auth returns synthetic context when disabled - endpoints/auth.py: /me returns synthetic admin; /sign-out → / when disabled - main.py: startup WARNING when disabled; pass auth_disabled to login.html - templates/login.html: info banner with Go to dashboard link when disabled - templates/setup.html: document AUTH_DISABLED option with security warning - tests/test_auth.py: 4 new AUTH_DISABLED tests (445 total, all pass) Agent-Logs-Url: https://github.com/christianlouis/dmarq/sessions/18f41bf2-0b68-4b7d-afb5-d2894c212a8f Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -237,3 +237,59 @@ class TestSignOutEndpoint:
|
||||
set_cookie = res.headers.get("set-cookie", "")
|
||||
assert SESSION_COOKIE in set_cookie
|
||||
assert "Max-Age=0" in set_cookie or "max-age=0" in set_cookie
|
||||
|
||||
|
||||
# ── AUTH_DISABLED mode ────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestAuthDisabled:
|
||||
"""Verify the AUTH_DISABLED=true no-auth fallback mode."""
|
||||
|
||||
def test_me_returns_synthetic_admin_when_auth_disabled(self, client: TestClient):
|
||||
"""With AUTH_DISABLED, /me must return the synthetic admin profile."""
|
||||
with patch("app.api.api_v1.endpoints.auth.settings") as mock_settings:
|
||||
mock_settings.AUTH_DISABLED = True
|
||||
res = client.get("/api/v1/auth/me")
|
||||
assert res.status_code == 200
|
||||
data = res.json()
|
||||
assert data["is_superuser"] is True
|
||||
assert data["auth_disabled"] is True
|
||||
assert data["email"] == "admin@localhost"
|
||||
|
||||
def test_sign_out_redirects_to_root_when_auth_disabled(self, client: TestClient):
|
||||
"""With AUTH_DISABLED, sign-out should redirect to / (no Logto session to clear)."""
|
||||
with patch("app.api.api_v1.endpoints.auth.settings") as mock_settings:
|
||||
mock_settings.AUTH_DISABLED = True
|
||||
res = client.get("/api/v1/auth/sign-out", follow_redirects=False)
|
||||
assert res.status_code == 302
|
||||
assert res.headers["location"] == "/"
|
||||
|
||||
def test_require_admin_auth_passes_when_disabled(self):
|
||||
"""require_admin_auth must return a synthetic context when AUTH_DISABLED=True."""
|
||||
import asyncio
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from app.core.security import require_admin_auth
|
||||
|
||||
with patch("app.core.security.settings") as mock_settings:
|
||||
mock_settings.AUTH_DISABLED = True
|
||||
mock_req = MagicMock()
|
||||
mock_req.cookies = {}
|
||||
result = asyncio.get_event_loop().run_until_complete(
|
||||
require_admin_auth(request=mock_req, api_key=None, bearer=None)
|
||||
)
|
||||
assert result["auth_type"] == "disabled"
|
||||
|
||||
def test_middleware_passes_all_requests_when_auth_disabled(self, client: TestClient):
|
||||
"""The auth middleware must let every request through when AUTH_DISABLED=True."""
|
||||
# The middleware does `from app.core.config import get_settings` inside dispatch,
|
||||
# so we patch the canonical location used at call time.
|
||||
with patch("app.core.config.get_settings") as mock_get_settings:
|
||||
mock_cfg = MagicMock()
|
||||
mock_cfg.AUTH_DISABLED = True
|
||||
mock_get_settings.return_value = mock_cfg
|
||||
# Even without a session cookie, the middleware lets the request through.
|
||||
# The endpoint itself then handles auth (API key or 401), but it must
|
||||
# never be a 302 redirect from the middleware.
|
||||
res = client.get("/settings", follow_redirects=False)
|
||||
assert res.status_code != 302
|
||||
|
||||
Reference in New Issue
Block a user