Exclude static asset extensions from auth redirect middleware
Agent-Logs-Url: https://github.com/christianlouis/dmarq/sessions/3e1fe3d2-54b1-4eb4-90d2-173a5e95d376 Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -37,6 +37,24 @@ _PUBLIC_PREFIXES: tuple[str, ...] = (
|
|||||||
"/openapi",
|
"/openapi",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# File extensions for static assets that are always publicly accessible
|
||||||
|
_STATIC_EXTENSIONS: tuple[str, ...] = (
|
||||||
|
".ico",
|
||||||
|
".png",
|
||||||
|
".jpg",
|
||||||
|
".jpeg",
|
||||||
|
".gif",
|
||||||
|
".svg",
|
||||||
|
".webp",
|
||||||
|
".css",
|
||||||
|
".js",
|
||||||
|
".woff",
|
||||||
|
".woff2",
|
||||||
|
".ttf",
|
||||||
|
".eot",
|
||||||
|
".map",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthRedirectMiddleware(BaseHTTPMiddleware):
|
class AuthRedirectMiddleware(BaseHTTPMiddleware):
|
||||||
"""
|
"""
|
||||||
@@ -68,6 +86,8 @@ class AuthRedirectMiddleware(BaseHTTPMiddleware):
|
|||||||
return await call_next(request)
|
return await call_next(request)
|
||||||
if any(path.startswith(p) for p in _PUBLIC_PREFIXES):
|
if any(path.startswith(p) for p in _PUBLIC_PREFIXES):
|
||||||
return await call_next(request)
|
return await call_next(request)
|
||||||
|
if any(path.endswith(ext) for ext in _STATIC_EXTENSIONS):
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
# ── 2. Valid session cookie ───────────────────────────────────────────
|
# ── 2. Valid session cookie ───────────────────────────────────────────
|
||||||
token = request.cookies.get(SESSION_COOKIE)
|
token = request.cookies.get(SESSION_COOKIE)
|
||||||
|
|||||||
@@ -293,3 +293,39 @@ class TestAuthDisabled:
|
|||||||
# never be a 302 redirect from the middleware.
|
# never be a 302 redirect from the middleware.
|
||||||
res = client.get("/settings", follow_redirects=False)
|
res = client.get("/settings", follow_redirects=False)
|
||||||
assert res.status_code != 302
|
assert res.status_code != 302
|
||||||
|
|
||||||
|
|
||||||
|
# ── Static asset bypass ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
class TestStaticAssetBypass:
|
||||||
|
"""Static assets must never be redirected to the login page."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _logto_configured_mock():
|
||||||
|
mock_cfg = MagicMock()
|
||||||
|
mock_cfg.AUTH_DISABLED = False
|
||||||
|
mock_cfg.logto_configured = True
|
||||||
|
return mock_cfg
|
||||||
|
|
||||||
|
def test_favicon_not_redirected_to_login(self, client: TestClient):
|
||||||
|
"""GET /favicon.ico without a session must pass through (not redirect to /login)."""
|
||||||
|
with patch("app.core.config.get_settings") as mock_get_settings:
|
||||||
|
mock_get_settings.return_value = self._logto_configured_mock()
|
||||||
|
res = client.get("/favicon.ico", follow_redirects=False)
|
||||||
|
assert res.status_code != 302
|
||||||
|
|
||||||
|
def test_png_asset_not_redirected_to_login(self, client: TestClient):
|
||||||
|
"""GET /logo.png without a session must pass through."""
|
||||||
|
with patch("app.core.config.get_settings") as mock_get_settings:
|
||||||
|
mock_get_settings.return_value = self._logto_configured_mock()
|
||||||
|
res = client.get("/logo.png", follow_redirects=False)
|
||||||
|
assert res.status_code != 302
|
||||||
|
|
||||||
|
def test_protected_page_still_redirected(self, client: TestClient):
|
||||||
|
"""GET /dashboard without a session must still redirect to /login."""
|
||||||
|
with patch("app.core.config.get_settings") as mock_get_settings:
|
||||||
|
mock_get_settings.return_value = self._logto_configured_mock()
|
||||||
|
res = client.get("/dashboard", follow_redirects=False)
|
||||||
|
assert res.status_code == 302
|
||||||
|
assert res.headers["location"].startswith("/login")
|
||||||
|
|||||||
Reference in New Issue
Block a user