"""Einstellungen aus Umgebungsvariablen (Präfix DBMCP_).""" import os from collections.abc import Mapping from dataclasses import dataclass def _list(value: str) -> tuple[str, ...]: return tuple(v.strip().lower() for v in value.split(",") if v.strip()) def _bool(value: str) -> bool: return value.strip().lower() in ("1", "true", "yes", "on") @dataclass(frozen=True) class Settings: # Schlüssel der Clients: Geheimnis -> Name (für das Audit-Log); DBMCP_API_KEYS="bifroest:geheim,…" api_keys: Mapping[str, str] # Datenbank-Hosts, zu denen verbunden werden darf: exakt oder als Endung (".db.intern") allowed_hosts: tuple[str, ...] = () allow_local: bool = False connect_timeout: float = 5 query_timeout: float = 15 max_rows: int = 500 max_tables: int = 300 sample_rows: int = 5 max_cell_chars: int = 200 max_sql_chars: int = 10000 # Host-Header, die der MCP-Transport annimmt (Schutz vor DNS-Rebinding); leer = keine Prüfung http_hosts: tuple[str, ...] = () # Nur für die lokale Entwicklung: ohne Schlüssel starten insecure_no_auth: bool = False bind: str = "0.0.0.0" port: int = 8080 @classmethod def from_env(cls, env: Mapping[str, str] = os.environ) -> "Settings": keys = {} for entry in env.get("DBMCP_API_KEYS", "").split(","): name, sep, secret = entry.strip().partition(":") if entry.strip(): if not sep or not name or len(secret) < 16: raise ValueError("DBMCP_API_KEYS: Einträge als name:geheimnis, Geheimnis mindestens 16 Zeichen") keys[secret] = name settings = cls( api_keys=keys, allowed_hosts=_list(env.get("DBMCP_ALLOWED_HOSTS", "")), allow_local=_bool(env.get("DBMCP_ALLOW_LOCAL", "")), connect_timeout=float(env.get("DBMCP_CONNECT_TIMEOUT_SECONDS", 5)), query_timeout=float(env.get("DBMCP_QUERY_TIMEOUT_SECONDS", 15)), max_rows=int(env.get("DBMCP_MAX_ROWS", 500)), http_hosts=_list(env.get("DBMCP_HTTP_HOSTS", "")), insecure_no_auth=_bool(env.get("DBMCP_INSECURE_NO_AUTH", "")), bind=env.get("DBMCP_BIND", "0.0.0.0"), port=int(env.get("DBMCP_PORT", 8080)), ) if not settings.api_keys and not settings.insecure_no_auth: raise ValueError("DBMCP_API_KEYS fehlt (nur für die Entwicklung: DBMCP_INSECURE_NO_AUTH=true)") return settings