feat(auth): écran de connexion + session signée (le Basic reste pour les scripts)

- /login professionnel, cookie HMAC 7 jours, next local seulement,
  CSRF double-submit réutilisé, /logout réel + lien Sortir.
- Middleware : session OU Basic ; navigateurs redirigés vers
  /login, API en 401 JSON ; /static et /login publics.
This commit is contained in:
Spark
2026-10-14 08:02:49 -04:00
parent 0bdde28b79
commit 319619d200
5 changed files with 290 additions and 36 deletions
+13 -3
View File
@@ -8,9 +8,15 @@ Environment variables:
GITEA_URL Base URL of the Gitea instance.
GITEA_TOKEN Gitea API token (read/write issues).
KANBANTE_DB_PATH Path of the local SQLite database.
KANBANTE_AUTH_USER Basic-auth username (with _PASSWORD: enables auth).
KANBANTE_AUTH_PASSWORD Basic-auth password.
KANBANTE_AUTH_USER Login username (with _PASSWORD: enables auth).
KANBANTE_AUTH_PASSWORD Login password.
KANBANTE_REQUIRE_AUTH "1" = refuse to serve if auth is not configured.
KANBANTE_SESSION_SECRET HMAC key for the login session cookie
(default: derived from the credentials —
rotating the password invalidates sessions).
KANBANTE_SNAPSHOT_INTERVAL Seconds between background snapshot
refreshes (chantier B, default 120;
0 disables the refresher).
KANBANTE_ACTIVE_WINDOW_DAYS A non-archived repo updated within this many
days counts as "active" (default 365).
KANBANTE_CACHE_TTL Seconds to cache Gitea list responses (default 30).
@@ -89,7 +95,7 @@ from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
APP_VERSION = "0.21.0"
APP_VERSION = "0.22.0"
def _env_bool(name: str, default: bool) -> bool:
@@ -158,6 +164,8 @@ class Settings:
briefing_include_standup: bool = False
rules_raw: str = ""
zombie_days: int = 30
session_secret: str = ""
snapshot_interval: int = 120
@property
def auth_enabled(self) -> bool:
@@ -203,4 +211,6 @@ def get_settings() -> Settings:
briefing_include_standup=_env_bool("KANBANTE_BRIEFING_INCLUDE_STANDUP", False),
rules_raw=os.getenv("KANBANTE_RULES", ""),
zombie_days=_env_int("KANBANTE_ZOMBIE_DAYS", 30),
session_secret=os.getenv("KANBANTE_SESSION_SECRET", ""),
snapshot_interval=_env_int("KANBANTE_SNAPSHOT_INTERVAL", 120),
)