45 lines
1.7 KiB
Python
45 lines
1.7 KiB
Python
"""Basic auth + fail-closed behaviour (audit C2)."""
|
|
|
|
import base64
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
import app.main as main
|
|
|
|
|
|
def _basic(user: str, password: str) -> dict:
|
|
raw = base64.b64encode(f"{user}:{password}".encode()).decode()
|
|
return {"Authorization": f"Basic {raw}"}
|
|
|
|
|
|
def test_no_credentials_means_open_but_health_works(client):
|
|
assert client.get("/").status_code == 200
|
|
|
|
|
|
def test_auth_required_when_configured(monkeypatch, fake_gitea):
|
|
monkeypatch.setenv("KANBANTE_AUTH_USER", "bruno")
|
|
monkeypatch.setenv("KANBANTE_AUTH_PASSWORD", "motdepasse")
|
|
with TestClient(main.app) as client:
|
|
# Sans en-tête navigateur : 401 (comportement historique).
|
|
assert client.get("/").status_code == 401
|
|
# Le Basic reste accepté pour les scripts (0.22.0).
|
|
assert client.get("/", headers=_basic("bruno", "mauvais")).status_code == 401
|
|
assert client.get("/", headers=_basic("bruno", "motdepasse")).status_code == 200
|
|
# Un navigateur, lui, est envoyé vers l'écran de connexion.
|
|
r = client.get(
|
|
"/", headers={"Accept": "text/html"}, follow_redirects=False
|
|
)
|
|
assert r.status_code == 303
|
|
assert r.headers["location"].startswith("/login")
|
|
# Liveness stays public for Docker HEALTHCHECK.
|
|
assert client.get("/api/health").status_code == 200
|
|
|
|
|
|
def test_require_auth_fails_closed_without_credentials(monkeypatch, fake_gitea):
|
|
monkeypatch.delenv("KANBANTE_AUTH_USER", raising=False)
|
|
monkeypatch.delenv("KANBANTE_AUTH_PASSWORD", raising=False)
|
|
monkeypatch.setenv("KANBANTE_REQUIRE_AUTH", "1")
|
|
with TestClient(main.app) as client:
|
|
assert client.get("/").status_code == 503
|
|
assert client.get("/api/health").status_code == 200
|