52 lines
1.5 KiB
Python
52 lines
1.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Optionaler HTTP-Basic-Schutz für die gesamte Suite.
|
||
|
||
Ist APP_PASSWORD gesetzt, gilt der Schutz für Dashboard und alle Plugins.
|
||
Die Zugangsdaten sind identisch zu denen, die der AI-Webimport bisher schon
|
||
genutzt hat – bestehende .env-Dateien funktionieren unverändert weiter.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import base64
|
||
import hmac
|
||
import os
|
||
|
||
from fastapi import Request
|
||
from fastapi.responses import JSONResponse, Response
|
||
|
||
OPEN_PATHS = ("/api/health",)
|
||
|
||
|
||
def _unauthorized() -> Response:
|
||
return JSONResponse(
|
||
status_code=401,
|
||
content={"detail": "Anmeldung erforderlich."},
|
||
headers={"WWW-Authenticate": 'Basic realm="boehmitools"'},
|
||
)
|
||
|
||
|
||
async def basic_auth_middleware(request: Request, call_next):
|
||
expected_password = os.environ.get("APP_PASSWORD", "")
|
||
if not expected_password or request.url.path in OPEN_PATHS:
|
||
return await call_next(request)
|
||
|
||
expected_username = os.environ.get("APP_USERNAME", "admin")
|
||
header = request.headers.get("Authorization", "")
|
||
if not header.startswith("Basic "):
|
||
return _unauthorized()
|
||
|
||
try:
|
||
decoded = base64.b64decode(header[6:]).decode("utf-8")
|
||
username, password = decoded.split(":", 1)
|
||
except Exception:
|
||
return _unauthorized()
|
||
|
||
if not (
|
||
hmac.compare_digest(username, expected_username)
|
||
and hmac.compare_digest(password, expected_password)
|
||
):
|
||
return _unauthorized()
|
||
|
||
return await call_next(request)
|