From 64e25bb9ae46f5572bb55dafac3991ce1b793be3 Mon Sep 17 00:00:00 2001 From: Tim Eertmoed Date: Wed, 2 Sep 2026 18:37:11 +0200 Subject: [PATCH] Nur noch, was zum Installieren und Betreiben von TESM noetig ist Entfernt: Tests, Architekturdokumentation und die Serverhaelfte des Lizenzprotokolls. Beides liegt im Entwicklungsrepository alientim/TESM-DEV. Das Lizenzprotokoll ist geteilt. TESM braucht nur die gemeinsame Haelfte (tesm-licensing): Lizenzen verifizieren, Status bewerten, Anfragen stellen, Antworten pruefen. Ausstellen, erneuern, Antworten signieren und Schluesselerzeugung liegen jetzt in tesm-licensing-server und damit ausschliesslich beim Lizenzserver -- ein Client soll den Code zum Ausstellen nicht einmal mitbringen. Nachgeprueft: kein Modul von TESM oder tesm-core importiert eine der verschobenen Funktionen. install.sh installiert entsprechend je Anwendung nur die noetigen Pakete und bricht mit klarer Meldung ab, wenn die verlangte Anwendung nicht im Baum liegt. Co-Authored-By: Claude Opus 5 --- README.md | 108 +- deploy/install.sh | 15 +- docs/ARCHITEKTUR.md | 166 -- .../src/tesm_licensing/__init__.py | 12 - .../src/tesm_licensing/canonical.py | 13 - .../src/tesm_licensing/license.py | 102 +- .../src/tesm_licensing/protocol.py | 85 +- tesm-v2.0.0.tar.gz | Bin 0 -> 374007 bytes tests/conftest.py | 211 --- tests/e2e_smoke.py | 499 ------ tests/test_core_security.py | 721 --------- tests/test_core_smoke.py | 246 --- tests/test_legacy_transfer.py | 279 ---- tests/test_licensing.py | 408 ----- tests/test_repo_hygiene.py | 326 ---- tests/test_tesm_restarts_ssh.py | 1376 ----------------- tests/test_tesm_smoke.py | 968 ------------ tests/test_webserver.py | 521 ------- 18 files changed, 56 insertions(+), 6000 deletions(-) delete mode 100644 docs/ARCHITEKTUR.md create mode 100644 tesm-v2.0.0.tar.gz delete mode 100644 tests/conftest.py delete mode 100644 tests/e2e_smoke.py delete mode 100644 tests/test_core_security.py delete mode 100644 tests/test_core_smoke.py delete mode 100644 tests/test_legacy_transfer.py delete mode 100644 tests/test_licensing.py delete mode 100644 tests/test_repo_hygiene.py delete mode 100644 tests/test_tesm_restarts_ssh.py delete mode 100644 tests/test_tesm_smoke.py delete mode 100644 tests/test_webserver.py diff --git a/README.md b/README.md index eca36c0..4a6cc8a 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,26 @@ Portsteuerung, Neustart (PoE, SSH, RPC), DHCP ueber Kea, Dateifreigaben, Wartung ueber SSH, Protokolle, Papierkorb, verschluesselte Ein- und Ausfuhr und ein SSH-Terminal im Browser. -Der Lizenzserver liegt in einem eigenen Repository: -[alientim/tesm-license](https://gitea.int.eertmoed.net/alientim/tesm-license). -Beide teilen sich den Kern (`packages/tesm-core`) und das Lizenzprotokoll -(`packages/tesm-licensing`); diese Pakete sind hier mit enthalten, damit sich -das Repository allein installieren laesst. +**Dieses Repository enthaelt genau das, was zum Installieren und Betreiben von +TESM gebraucht wird** -- nichts darueber hinaus: + +``` +apps/tesm die Anwendung +packages/tesm-core gemeinsamer Kern (Anmeldung, Rechte, Sicherung, nginx, ...) +packages/tesm-licensing Lizenzprotokoll, Clientseite +deploy/ install.sh, update.sh, release.sh, systemd, privilegierter Helfer +docs/ Betrieb und Sicherheit +``` + +Die Serverhaelfte des Lizenzprotokolls (`tesm-licensing-server`: ausstellen, +erneuern, Antworten signieren) liegt **nicht** hier. TESM verifiziert Lizenzen +und stellt Anfragen; den Code zum Ausstellen soll ein Client nicht einmal +mitbringen. + +| Wo sonst noch | | +|---|---| +| [alientim/tesm-license](https://gitea.int.eertmoed.net/alientim/tesm-license) | der Lizenzserver | +| [alientim/TESM-DEV](https://gitea.int.eertmoed.net/alientim/TESM-DEV) | Entwicklung: beide Anwendungen, Tests, Architekturdoku | ## Zweige @@ -20,7 +35,7 @@ das Repository allein installieren laesst. --- -## Installation auf einem Server +## Installation ```bash sudo bash deploy/install.sh --app tesm --https @@ -64,41 +79,7 @@ Rollback. --- -## Release bauen - -```bash -git tag -a v2.0.0 -m "TESM 2.0.0" && git push origin v2.0.0 -bash deploy/release.sh v2.0.0 -``` - -Heraus kommt `tesm-v2.0.0.tar.gz` samt SHA-256. Der Name kommt aus dem -Repository, nicht aus einer festen Zeichenkette. - ---- - -## Entwicklung - -```bash -python -m venv .venv && . .venv/bin/activate -pip install -e packages/tesm-licensing -e packages/tesm-core -e apps/tesm -pip install pytest -pytest tests -q -``` - -Ohne gesetzte Umgebungsvariablen legt die Anwendung ihre Daten unter -`instance/tesm/` im Arbeitsverzeichnis an -- kein Zugriff auf `/srv` oder -`/var/log` noetig. - -```bash -TESM_DEBUG=1 flask --app tesm run -``` - -Im Debug-Modus wird `COOKIE_SECURE` abgeschaltet, sonst kaeme das -Sitzungscookie ueber `http://127.0.0.1` nie an. - ---- - -## Verwaltung von der Kommandozeile +## Betrieb von der Kommandozeile ```bash sudo -u tesm tesm-admin status # Zustand der Installation @@ -114,13 +95,13 @@ Ein Export des Vorgaengers laesst sich uebersetzen und dann ueber den normalen Import einlesen -- Clients, Switche, Zugangsdaten und die DHCP-Konfiguration: ```bash -tesm-admin convert-legacy tesm_export_20260813_195455.json --dry-run +sudo -u tesm tesm-admin convert-legacy tesm_export_20260813_195455.json --dry-run ``` `--dry-run` zeigt nur den Bericht. Ohne die Option entsteht eine Datei im neuen Format, die unter *Verwaltung -> Sicherung* eingelesen wird. Der Bericht -nennt ausdruecklich, was **nicht** uebersetzt werden kann (lokale Konten, Rechte, -Verzeichnisdienst, Webserver) -- das ist von Hand nachzuziehen. +nennt ausdruecklich, was **nicht** uebersetzt werden kann (lokale Konten, +Rechte, Verzeichnisdienst, Webserver) -- das ist von Hand nachzuziehen. Die Passphrase gehoert an die verdeckte Eingabeaufforderung. `--passphrase` existiert nur fuer Automatisierung: es schreibt sie in Shell-Historie und @@ -128,30 +109,27 @@ Prozessliste. --- +## Release bauen + +```bash +git tag -a v2.0.0 -m "TESM 2.0.0" && git push origin v2.0.0 +bash deploy/release.sh v2.0.0 +``` + +Heraus kommt `tesm-v2.0.0.tar.gz` samt SHA-256. Der Name kommt aus dem +Repository, nicht aus einer festen Zeichenkette. + +--- + ## Dokumentation | | | |---|---| -| [docs/ARCHITEKTUR.md](docs/ARCHITEKTUR.md) | Aufbau, Schnitte und die Gruende dafuer | -| [docs/SICHERHEIT.md](docs/SICHERHEIT.md) | Anmeldung, Sitzungen, CSRF, Geheimnisse, Rechtetrennung | | [docs/BETRIEB.md](docs/BETRIEB.md) | Installation, Update, HTTPS, Sicherung, Fehlersuche | +| [docs/SICHERHEIT.md](docs/SICHERHEIT.md) | Anmeldung, Sitzungen, CSRF, Geheimnisse, Rechtetrennung | ---- - -## Tests - -```bash -pytest tests -q -``` - -Dazu ein Skript, das **auf dem Zielhost gegen die laufende Installation** -spricht -- ueber nginx, gunicorn und echtes TLS. Es wird nicht mitinstalliert -(`install.sh` laesst `tests/` aus); bei Bedarf dorthin kopieren. Jeder Lauf legt -eigene Datensaetze an, ist also beliebig oft wiederholbar: - -```bash -python3 tests/e2e_smoke.py https://host admin 'Passwort' -``` +Aufbau und Entwurfsentscheidungen stehen in `docs/ARCHITEKTUR.md` im +Entwicklungsrepository, ebenso die Tests. --- @@ -167,10 +145,10 @@ anders geloest ist. Die wichtigsten: * **Rechte werden serverseitig geprueft**, nicht nur im Template ausgeblendet. * **Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert.** Ein falscher `alias`-Pfad ueberlebte im Vorgaenger jahrelang jedes Update. +* **Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft** -- nginx wie + Kea. Eine fehlerhafte Datei faellt beim Schreiben auf, nicht erst beim + naechsten Start des Dienstes. * **Kein Hostname im Lizenz-Fingerabdruck.** Umbenennen brach die Bindung. * **Zwei Installationen stoeren sich nicht** -- eigener `SITE_KEY` fuer nginx-Site, netplan, logrotate, Zertifikate und Sitzungscookie. * **Verschachtelte Datenbankverbindungen sind ein Fehler**, kein Deadlock. -* **Jede Konfiguration wird vor dem Uebernehmen geprueft** -- nginx wie Kea. - Eine fehlerhafte Datei faellt beim Schreiben auf, nicht erst beim naechsten - Start des Dienstes. diff --git a/deploy/install.sh b/deploy/install.sh index a27801a..8130693 100644 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -229,11 +229,16 @@ fi # Immer ueber "python3 -m pip" -- der pip-Shim im venv baeckt einen absoluten # Pfad in seinen Shebang, der nach einer Umbenennung des Verzeichnisses bricht. "$ROOT/venv/bin/python3" -m pip install --quiet --upgrade pip setuptools wheel -"$ROOT/venv/bin/python3" -m pip install --quiet \ - "$ROOT/packages/tesm-licensing" \ - "$ROOT/packages/tesm-core" \ - "$ROOT/apps/$APP" \ - gunicorn +PAKETE=("$ROOT/packages/tesm-licensing" "$ROOT/packages/tesm-core") +# Die Serverhaelfte des Lizenzprotokolls -- ausstellen, erneuern, antworten -- +# gehoert ausschliesslich zum Lizenzserver. Ein Client verifiziert Lizenzen und +# stellt Anfragen; den Code zum Ausstellen soll er nicht einmal mitbringen. +if [[ "$APP" == "tesm-license" ]]; then + [[ -d "$ROOT/packages/tesm-licensing-server" ]] || + fail "packages/tesm-licensing-server fehlt -- ohne sie kann der Lizenzserver nichts ausstellen" + PAKETE+=("$ROOT/packages/tesm-licensing-server") +fi +"$ROOT/venv/bin/python3" -m pip install --quiet "${PAKETE[@]}" "$ROOT/apps/$APP" gunicorn if [[ "$APP" == "tesm" ]]; then "$ROOT/venv/bin/python3" -m pip install --quiet flask-sock fi diff --git a/docs/ARCHITEKTUR.md b/docs/ARCHITEKTUR.md deleted file mode 100644 index cdecece..0000000 --- a/docs/ARCHITEKTUR.md +++ /dev/null @@ -1,166 +0,0 @@ -# Architektur - -Zwei Anwendungen, ein gemeinsamer Kern, ein gemeinsames Lizenzprotokoll. - -``` -packages/ - tesm-licensing/ Lizenzformat und -protokoll (Ed25519). Keine Web-Abhaengigkeit. - tesm-core/ Der Kern: Datenbank, Rechte, Anmeldung, Sitzungen, Protokolle, - Einstellungen, Systemzugriff, Oberflaechenbausteine. -apps/ - tesm/ Geraete- und Switchverwaltung (PoE, DHCP, Dateifreigaben, Wartung). - tesm-license/ Lizenzserver (Kunden, Tickets, Lizenzen, Portal). -deploy/ Helfer, sudoers, systemd, nginx, logrotate, install.sh -tests/ Unit- und Integrationstests, dazu drei E2E-Skripte fuer den Zielhost. -``` - -## Warum genau diese Schnitte - -**`tesm-licensing` ist ein eigenes Paket, keine kopierte Datei.** -Im Vorgaengerprojekt lag `licensing.py` zweimal im Repo -- einmal beim Client, -einmal beim Server -- mit der Auflage, beide byte-identisch zu halten. Das ist -keine Invariante, die ein Mensch dauerhaft einhaelt. Hier gibt es das Modul -einmal; beide Anwendungen installieren dasselbe Paket. -`tests/test_repo_hygiene.py` schlaegt Alarm, sobald irgendwo wieder eine Datei -namens `licensing.py` auftaucht. - -**`tesm-core` enthaelt alles, was beide Anwendungen gleich machen.** -Nicht nur Technik (Datenbank, Sitzungen, CSRF), sondern auch ganze Seiten: -Benutzer- und Gruppenverwaltung, Protokollansichten, Papierkorb, Diagnose, -Netzwerk-, Webserver- und Verzeichnisdiensteinstellungen. Eine Seite, die in -beiden Anwendungen gleich aussehen soll, existiert genau einmal. - -**Die Apps bringen nur ihre Fachlichkeit mit** -- plus vier Beschreibungen, die -der Kern auswertet: `permissions.py` (Rechtebaum), `navigation.py`, -`schema.py` (Migrationen), `settings_spec.py`. - -## Datenbank - -Rohes `sqlite3`, kein ORM. Begruendung: das Datenmodell ist klein und stabil, -die Abfragen sind lesbar, und es gibt keine Schicht, die im Fehlerfall erst -verstanden werden muss. - -Die Klasse `tesm_core.db.Database` erzwingt drei Dinge: - -* WAL-Modus, `foreign_keys=ON`, `busy_timeout=15000`. -* Verschachtelte Transaktionen laufen ueber SAVEPOINTs. -* Ein `connect()` **waehrend** einer offenen Schreibtransaktion loest einen - `NestedConnectionError` aus, statt in einen Deadlock zu laufen. Das ist der - strukturelle Riegel gegen die haeufigste Fehlerklasse des Vorgaengers: eine - Funktion oeffnete waehrend eines Schreibvorgangs eine zweite Verbindung, und - die Anwendung blieb stehen, bis das Zeitlimit zuschlug. - -Migrationen sind versioniert nach `(komponente, version)` mit Pruefsumme. Sie -laufen ueber `split_statements()`, nicht ueber `executescript()` -- letzteres -committet implizit und zerlegt die umgebende Transaktion. - -## Rechte - -Ein Rechtebaum aus **Bereich -> Ressource -> Aktion**. Die Aktionen sind ein -festes Vokabular: `VIEW`, `CREATE`, `EDIT`, `DELETE`, `EXECUTE`, `EXPORT`, -`SECRETS`. - -* `SECRETS` und `EXPORT` sind eigene Rechte. Wer Zugangsdaten bearbeiten darf, - darf sie damit noch nicht im Klartext sehen oder ausfuehren. -* Bereiche haben einen Hauptschalter: ein abgeschalteter Bereich ist weg, - unabhaengig von Einzelrechten. -* **Keine Rechteausweitung:** niemand kann einem anderen Konto ein Recht geben, - das er selbst nicht hat (`guard_no_amplification`). Ebenso wenig laesst sich - der letzte Administrator entfernen oder sperren (`guard_last_admin`). -* Jede Pruefung ist serverseitig. Das Template blendet zusaetzlich aus, was - nicht erlaubt ist -- aber es *entscheidet* nichts. Im Vorgaenger wurden - Verzeichnisdienst und Webserver nur im Template geprueft; ein direkter POST - kam durch. - -## Lizenzierung - -Ed25519, mit den Haertungen, die ein Signaturprotokoll erst brauchbar machen: - -* **Getrennte Signaturkontexte** fuer Lizenz, Anfrage und Antwort. Eine - Signatur aus dem einen Kontext ist im anderen wertlos. -* **Kanonisches JSON** -- die Signatur haengt nicht an der Schluesselreihenfolge. -* **Nonce gegen Wiedereinspielung**, Antwort an die Anfrage gebunden. -* **Frischefenster** gegen alte, mitgeschnittene Antworten. -* **`seq` gegen Rueckstufung:** eine aeltere Revision wird abgelehnt. -* **Aussteller-Pinning (Trust on first use).** Die erste eingespielte Lizenz - pinnt den oeffentlichen Schluessel ihres Ausstellers. Ein Wechsel verlangt - eine ausdrueckliche Bestaetigung durch einen Administrator. -* **Fingerabdruck ohne Hostnamen.** Er bildet sich aus Maschinen-ID und - DMI-UUID. Der Hostname ist bewusst **nicht** enthalten: im Vorgaenger brach - jedes Umbenennen des Rechners die Bindung. - -Der Ablauf: Kunde -> Ticket -> Lizenz. Das Ticket haelt die Parameter (Typ, -Module, Laufzeit, Rolle); die Lizenz ist das signierte Ergebnis. Ein zweiter -Klick auf "Ausstellen" erzeugt keine zweite Lizenz, sondern zeigt die offene. - -Aenderungen an einer laufenden Lizenz brauchen keine Neuinstallation: der -Anbieter signiert eine neue Revision, der Client holt sie beim naechsten -Heartbeat. - -## Systemzugriff - -Der Webprozess laeuft unprivilegiert. Root-Aktionen gehen ausschliesslich durch -`deploy/tesm-helper`: ein Skript mit fester Verbliste, Argumentpruefung gegen -Positivlisten und ohne jede Weitergabe an eine Shell. sudoers gibt genau diesen -einen Pfad frei. - -Aktionen, die mehrere Schritte brauchen, sind **ein** Verb -- nicht drei. -`nginx-apply` etwa schreibt, prueft und laedt neu; scheitert `nginx -t`, stellt -es den vorherigen Stand wieder her. Getrennte Verben hatten hier eine echte -Luecke: `nginx-write` verlinkt sofort nach `sites-enabled`, und eine -anschliessend abgelehnte Datei blieb aktiviert liegen. Der laufende nginx merkte -nichts -- der naechste Reload durch irgendwen legte den ganzen Webserver lahm. - -## Mehrere Installationen auf einem Host - -Zwei Schluessel, mit klarer Aufgabenteilung: - -* **`app_key`** (`tesm`, `tesm-license`) -- was die Anwendung *ist*. Bestimmt - Env-Praefix, Rechtebaum, Migrationsnamensraum. -* **`site_key`** (`tesm`, `tesm-opus`, ...) -- welche Installation es *ist*. - Bestimmt alles ausserhalb von `/srv/`: nginx-Site, netplan-Datei, - logrotate-Regel, Zertifikatsverzeichnis und den Namen des Sitzungscookies. - -Ohne diese Trennung wuerde eine Testinstanz beim Speichern der -Webserver-Einstellungen die Site-Datei der Produktion ueberschreiben -- und -beide Installationen teilten sich dasselbe Sitzungscookie, weil Cookies keine -Ports unterscheiden. - -Die Adresse des Anwendungsservers (`BIND`) steht in derselben Umgebungsdatei, -aus der auch gunicorn seinen `--bind` bekommt. Sie ist zugleich das -`proxy_pass`-Ziel der erzeugten nginx-Site. Vorher war die `5000` fest im Code: -eine Instanz auf Port 5100 erzeugte damit eine Konfiguration, die auf die -*andere* Installation zeigte. - -## Oberflaeche - -Handgeschriebenes CSS in `@layer`n mit semantischen Tokens, drei Themen (hell, -dunkel, System). JavaScript als ES-Module, angebunden ueber -`data-behavior`-Attribute -- kein Inline-Script, kein Build-Schritt, und damit -vertraeglich mit einer strikten CSP, die `unsafe-inline` fuer Skripte nicht -erlaubt. - -## Tests - -| Datei | Was sie absichert | -|---|---| -| `test_licensing.py` | Format, Signaturen, Protokoll, Fingerabdruck | -| `test_core_smoke.py` | Kern: Datenbank, Migrationen, Schluesselspeicher, Sitzungen | -| `test_core_security.py` | Passwoerter, TOTP, Ratenbegrenzung, CSRF, Rechte | -| `test_webserver.py` | nginx-Vorlage, TLS, Zertifikate, Instanztrennung | -| `test_tesm_smoke.py` | TESM: Seiten, Validierung, Rechtematrix | -| `test_license_server.py` | Lizenzserver: Ausstellung, Portal, API | -| `test_repo_hygiene.py` | Zeilenenden, Geheimnisse im Repo, Helferpruefungen, sudoers | - -Dazu drei Skripte, die **auf dem Zielhost gegen die echte Installation** laufen -(nginx, gunicorn, systemd, TLS -- keine Attrappen): - -```bash -python3 tests/e2e_smoke.py https://host admin 'Passwort' -python3 tests/e2e_license_server.py https://lizenz.host admin 'Passwort' -python3 tests/e2e_license.py https://lizenz.host https://host admin 'Passwort' http://127.0.0.1:5001 -``` - -Das dritte spielt den vollstaendigen Lizenz-Lebenszyklus ueber beide -Anwendungen hinweg durch: ausstellen, herunterladen, einspielen, aktivieren, -Module pruefen, Heartbeat, Revision, Widerruf. diff --git a/packages/tesm-licensing/src/tesm_licensing/__init__.py b/packages/tesm-licensing/src/tesm_licensing/__init__.py index 20c2cc6..3e2a5c4 100644 --- a/packages/tesm-licensing/src/tesm_licensing/__init__.py +++ b/packages/tesm-licensing/src/tesm_licensing/__init__.py @@ -16,8 +16,6 @@ from .canonical import ( canonical_bytes, decode_code, encode_code, - generate_keypair, - public_from_private, sign, verify, ) @@ -44,12 +42,10 @@ from .license import ( ROLE_LABELS, TRIAL_DEFAULT_DAYS, LicenseStatus, - issue_license, iso, key_id, license_status, parse_iso, - resign_license, split_bundle, utcnow, verify_license, @@ -62,9 +58,7 @@ from .protocol import ( REQUEST_MAX_AGE_SECONDS, RESPONSE_STATES, build_client_request, - build_master_response, new_nonce, - verify_client_request, verify_master_response, ) @@ -100,26 +94,20 @@ __all__ = [ "TRIAL_DEFAULT_DAYS", "UntrustedIssuer", "build_client_request", - "build_master_response", "canonical_bytes", "decode_code", "encode_code", - "generate_keypair", - "issue_license", "iso", "key_id", "license_status", "machine_identity", "new_nonce", "parse_iso", - "public_from_private", - "resign_license", "sign", "split_bundle", "system_fingerprint", "utcnow", "verify", - "verify_client_request", "verify_license", "verify_master_response", ] diff --git a/packages/tesm-licensing/src/tesm_licensing/canonical.py b/packages/tesm-licensing/src/tesm_licensing/canonical.py index 19ad3aa..11c50c6 100644 --- a/packages/tesm-licensing/src/tesm_licensing/canonical.py +++ b/packages/tesm-licensing/src/tesm_licensing/canonical.py @@ -45,21 +45,8 @@ def _b64d(value: str, *, field: str) -> bytes: raise ValueError(f"{field} ist kein gueltiges Base64") from exc -def generate_keypair() -> tuple[str, str]: - """Erzeugt ein Ed25519-Schluesselpaar. Rueckgabe: ``(private_b64, public_b64)``.""" - private = ed25519.Ed25519PrivateKey.generate() - return ( - base64.b64encode(private.private_bytes_raw()).decode("ascii"), - base64.b64encode(private.public_key().public_bytes_raw()).decode("ascii"), - ) -def public_from_private(private_key_b64: str) -> str: - """Leitet den oeffentlichen Teil aus einem privaten Schluessel ab.""" - key = ed25519.Ed25519PrivateKey.from_private_bytes( - _b64d(private_key_b64, field="private_key") - ) - return base64.b64encode(key.public_key().public_bytes_raw()).decode("ascii") def sign(payload: Mapping[str, Any], private_key_b64: str, context: bytes) -> str: diff --git a/packages/tesm-licensing/src/tesm_licensing/license.py b/packages/tesm-licensing/src/tesm_licensing/license.py index feec766..c4b24ac 100644 --- a/packages/tesm-licensing/src/tesm_licensing/license.py +++ b/packages/tesm-licensing/src/tesm_licensing/license.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from typing import Any, Iterable, Mapping, Sequence -from .canonical import SIG_CONTEXT_LICENSE, generate_keypair, sign, verify +from .canonical import SIG_CONTEXT_LICENSE, sign, verify from .errors import InvalidLicenseFile, SignatureInvalid, UntrustedIssuer FORMAT_VERSION = 2 @@ -147,110 +147,10 @@ class LicenseStatus: return self.modules_active and name in self.modules -def _validate_issue_args( - license_type: str, role: str, modules: Sequence[str], lifetime: bool -) -> None: - if license_type not in LICENSE_TYPES: - raise ValueError(f"Unbekannter Lizenztyp: {license_type!r}") - if role not in LICENSE_ROLES: - raise ValueError(f"Unbekannte Rolle: {role!r}") - unknown = sorted(set(modules) - set(ALL_MODULES)) - if unknown: - raise ValueError(f"Unbekannte Module: {', '.join(unknown)}") - if lifetime and license_type == "trial": - # Dritte und letzte Absicherung derselben Regel (UI + Service pruefen - # vorher schon): eine unbefristete Trial-Lizenz waere eine permanente - # Lizenz ohne Module -- ein sinnloser Zustand. - raise ValueError("Trial-Lizenzen koennen nicht unbefristet sein") - if role != "customer" and modules: - raise ValueError("Lizenzserver-Lizenzen tragen keine TESM-Module") -def issue_license( - *, - customer: Mapping[str, Any], - license_type: str, - modules: Iterable[str], - valid_days: int, - master_private_key_b64: str, - master_public_key_b64: str, - master_endpoint: str, - vendor: Mapping[str, Any], - license_id: str | None = None, - role: str = "customer", - lifetime: bool = False, - seq: int = 1, - now: datetime | None = None, -) -> tuple[dict[str, Any], str]: - """Stellt eine Lizenz aus. - - Rueckgabe: ``(bundle, license_public_key)``. ``bundle`` ist die Datei, die - an den Kunden geht; ``license_public_key`` wandert in die Master-Datenbank, - damit der Master spaeter Client-Anfragen dieser Lizenz verifizieren kann. - """ - module_list = sorted({str(m) for m in modules}) - _validate_issue_args(license_type, role, module_list, lifetime) - if not lifetime and int(valid_days) <= 0: - raise ValueError("valid_days muss positiv sein") - - moment = now or utcnow() - client_private, client_public = generate_keypair() - payload: dict[str, Any] = { - "fmt": FORMAT_VERSION, - "license_id": license_id or str(uuid.uuid4()), - "seq": int(seq), - "customer": dict(customer), - "type": license_type, - "role": role, - "modules": module_list, - "lifetime": bool(lifetime), - "issued_at": iso(moment), - "not_before": iso(moment), - "expires_at": LIFETIME_EXPIRES_AT - if lifetime - else iso(moment + timedelta(days=int(valid_days))), - "license_pubkey": client_public, - "master_pubkey": master_public_key_b64, - "master_key_id": key_id(master_public_key_b64), - "master_endpoint": (master_endpoint or "").rstrip("/"), - "vendor": dict(vendor), - } - signature = sign(payload, master_private_key_b64, SIG_CONTEXT_LICENSE) - document = {**payload, "signature": signature} - bundle = { - "kind": "tesm-license-bundle", - "fmt": FORMAT_VERSION, - "license": document, - "client_key": client_private, - } - return bundle, client_public -def resign_license( - document: Mapping[str, Any], - *, - master_private_key_b64: str, - changes: Mapping[str, Any], - now: datetime | None = None, -) -> dict[str, Any]: - """Erzeugt eine neu signierte Revision eines bestehenden Lizenzdokuments. - - Wird fuer ``license_update`` im Heartbeat gebraucht (z. B. nachtraeglich - erweiterte Module oder verlaengerte Laufzeit). ``seq`` steigt dabei zwingend, - damit der Client eine aeltere Revision nicht als Update akzeptiert. - """ - payload = {k: v for k, v in document.items() if k != "signature"} - payload.update(changes) - payload["seq"] = int(document.get("seq") or 1) + 1 - payload["issued_at"] = iso(now or utcnow()) - _validate_issue_args( - str(payload.get("type")), - str(payload.get("role")), - [str(m) for m in payload.get("modules") or ()], - bool(payload.get("lifetime")), - ) - payload["signature"] = sign(payload, master_private_key_b64, SIG_CONTEXT_LICENSE) - return payload def split_bundle(bundle: Mapping[str, Any]) -> tuple[dict[str, Any], str]: diff --git a/packages/tesm-licensing/src/tesm_licensing/protocol.py b/packages/tesm-licensing/src/tesm_licensing/protocol.py index f053e45..ee8d8b7 100644 --- a/packages/tesm-licensing/src/tesm_licensing/protocol.py +++ b/packages/tesm-licensing/src/tesm_licensing/protocol.py @@ -95,7 +95,7 @@ def build_client_request( return payload -def _check_freshness(timestamp: str, now: datetime, what: str) -> None: +def check_freshness(timestamp: str, now: datetime, what: str) -> None: try: stamp = parse_iso(timestamp) except (ValueError, TypeError) as exc: @@ -106,89 +106,8 @@ def _check_freshness(timestamp: str, now: datetime, what: str) -> None: raise ReplayDetected(f"{what}: Zeitstempel ist zu alt") -def verify_client_request( - request: Mapping[str, Any], - license_public_key: str, - *, - expected_action: str | None = None, - now: datetime | None = None, - seen_nonce: Callable[[str], bool] | None = None, -) -> None: - """Verifiziert eine Client-Anfrage serverseitig. Wirft bei Problemen. - - ``seen_nonce`` ist ein Callback, der ``True`` zurueckgibt, wenn die Nonce - bereits verbraucht ist. Der Aufrufer entscheidet ueber die Persistenz - (in dieser Codebasis: Tabelle ``license_nonces``). - """ - if not isinstance(request, Mapping): - raise ProtocolViolation("Anfrage ist kein Objekt") - if int(request.get("fmt") or 0) != FORMAT_VERSION: - raise ProtocolViolation("Nicht unterstuetzte Protokollversion") - - action = str(request.get("action") or "") - if action not in ACTIONS: - raise ProtocolViolation("Unbekannte Aktion") - if expected_action and action != expected_action: - raise ProtocolViolation("Aktion passt nicht zum Endpunkt") - - nonce = str(request.get("nonce") or "") - if not _NONCE_RE.match(nonce): - raise ProtocolViolation("Nonce hat ein unerwartetes Format") - fingerprint = str(request.get("fingerprint") or "") - if not _FINGERPRINT_RE.match(fingerprint): - raise ProtocolViolation("Fingerprint hat ein unerwartetes Format") - - payload = {k: v for k, v in request.items() if k != "signature"} - if not verify( - payload, str(request.get("signature") or ""), license_public_key, SIG_CONTEXT_REQUEST - ): - raise SignatureInvalid("Signatur der Anfrage ist ungueltig") - - # Frische und Replay erst *nach* der Signaturpruefung: unsignierte Muellanfragen - # sollen keine Nonce-Eintraege in der Datenbank erzeugen koennen. - moment = now or utcnow() - _check_freshness(str(request.get("timestamp") or ""), moment, "Anfrage") - if seen_nonce is not None and seen_nonce(nonce): - raise ReplayDetected("Diese Anfrage wurde bereits verarbeitet") - - report = request.get("report") - if report is not None: - if not isinstance(report, Mapping): - raise ProtocolViolation("Bericht ist kein Objekt") - total = sum(len(report.get(key) or ()) for key in ("customers", "tickets", "licenses")) - if total > MAX_REPORT_ROWS: - raise ProtocolViolation("Bericht ueberschreitet die Groessengrenze") -def build_master_response( - action: str, - *, - license_id: str, - fingerprint: str, - request_nonce: str, - master_private_key: str, - status: str = "ok", - detail: str = "", - license_update: Mapping[str, Any] | None = None, - now: datetime | None = None, -) -> dict[str, Any]: - """Baut eine signierte Master-Antwort, gebunden an die Nonce der Anfrage.""" - if status not in RESPONSE_STATES: - raise ProtocolViolation(f"Unbekannter Antwortstatus: {status!r}") - payload: dict[str, Any] = { - "fmt": FORMAT_VERSION, - "license_id": license_id, - "fingerprint": fingerprint, - "action": action, - "status": status, - "detail": detail, - "nonce": request_nonce, - "timestamp": iso(now or utcnow()), - } - if license_update is not None: - payload["license_update"] = dict(license_update) - payload["signature"] = sign(payload, master_private_key, SIG_CONTEXT_RESPONSE) - return payload def verify_master_response( @@ -230,7 +149,7 @@ def verify_master_response( if str(response.get("status") or "") not in RESPONSE_STATES: raise ProtocolViolation("Unbekannter Antwortstatus") - _check_freshness(str(response.get("timestamp") or ""), now or utcnow(), "Antwort") + check_freshness(str(response.get("timestamp") or ""), now or utcnow(), "Antwort") update = response.get("license_update") if update is not None: diff --git a/tesm-v2.0.0.tar.gz b/tesm-v2.0.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6f10933401662a8109b7c0ee21ec1cba62dfbf4d GIT binary patch literal 374007 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≫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
  • LI> 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 Path: - base = tmp_path / "instance" - monkeypatch.setenv("TESM_BASE_DIR", str(base)) - monkeypatch.setenv("TESM_LICENSE_BASE_DIR", str(tmp_path / "instance-license")) - # Tests laufen ueber http:// -- ein Secure-Cookie wuerde nie gesetzt. - monkeypatch.setenv("TESM_COOKIE_SECURE", "0") - monkeypatch.setenv("TESM_LICENSE_COOKIE_SECURE", "0") - monkeypatch.setenv("TESM_DEBUG", "0") - monkeypatch.setenv("TESM_LICENSE_DEBUG", "0") - return base - - -@pytest.fixture() -def core_app(instance_env: Path) -> Iterator[Any]: - """Minimale App auf Basis von ``tesm_core`` -- ohne Fachlogik der beiden Apps.""" - from flask import render_template_string - - from tesm_core import load_core_config - from tesm_core.app_factory import create_base_app - from tesm_core.auth.models import login_required - from tesm_core.nav import NavItem, NavTree - from tesm_core.rbac.common import administration_area, observability_area - from tesm_core.rbac.model import Action, Area, PermissionTree, Resource, RolePreset - - demo = Area( - key="demo", - label="Demo", - icon="grid", - resources=( - Resource( - key="widgets", - label="Widgets", - actions=(Action.VIEW, Action.CREATE, Action.EDIT, Action.DELETE, Action.SECRETS), - ), - ), - ) - tree = PermissionTree([demo, administration_area(), observability_area()]) - nav = NavTree( - [ - NavItem(key="index", label="Start", icon="grid", endpoint="index"), - NavItem( - key="widgets", label="Widgets", icon="cpu", endpoint="widgets", - permissions=("widgets.view",), - ), - NavItem( - key="administration", - label="Verwaltung", - icon="sliders", - children=( - NavItem( - key="users", label="Benutzer", icon="users", endpoint="admin.users", - permissions=("users.view",), - ), - NavItem( - key="groups", label="Gruppen", icon="shield-users", endpoint="admin.groups", - permissions=("groups.view",), - ), - NavItem( - key="trash", label="Papierkorb", icon="trash", endpoint="trash.overview", - permissions=("trash.view",), - ), - NavItem( - key="diagnostics", label="Diagnose", icon="info", - endpoint="diagnostics.overview", permissions=("diagnostics.view",), - ), - ), - ), - ] - ) - presets = ( - RolePreset( - key="viewer", - name="Benutzer", - description="Nur lesen", - grants=(("widgets", (Action.VIEW,)),), - is_default=True, - ), - ) - - config = load_core_config( - app_key="tesm", app_name="TESM Test", env_prefix="TESM_", default_base=instance_env - ) - app = create_base_app( - config=config, - permissions=tree, - nav=nav, - role_presets=presets, - app_short="TT", - app_edition="Test", - enable_license=False, - ) - - @app.get("/") - def index(): # type: ignore[no-untyped-def] - return render_template_string( - '{% extends "tesm_core/base.html" %}{% block page_title %}Start{% endblock %}' - "{% block content %}

    Start

    {% endblock %}" - ) - - @app.get("/widgets") - @login_required - def widgets(): # type: ignore[no-untyped-def] - return render_template_string( - '{% extends "tesm_core/base.html" %}{% block page_title %}Widgets{% endblock %}' - "{% block content %}

    Widgets

    {% endblock %}" - ) - - app.config.update(TESTING=True) - yield app - - -@pytest.fixture() -def client(core_app: Any) -> Any: - return core_app.test_client() - - -@pytest.fixture() -def make_user(core_app: Any): - """Erzeugt ein Konto direkt in der Datenbank.""" - - def factory( - username: str = "tester", - password: str = "Sicher-Passwort-2026", - *, - is_admin: bool = False, - permissions: tuple[str, ...] = (), - is_locked: bool = False, - ) -> dict[str, Any]: - from tesm_core.db import Database - from tesm_core.extension import core - from tesm_core.security import passwords - - extension = core(core_app) - with extension.database.session() as conn, extension.database.transaction(conn): - cursor = conn.execute( - "INSERT INTO users (username, password_hash, is_admin, is_locked, auth_source, " - "created_at, updated_at) VALUES (?,?,?,?,'local',datetime('now'),datetime('now'))", - (username, passwords.hash_password(password), int(is_admin), int(is_locked)), - ) - user_id = int(cursor.lastrowid or 0) - if permissions: - group = conn.execute( - "INSERT INTO groups (name, description, created_at, updated_at) " - "VALUES (?,'Testgruppe',datetime('now'),datetime('now'))", - (f"grp-{username}",), - ) - group_id = int(group.lastrowid or 0) - conn.executemany( - "INSERT INTO group_permissions (group_id, permission) VALUES (?,?)", - [(group_id, p) for p in extension.permissions.sanitize(permissions)], - ) - conn.execute( - "INSERT INTO user_groups (user_id, group_id) VALUES (?,?)", (user_id, group_id) - ) - row = Database.one(conn, "SELECT * FROM users WHERE id=?", (user_id,)) - assert row is not None - return {**row, "password": password} - - return factory - - -@pytest.fixture() -def login(client: Any): - def do_login(username: str, password: str) -> Any: - return client.post( - "/login", - data={"username": username, "password": password, "csrf_token": _token(client)}, - follow_redirects=True, - ) - - return do_login - - -def _token(client: Any) -> str: - """Holt ein CSRF-Token aus der Anmeldeseite.""" - page = client.get("/login") - body = page.get_data(as_text=True) - marker = 'name="csrf_token" value="' - start = body.index(marker) + len(marker) - return body[start : body.index('"', start)] - - -@pytest.fixture() -def csrf_token(client: Any): - def get(path: str = "/login") -> str: - page = client.get(path) - body = page.get_data(as_text=True) - marker = 'name="csrf_token" value="' - if marker not in body: - marker = 'data-csrf="' - start = body.index(marker) + len(marker) - return body[start : body.index('"', start)] - - return get diff --git a/tests/e2e_smoke.py b/tests/e2e_smoke.py deleted file mode 100644 index 66e3d3c..0000000 --- a/tests/e2e_smoke.py +++ /dev/null @@ -1,499 +0,0 @@ -"""End-to-End-Rauchtest gegen eine laufende Installation. - -Laeuft auf dem Zielhost, spricht ueber HTTP mit nginx und der echten Anwendung --- kein Testclient, keine Attrappen. Prueft genau das, was ein Mensch beim -ersten Durchklicken pruefen wuerde, plus die Dinge, die man beim Klicken leicht -uebersieht (Sicherheitsheader, CSRF, statische Dateien, Rechteschranken). - -Aufruf: - python3 e2e_smoke.py http://127.0.0.1:8080 admin 'Passwort' -""" - -from __future__ import annotations - -import http.cookiejar -import json -import re -import secrets -import ssl -import subprocess -import sys -import urllib.error -import urllib.parse -import urllib.request - -CSRF_RE = re.compile(r'name="csrf_token" value="([^"]+)"') -BODY_CSRF_RE = re.compile(r'data-csrf="([^"]+)"') - - -class Client: - def __init__(self, base: str) -> None: - self.base = base.rstrip("/") - self.https = self.base.startswith("https://") - self.jar = http.cookiejar.CookieJar() - - # Der Test laeuft auf dem Zielhost selbst gegen ein Zertifikat, das - # dieselbe Maschine ausgestellt hat. Es zu pruefen hiesse, das eigene - # Zertifikat gegen sich selbst zu pruefen -- geprueft wird hier, ob TLS - # steht und die Anwendung darueber antwortet. - context = ssl.create_default_context() - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - self.tls = urllib.request.HTTPSHandler(context=context) - - self.opener = urllib.request.build_opener( - urllib.request.HTTPCookieProcessor(self.jar), - urllib.request.HTTPRedirectHandler(), - self.tls, - ) - self.opener.addheaders = [("User-Agent", "tesm-e2e/2.0")] - - class _NoRedirect(urllib.request.HTTPRedirectHandler): - def redirect_request(self, *_args, **_kwargs): # type: ignore[override] - return None - - # Zweiter Opener ohne Weiterleitung: nur so laesst sich pruefen, ob eine - # Seite tatsaechlich mit 302 auf die Anmeldung verweist. - self.plain = urllib.request.build_opener( - urllib.request.HTTPCookieProcessor(self.jar), _NoRedirect(), self.tls - ) - self.plain.addheaders = [("User-Agent", "tesm-e2e/2.0")] - - def get(self, path: str) -> tuple[int, str, dict[str, str]]: - request = urllib.request.Request(self.base + path) - try: - with self.opener.open(request, timeout=20) as response: - return response.status, response.read().decode("utf-8", "replace"), dict( - response.headers - ) - except urllib.error.HTTPError as exc: - return exc.code, exc.read().decode("utf-8", "replace"), dict(exc.headers) - - def get_raw(self, path: str) -> int: - """Statuscode ohne Weiterleitung zu folgen.""" - request = urllib.request.Request(self.base + path) - try: - with self.plain.open(request, timeout=20) as response: - return response.status - except urllib.error.HTTPError as exc: - return exc.code - - def post(self, path: str, data: dict[str, object]) -> tuple[int, str]: - encoded = urllib.parse.urlencode( - [ - (key, str(item)) - for key, value in data.items() - for item in (value if isinstance(value, (list, tuple)) else [value]) - ] - ).encode("utf-8") - request = urllib.request.Request( - self.base + path, - data=encoded, - headers={ - "Content-Type": "application/x-www-form-urlencoded", - "Origin": self.base, - "Referer": self.base + path, - }, - ) - try: - with self.opener.open(request, timeout=30) as response: - return response.status, response.read().decode("utf-8", "replace") - except urllib.error.HTTPError as exc: - return exc.code, exc.read().decode("utf-8", "replace") - - def token(self, path: str) -> str: - _, body, _ = self.get(path) - match = CSRF_RE.search(body) or BODY_CSRF_RE.search(body) - if not match: - raise AssertionError(f"Kein CSRF-Token auf {path}") - return match.group(1) - - -PASSED: list[str] = [] -FAILED: list[str] = [] - -#: Jeder Lauf legt eigene Datensaetze an. Sonst laesst sich der Test kein -#: zweites Mal gegen dieselbe Installation ausfuehren -- beim ersten Lauf -#: landet ein Geraet im Papierkorb, und die Anwendung weigert sich danach -#: voellig zu Recht, dieselbe MAC noch einmal zu vergeben. -RUN = secrets.token_hex(3) -MAC = ":".join(("AA", "BB", RUN[0:2], RUN[2:4], RUN[4:6], "01")).upper() -SUBNET = f"10.99.{int(RUN[0:2], 16)}" - -FLASH_RE = re.compile(r'id="flashed-messages">(.*?)', re.S) -ROW_SPLIT = re.compile(r"]|]") - - -def flash(body: str) -> str: - """Die Meldungen der Seite -- damit ein Fehlschlag erklaert, was schiefging.""" - match = FLASH_RE.search(body) - if not match: - return "keine Meldung" - try: - return " | ".join(text for _level, text in json.loads(match.group(1))) - except (ValueError, TypeError): - return match.group(1)[:120] - - -def id_for(body: str, needle: str, pattern: str) -> str: - """Sucht die Kennung in der Zeile, in der ``needle`` steht. - - Die Listenansicht nennt die Kennung nur in Formular- und Link-Zielen. Der - Test darf sie nicht raten -- eine feste ``1`` funktioniert genau einmal und - trifft beim naechsten Lauf einen fremden Datensatz. - """ - for chunk in ROW_SPLIT.split(body): - if needle in chunk: - match = re.search(pattern, chunk) - if match: - return match.group(1) - return "" - - -def check(name: str, condition: bool, detail: str = "") -> None: - if condition: - PASSED.append(name) - print(f" [ok] {name}") - else: - FAILED.append(f"{name}: {detail}") - print(f" [FEHL] {name} -- {detail}") - - -def local_host_fingerprints() -> set[str]: - """Die Fingerprints, die ``ssh-keyscan`` fuer diesen Host meldet. - - Der Vergleich ist der Kern der Pruefung: die Anwendung darf nicht - irgendeinen plausiblen Wert anzeigen, sondern genau den des Gegenuebers. - """ - try: - scan = subprocess.run( - ["ssh-keyscan", "-T", "5", "-p", "22", "127.0.0.1"], - capture_output=True, - text=True, - timeout=20, - ) - fingerprints: set[str] = set() - for line in scan.stdout.splitlines(): - if not line.strip() or line.startswith("#"): - continue - result = subprocess.run( - ["ssh-keygen", "-lf", "-"], input=line, capture_output=True, text=True, timeout=10 - ) - match = re.search(r"(SHA256:[A-Za-z0-9+/]{43})", result.stdout) - if match: - fingerprints.add(match.group(1)) - return fingerprints - except (OSError, subprocess.SubprocessError): - return set() - - -def main(base: str, username: str, password: str) -> int: - client = Client(base) - print(f"\n== Rauchtest gegen {base} (Lauf {RUN}) ==\n") - - # -- oeffentlich --------------------------------------------------------- - print("Oeffentlich erreichbar:") - status, body, headers = client.get("/gesundheit") - check("Gesundheitsendpunkt", status == 200 and json.loads(body)["status"] == "ok", body[:80]) - - status, body, headers = client.get("/login") - check("Anmeldeseite", status == 200 and "Anmelden" in body, f"HTTP {status}") - if client.https: - cookie = headers.get("Set-Cookie", "") - check("Sitzungscookie mit Secure", "Secure" in cookie, cookie[:80] or "kein Cookie") - check("Sitzungscookie HttpOnly", "HttpOnly" in cookie, cookie[:80] or "kein Cookie") - check("Sitzungscookie SameSite", "SameSite" in cookie, cookie[:80] or "kein Cookie") - csp = headers.get("Content-Security-Policy", "") - check("CSP mit Nonce", "nonce-" in csp and "unsafe-inline" not in csp.split("style-src")[0], - csp[:90]) - check("X-Frame-Options", headers.get("X-Frame-Options") == "DENY", str(headers.get("X-Frame-Options"))) - check("Vorgangsnummer im Header", bool(headers.get("X-Request-Id")), "fehlt") - - status, body, _ = client.get("/") - check("Oeffentliche Statusuebersicht", status == 200 and "Statusuebersicht" in body, - f"HTTP {status}") - check("Ohne Anmeldung keine Details", "oeffentliche Ansicht" in body, "Hinweis fehlt") - - for asset in ("/core-assets/css/base.css", "/core-assets/js/app.js"): - status, body, _ = client.get(asset) - check(f"Statische Datei {asset}", status == 200 and len(body) > 1000, f"HTTP {status}") - - print("\nSchranken ohne Anmeldung:") - for path in ("/clients/", "/verwaltung/benutzer", "/diagnose/"): - status = client.get_raw(path) - check(f"{path} verlangt Anmeldung", status in (302, 401), f"HTTP {status}") - - print("\nCSRF:") - status, body = client.post("/login", {"username": "x", "password": "y"}) - check("POST ohne Token wird abgewiesen", status == 403, f"HTTP {status}") - - # -- Anmeldung ----------------------------------------------------------- - print("\nAnmeldung:") - token = client.token("/login") - status, body = client.post( - "/login", {"username": username, "password": password, "csrf_token": token} - ) - check("Anmeldung erfolgreich", "Willkommen" in body or status == 200, f"HTTP {status}") - - status, body, _ = client.get("/konto/") - check("Kontoseite erreichbar", status == 200 and username in body, f"HTTP {status}") - - # -- Seiten -------------------------------------------------------------- - print("\nSeiten (angemeldet):") - pages = [ - ("/", "Statusuebersicht"), - ("/clients/", "Clients"), - ("/switche/", "Switche"), - ("/zugangsdaten/", "Zugangsdaten"), - ("/protokolle/laufend", "Protokoll"), - ("/protokolle/aenderungen", "Aenderungsprotokoll"), - ("/protokolle/neustarts", "Neustarts"), - ("/einstellungen/system", "Systemeinstellungen"), - ("/einstellungen/netzwerk", "Netzwerk"), - ("/einstellungen/webserver", "Webserver"), - ("/einstellungen/verzeichnisdienst", "Verzeichnisdienst"), - ("/lizenz/", "Lizenz"), - ("/sicherung/", "Sicherung"), - ("/papierkorb/", "Papierkorb"), - ("/diagnose/", "Diagnose"), - ("/verwaltung/benutzer", "Benutzer"), - ("/verwaltung/gruppen", "Gruppen"), - ("/verwaltung/sitzungen", "Sitzungen"), - ("/konto/sicherheit", "Sicherheit"), - ] - for path, needle in pages: - status, body, _ = client.get(path) - ok = status == 200 and needle in body and "Interner Fehler" not in body - check(f"{path}", ok, f"HTTP {status}") - - # Die Modulrouten sind an die Lizenz gebunden. Welcher Zustand gerade gilt, - # darf der Test nicht vorschreiben -- er darf nur verlangen, dass alle drei - # dasselbe sagen. Ein Modul, das ohne Lizenz durchkommt, waere ein Loch; - # ein Modul, das mit Lizenz 404 liefert, waere ein Ausfall. - print("\nLizenzpflichtige Module:") - module_status = { - path: client.get(path)[0] for path in ("/dhcp/", "/wartung/", "/dateifreigaben/") - } - licensed = set(module_status.values()) == {200} - unlicensed = set(module_status.values()) == {404} - check( - "Alle Modulrouten im selben Zustand", - licensed or unlicensed, - ", ".join(f"{path} {code}" for path, code in module_status.items()), - ) - print(f" [info] Lizenz ist {'aktiv' if licensed else 'nicht aktiv'}.") - - # -- Daten anlegen ------------------------------------------------------- - print("\nDaten anlegen:") - credential_name = f"E2E-Switchlogin-{RUN}" - switch_name = f"e2e-switch-{RUN}" - device_name = f"E2E-Kamera-{RUN}" - secret = f"Geheim-fuer-den-Test-{RUN}" - - token = client.token("/zugangsdaten/") - status, body = client.post( - "/zugangsdaten/neu", - { - "csrf_token": token, - "name": credential_name, - "username": "operator", - "secret": secret, - "category": "switch", - }, - ) - check("Zugangsdaten angelegt", "angelegt" in body, flash(body)) - credential_id = id_for(body, credential_name, r"/zugangsdaten/(\d+)/") - check("Kennung der Zugangsdaten gefunden", bool(credential_id), "nicht gefunden") - - token = client.token("/switche/") - status, body = client.post( - "/switche/neu", - { - "csrf_token": token, - "hostname": switch_name, - "ip": f"{SUBNET}.2", - "ssh_port": "22", - "credential_id": credential_id, - }, - ) - check("Switch angelegt", "angelegt" in body, flash(body)) - switch_id = id_for(body, switch_name, r"/switche/(\d+)") - check("Kennung des Switches gefunden", bool(switch_id), "nicht gefunden") - - token = client.token("/clients/") - status, body = client.post( - "/clients/neu", - { - "csrf_token": token, - "name": device_name, - "mac": MAC, - "ip": f"{SUBNET}.50", - "switch_id": switch_id, - "port": "7", - "ssh_port": "22", - "is_active": "1", - "auto_restart": "1", - }, - ) - check("Geraet angelegt", "angelegt" in body, flash(body)) - device_id = id_for(body, device_name, r"/clients/(\d+)") - check("Kennung des Geraets gefunden", bool(device_id), "nicht gefunden") - - status, body, _ = client.get("/clients/") - check( - "Geraet in der Liste", - device_name in body and MAC.lower() in body, - "nicht gefunden", - ) - - status, body, _ = client.get("/") - check("Geraet auf der Uebersicht", device_name in body, "nicht gefunden") - - print("\nValidierung:") - token = client.token("/clients/") - status, body = client.post( - "/clients/neu", - {"csrf_token": token, "name": "Kaputt", "mac": "unsinn", "ip": f"{SUBNET}.51"}, - ) - check("Ungueltige MAC abgelehnt", "hexadezimale" in body, flash(body)) - - token = client.token("/clients/") - status, body = client.post( - "/clients/neu", - {"csrf_token": token, "name": "Kaputt", "mac": MAC[:-2] + "FE", "ip": "999.1.1.1"}, - ) - check("Ungueltige IP abgelehnt", "gueltige IP-Adresse" in body, flash(body)) - - token = client.token("/clients/") - status, body = client.post( - "/clients/neu", - {"csrf_token": token, "name": "Doppelt", "mac": MAC, "ip": f"{SUBNET}.52"}, - ) - check("Doppelte MAC abgelehnt", "angelegt" not in body, "zweimal dieselbe MAC") - - # -- Host-Schluessel eines Clients --------------------------------------- - # Gegen einen echten SSH-Dienst: der Host selbst. Nur so ist geprueft, dass - # der gelesene Fingerprint tatsaechlich der des Gegenuebers ist -- eine - # Attrappe wuerde genau den Fehler durchlassen, auf den es hier ankommt. - print("\nSSH-Host-Schluessel eines Clients:") - token = client.token("/clients/") - ssh_name = f"E2E-SSH-{RUN}" - status, body = client.post( - "/clients/neu", - { - "csrf_token": token, - "name": ssh_name, - "mac": MAC[:-2] + "0F", - "ip": "127.0.0.1", - "ssh_port": "22", - "credential_id": credential_id, - "is_active": "0", - }, - ) - check("Client fuer den SSH-Test angelegt", "angelegt" in body, flash(body)) - ssh_id = id_for(body, ssh_name, r"/clients/(\d+)") - check("Kennung gefunden", bool(ssh_id), "nicht gefunden") - - if ssh_id: - status, body, _ = client.get(f"/clients/{ssh_id}") - check("Detailseite zeigt die Schluesselkarte", "SSH-Host-Schluessel" in body, "fehlt") - check("Zustand offen", "Noch kein Schluessel hinterlegt" in body, "unerwartet") - - token = client.token(f"/clients/{ssh_id}") - status, body = client.post( - f"/clients/{ssh_id}/hostkey/pruefen", {"csrf_token": token} - ) - check("Schluessel ausgelesen", "Host-Schluessel gelesen" in body, flash(body)) - - match = re.search(r"(SHA256:[A-Za-z0-9+/]{43})", body) - fingerprint = match.group(1) if match else "" - check("Fingerprint erhalten", bool(fingerprint), "nicht gefunden") - - expected = local_host_fingerprints() - check( - "Fingerprint stimmt mit ssh-keyscan ueberein", - bool(expected) and fingerprint in expected, - f"gelesen {fingerprint}, erwartet eines von {sorted(expected)}", - ) - - key_match = re.search(r'name="key_type" value="([^"]+)"', body) - key_type = key_match.group(1) if key_match else "" - token = client.token(f"/clients/{ssh_id}") - status, body = client.post( - f"/clients/{ssh_id}/hostkey/freigeben", - {"csrf_token": token, "key_type": key_type, "fingerprint": fingerprint}, - ) - check("Schluessel freigegeben", "freigegeben" in body, flash(body)) - check("Fingerprint steht auf der Seite", fingerprint in body, "nicht gefunden") - - token = client.token(f"/clients/{ssh_id}") - status, body = client.post( - f"/clients/{ssh_id}/hostkey/freigeben", - {"csrf_token": token, "key_type": key_type, "fingerprint": "SHA256:zu-kurz"}, - ) - check("Unsinniger Fingerprint abgelehnt", "kein gueltiger Fingerprint" in body, flash(body)) - - status, body, _ = client.get("/protokolle/aenderungen") - check("Freigabe protokolliert", "device.host_key_trusted" in body, "kein Eintrag") - - token = client.token(f"/clients/{ssh_id}") - status, body = client.post( - f"/clients/{ssh_id}/hostkey/verwerfen", {"csrf_token": token} - ) - check("Schluessel verworfen", "entfernt" in body, flash(body)) - - print("\nNeustart-Protokoll:") - status, body, _ = client.get("/protokolle/neustarts") - check("Seite heisst Neustarts", status == 200 and "Neustarts" in body, f"HTTP {status}") - check("Alte PoE-Adresse ist weg", client.get_raw("/protokolle/poe") == 404, "noch da") - check("Filter nach Methode vorhanden", "methode=ssh" in body, "kein Filter") - - print("\nGeheimnisse:") - token = client.token("/zugangsdaten/") - status, body = client.post( - f"/zugangsdaten/{credential_id}/anzeigen", {"csrf_token": token} - ) - check("Klartextanzeige moeglich (frisch angemeldet)", secret in body, f"HTTP {status}") - status, body, _ = client.get("/protokolle/aenderungen") - check("Zugriff protokolliert", "credential.secret_revealed" in body, "kein Eintrag") - - print("\nPapierkorb:") - token = client.token("/clients/") - client.post(f"/clients/{device_id}/loeschen", {"csrf_token": token}) - status, body, _ = client.get("/papierkorb/") - check("Geloeschtes Geraet im Papierkorb", device_name in body, "nicht gefunden") - token = client.token("/papierkorb/") - status, body = client.post( - f"/papierkorb/devices/{device_id}/wiederherstellen", {"csrf_token": token} - ) - if licensed: - check("Wiederherstellen mit Lizenz moeglich", "wiederhergestellt" in body, flash(body)) - else: - check("Wiederherstellen ohne Lizenz gesperrt", "Lizenz" in body, flash(body)) - - print("\nDiagnose:") - status, body, _ = client.get("/diagnose/") - check("Audit-Kette unversehrt", "unversehrt" in body, "Kette gebrochen?") - # Nicht "liegt die Datei da", sondern "antwortet er". Genau der - # Unterschied hat einmal verdeckt, dass jede Systemaktion an - # NoNewPrivileges scheiterte, waehrend die Seite "erreichbar" meldete. - check("Systemhelfer antwortet", "Helfer antwortet" in body, "Helfer meldet sich nicht") - check("Schluesselrechte in Ordnung", "in Ordnung" in body, "Problem gemeldet") - - print("\nAbmeldung:") - token = client.token("/konto/") - status, body = client.post("/logout", {"csrf_token": token}) - status = client.get_raw("/clients/") - check("Nach Abmeldung gesperrt", status in (302, 401), f"HTTP {status}") - - print(f"\n== Ergebnis: {len(PASSED)} bestanden, {len(FAILED)} fehlgeschlagen ==") - for entry in FAILED: - print(f" FEHLER: {entry}") - return 1 if FAILED else 0 - - -if __name__ == "__main__": - if len(sys.argv) != 4: - print(__doc__) - raise SystemExit(2) - raise SystemExit(main(sys.argv[1], sys.argv[2], sys.argv[3])) diff --git a/tests/test_core_security.py b/tests/test_core_security.py deleted file mode 100644 index 1b52c57..0000000 --- a/tests/test_core_security.py +++ /dev/null @@ -1,721 +0,0 @@ -"""Tests der Sicherheitsgrenzen des Kerns. - -Jeder Test hier deckt eine Grenze ab, die im Vorgaengerprojekt entweder fehlte -oder nur im Template durchgesetzt wurde. -""" - -from __future__ import annotations - -import json -import sqlite3 -from datetime import datetime, timedelta, timezone - -import pytest - -from tesm_core import audit -from tesm_core.db import Database, NestedConnectionError -from tesm_core.extension import core -from tesm_core.keystore import DecryptionError, Keystore -from tesm_core.migrations import split_statements -from tesm_core.rbac import service as rbac -from tesm_core.rbac.model import Action, Area, PermissionTree, Resource, RolePreset -from tesm_core.security import passwords, totp - - -# --------------------------------------------------------------------------- # -# Rechtebaum -# --------------------------------------------------------------------------- # - - -def _tree() -> PermissionTree: - return PermissionTree( - [ - Area( - key="ops", - label="Betrieb", - icon="cpu", - resources=( - Resource( - "devices", - "Geraete", - (Action.VIEW, Action.CREATE, Action.EDIT, Action.DELETE, Action.EXECUTE), - ), - Resource("switches", "Switche", (Action.VIEW, Action.EDIT)), - ), - ), - Area( - key="admin", - label="Verwaltung", - icon="sliders", - resources=(Resource("users", "Benutzer", (Action.VIEW, Action.CREATE)),), - ), - ] - ) - - -def test_area_columns_only_contain_used_actions(): - tree = _tree() - ops = tree.area("ops") - admin = tree.area("admin") - assert Action.EXECUTE in ops.columns() - assert Action.EXECUTE not in admin.columns() - assert Action.DELETE not in admin.columns() - - -def test_sanitize_adds_view_and_area_switch(): - tree = _tree() - result = tree.sanitize(["devices.delete"]) - assert result == ["devices.delete", "devices.view", "ops.view"] - - -def test_sanitize_drops_unknown_permissions(): - tree = _tree() - assert tree.sanitize(["gibtsnicht.view", "devices.view"]) == ["devices.view", "ops.view"] - - -def test_kill_switch_disables_all_children(): - tree = _tree() - granted = {"devices.view", "devices.edit", "switches.view"} - assert tree.effective(granted) == set() # ohne ops.view greift nichts - assert tree.effective(granted | {"ops.view"}) == granted | {"ops.view"} - - -def test_duplicate_resource_keys_rejected(): - with pytest.raises(ValueError): - PermissionTree( - [ - Area("a", "A", "grid", (Resource("x", "X", (Action.VIEW,)),)), - Area("b", "B", "grid", (Resource("x", "X", (Action.VIEW,)),)), - ] - ) - - -def test_resource_without_view_rejected(): - with pytest.raises(ValueError): - PermissionTree([Area("a", "A", "grid", (Resource("x", "X", (Action.EDIT,)),))]) - - -def test_preset_expansion(): - tree = _tree() - preset = RolePreset( - key="ops-read", - name="Betrieb lesen", - description="", - grants=(("devices", (Action.VIEW,)), ("switches", "*")), - ) - assert tree.expand_preset(preset) == [ - "devices.view", - "ops.view", - "switches.edit", - "switches.view", - ] - - -# --------------------------------------------------------------------------- # -# Eskalationsschutz -# --------------------------------------------------------------------------- # - - -class _Actor: - def __init__(self, is_admin: bool, permissions: set[str]): - self.is_admin = is_admin - self.id = 42 - self._permissions = permissions - - def has(self, *permissions: str) -> bool: - return self.is_admin or all(p in self._permissions for p in permissions) - - -def test_no_amplification_blocks_unknown_permission(): - tree = _tree() - actor = _Actor(False, {"ops.view", "devices.view"}) - with pytest.raises(rbac.PermissionDenied) as exc: - rbac.guard_no_amplification(actor, tree, ["devices.delete"]) - assert "nicht besitzen" in str(exc.value) - - -def test_no_amplification_allows_own_permissions(): - tree = _tree() - actor = _Actor(False, {"ops.view", "devices.view"}) - rbac.guard_no_amplification(actor, tree, ["devices.view", "ops.view"]) - - -def test_admin_may_grant_anything(): - tree = _tree() - rbac.guard_no_amplification(_Actor(True, set()), tree, ["devices.delete"]) - - -def test_group_editor_cannot_escalate_via_new_group(core_app, make_user): - """Der zentrale Fall: ein Gruppenverwalter darf sich nicht selbst hoeherstufen.""" - from tesm_core.auth.service import load_user - - extension = core(core_app) - row = make_user("gruppenchef", "Ein-gutes-Passwort-1", permissions=("groups.create", "groups.view")) - with core_app.app_context(), extension.database.session() as conn: - actor = load_user(conn, int(row["id"])) - assert actor is not None - with extension.database.transaction(conn): - with pytest.raises(rbac.PermissionDenied): - rbac.create_group( - conn, - name="Hintertuer", - description="", - permissions=["users.create", "administration.view"], - tree=extension.permissions, - actor=actor, - ) - - -def test_last_admin_cannot_be_demoted(core_app, make_user): - from tesm_core.auth.service import load_user - - extension = core(core_app) - admin_row = make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - with core_app.app_context(), extension.database.session() as conn: - actor = load_user(conn, int(admin_row["id"])) - second = conn.execute( - "INSERT INTO users (username, password_hash, is_admin, auth_source, created_at, updated_at) " - "VALUES ('zweiter','x',1,'local',datetime('now'),datetime('now'))" - ) - second_id = int(second.lastrowid or 0) - with extension.database.transaction(conn): - # Solange zwei Admins existieren, ist das Herabstufen erlaubt. - rbac.update_user( - conn, second_id, actor=actor, tree=extension.permissions, is_admin=False - ) - with extension.database.transaction(conn): - with pytest.raises(rbac.PermissionDenied): - rbac.guard_last_admin(conn, int(admin_row["id"]), action="Herabstufen") - - -def test_cannot_change_own_admin_flag(core_app, make_user): - from tesm_core.auth.service import load_user - - extension = core(core_app) - row = make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - with core_app.app_context(), extension.database.session() as conn: - actor = load_user(conn, int(row["id"])) - with extension.database.transaction(conn): - with pytest.raises(rbac.PermissionDenied): - rbac.update_user( - conn, int(row["id"]), actor=actor, tree=extension.permissions, is_admin=False - ) - - -def test_non_admin_cannot_touch_admin_account(core_app, make_user): - from tesm_core.auth.service import load_user - - extension = core(core_app) - admin_row = make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - editor_row = make_user("helfer", "Ein-gutes-Passwort-1", permissions=("users.edit", "users.view")) - with core_app.app_context(), extension.database.session() as conn: - actor = load_user(conn, int(editor_row["id"])) - with extension.database.transaction(conn): - with pytest.raises(rbac.PermissionDenied): - rbac.update_user( - conn, - int(admin_row["id"]), - actor=actor, - tree=extension.permissions, - is_locked=True, - ) - - -def test_system_group_permissions_locked(core_app, make_user): - from tesm_core.auth.service import load_user - - extension = core(core_app) - row = make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - with core_app.app_context(), extension.database.session() as conn: - actor = load_user(conn, int(row["id"])) - default = next(g for g in rbac.list_groups(conn) if g.is_default) - with extension.database.transaction(conn): - with pytest.raises(rbac.PermissionDenied): - rbac.update_group( - conn, - default.id, - permissions=[], - tree=extension.permissions, - actor=actor, - ) - # Mit ausdruecklicher Freischaltung geht es. - rbac.update_group( - conn, - default.id, - permissions=[], - tree=extension.permissions, - actor=actor, - unlock_system=True, - ) - - -def test_presets_are_not_reset_on_restart(core_app): - """Anders als im Vorgaenger bleiben Anpassungen an Systemgruppen erhalten.""" - extension = core(core_app) - with extension.database.session() as conn: - default = next(g for g in rbac.list_groups(conn) if g.is_default) - with extension.database.transaction(conn): - conn.execute("DELETE FROM group_permissions WHERE group_id=?", (default.id,)) - created = rbac.ensure_presets(conn, extension.permissions, extension.role_presets) - assert created == [] - assert rbac.group_permissions(conn, default.id) == [] - - -# --------------------------------------------------------------------------- # -# Datenbank -# --------------------------------------------------------------------------- # - - -def test_second_connection_during_write_transaction_raises(tmp_path): - database = Database(tmp_path / "test.db") - with database.session() as conn: - conn.execute("CREATE TABLE t (a INTEGER)") - with database.transaction(conn): - conn.execute("INSERT INTO t VALUES (1)") - with pytest.raises(NestedConnectionError) as exc: - database.connect() - assert "Schreibtransaktion" in str(exc.value) - - -def test_nested_transactions_commit_once(tmp_path): - database = Database(tmp_path / "test.db") - with database.session() as conn: - conn.execute("CREATE TABLE t (a INTEGER)") - with database.transaction(conn): - conn.execute("INSERT INTO t VALUES (1)") - with database.transaction(conn): - conn.execute("INSERT INTO t VALUES (2)") - assert Database.value(conn, "SELECT COUNT(*) FROM t") == 2 - - -def test_inner_rollback_keeps_outer(tmp_path): - database = Database(tmp_path / "test.db") - with database.session() as conn: - conn.execute("CREATE TABLE t (a INTEGER)") - with database.transaction(conn): - conn.execute("INSERT INTO t VALUES (1)") - try: - with database.transaction(conn): - conn.execute("INSERT INTO t VALUES (2)") - raise RuntimeError("abbruch") - except RuntimeError: - pass - assert Database.value(conn, "SELECT COUNT(*) FROM t") == 1 - - -def test_foreign_keys_enforced(tmp_path): - database = Database(tmp_path / "test.db") - with database.session() as conn: - conn.execute("CREATE TABLE parent (id INTEGER PRIMARY KEY)") - conn.execute("CREATE TABLE child (p INTEGER REFERENCES parent(id))") - with pytest.raises(sqlite3.IntegrityError): - conn.execute("INSERT INTO child VALUES (99)") - - -def test_wal_mode_active(tmp_path): - database = Database(tmp_path / "test.db") - with database.session() as conn: - assert Database.value(conn, "PRAGMA journal_mode").lower() == "wal" - - -def test_split_statements_handles_comments_and_strings(): - script = """ - -- ein Kommentar mit ; Semikolon - CREATE TABLE t (a TEXT DEFAULT 'x;y'); - INSERT INTO t VALUES ('a;b'); /* Block; Kommentar */ - """ - statements = split_statements(script) - assert len(statements) == 2 - assert "x;y" in statements[0] - assert "a;b" in statements[1] - - -# --------------------------------------------------------------------------- # -# Schluessel und Verschluesselung -# --------------------------------------------------------------------------- # - - -def test_keystore_roundtrip_and_aad_binding(tmp_path): - keystore = Keystore(tmp_path / "data.keys") - token = keystore.encrypt("geheim", aad="credentials.password:1") - assert keystore.decrypt(token, aad="credentials.password:1") == "geheim" - # Dasselbe Chiffrat an anderer Stelle eingespielt -> Fehler. - with pytest.raises(DecryptionError): - keystore.decrypt(token, aad="credentials.password:2") - - -def test_keystore_rotation_keeps_old_values_readable(tmp_path): - keystore = Keystore(tmp_path / "data.keys") - old_token = keystore.encrypt("alt", aad="x") - new_id = keystore.rotate() - assert new_id == "2" - assert keystore.decrypt(old_token, aad="x") == "alt" - assert keystore.needs_reencrypt(old_token) is True - new_token = keystore.encrypt("neu", aad="x") - assert keystore.needs_reencrypt(new_token) is False - - -def test_keystore_file_is_private(tmp_path): - import os - import stat - - path = tmp_path / "data.keys" - Keystore(path) - if os.name == "posix": - mode = path.stat().st_mode - assert not mode & (stat.S_IRGRP | stat.S_IROTH) - - -def test_tampered_ciphertext_rejected(tmp_path): - keystore = Keystore(tmp_path / "data.keys") - token = keystore.encrypt("geheim", aad="x") - prefix, key_id, nonce, ciphertext = token.split(".", 3) - broken = f"{prefix}.{key_id}.{nonce}.{ciphertext[:-4]}AAAA" - with pytest.raises(DecryptionError): - keystore.decrypt(broken, aad="x") - - -# --------------------------------------------------------------------------- # -# Passwoerter und TOTP -# --------------------------------------------------------------------------- # - - -def test_argon2_hash_and_verify(): - stored = passwords.hash_password("Ein-gutes-Passwort-1") - assert stored.startswith("$argon2id$") - assert passwords.verify_password(stored, "Ein-gutes-Passwort-1") is True - assert passwords.verify_password(stored, "falsch") is False - assert passwords.needs_rehash(stored) is False - - -def test_bcrypt_legacy_hash_accepted_and_flagged_for_rehash(): - bcrypt = pytest.importorskip("bcrypt") - legacy = bcrypt.hashpw(b"Altes-Passwort-1", bcrypt.gensalt(rounds=4)).decode() - assert passwords.is_legacy_hash(legacy) is True - assert passwords.verify_password(legacy, "Altes-Passwort-1") is True - assert passwords.needs_rehash(legacy) is True - - -def test_password_normalisation_across_platforms(): - import unicodedata - - nfd = unicodedata.normalize("NFD", "Grüße-aus-München-1") - nfc = unicodedata.normalize("NFC", "Grüße-aus-München-1") - assert nfd != nfc - stored = passwords.hash_password(nfd) - assert passwords.verify_password(stored, nfc) is True - - -def test_password_policy_rules(): - policy = passwords.PasswordPolicy(min_length=12) - assert policy.check("Ein-gutes-Passwort-1") == [] - assert any("Zeichen erforderlich" in p for p in policy.check("kurz")) - assert any("Zeichenarten" in p for p in policy.check("aaaaaaaaaaaaaaa")) - assert any("Benutzernamen" in p for p in policy.check("Tester-Passwort-1", username="tester")) - assert any("haeufig" in p for p in policy.check("passwort1234")) - - -def test_totp_generates_and_verifies(): - secret = totp.generate_secret() - code = totp.current_code(secret) - assert totp.verify_code(secret, code) is True - assert totp.verify_code(secret, "000000") is False - assert totp.verify_code(secret, code, now=0) is False - - -def test_totp_drift_tolerance(): - secret = totp.generate_secret() - now = 1_700_000_000 - previous = totp.code_at(secret, int(now // totp.PERIOD) - 1) - assert totp.verify_code(secret, previous, now=now) is True - far = totp.code_at(secret, int(now // totp.PERIOD) - 5) - assert totp.verify_code(secret, far, now=now) is False - - -def test_totp_uri_contains_issuer_and_secret(): - secret = totp.generate_secret() - uri = totp.provisioning_uri(secret, account="anna", issuer="TESM Test") - assert uri.startswith("otpauth://totp/") - assert secret in uri - assert "issuer=TESM%20Test" in uri - - -# --------------------------------------------------------------------------- # -# Aenderungsprotokoll -# --------------------------------------------------------------------------- # - - -def test_audit_chain_detects_modification(core_app): - extension = core(core_app) - with extension.database.session() as conn: - with extension.database.transaction(conn): - for index in range(5): - audit.log(conn, actor="tester", action="test.event", target=f"ziel-{index}") - assert audit.verify_chain(conn)["ok"] is True - - with extension.database.transaction(conn): - conn.execute("UPDATE audit_log SET detail='manipuliert' WHERE id=3") - result = audit.verify_chain(conn) - assert result["ok"] is False - assert result["broken_at"] == 3 - assert "Hash" in result["reason"] - - -def test_audit_chain_detects_deletion(core_app): - extension = core(core_app) - with extension.database.session() as conn: - with extension.database.transaction(conn): - for index in range(5): - audit.log(conn, actor="tester", action="test.event", target=str(index)) - with extension.database.transaction(conn): - conn.execute("DELETE FROM audit_log WHERE id=3") - result = audit.verify_chain(conn) - assert result["ok"] is False - - -def test_audit_archiving_moves_full_days(core_app, tmp_path): - extension = core(core_app) - old_day = (datetime.now(timezone.utc) - timedelta(days=3)).replace(microsecond=0) - with extension.database.session() as conn: - with extension.database.transaction(conn): - for index in range(40): - audit.log( - conn, - actor="tester", - action="test.old", - target=str(index), - ts=old_day + timedelta(seconds=index), - ) - for index in range(5): - audit.log(conn, actor="tester", action="test.new", target=str(index)) - with extension.database.transaction(conn): - written = audit.archive_old_rows(conn, tmp_path / "archiv", threshold=10, target=5) - assert written - # Uebrig bleiben ausschliesslich Zeilen des laufenden Tages (inkl. der - # Startzeile, die die App beim Migrieren selbst geschrieben hat). - today = datetime.now(timezone.utc).date().isoformat() - remaining = Database.all(conn, "SELECT DISTINCT substr(ts,1,10) AS day FROM audit_log") - assert [row["day"] for row in remaining] == [today] - lines = (tmp_path / "archiv" / written[0].name).read_text(encoding="utf-8").splitlines() - assert len(lines) == 40 - assert json.loads(lines[0])["action"] == "test.old" - - -# --------------------------------------------------------------------------- # -# Sitzungen -# --------------------------------------------------------------------------- # - - -def test_session_id_rotates_on_login(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - client.get("/login") - before = client.get_cookie("tesm_session") - login("anna", "Ein-gutes-Passwort-1") - after = client.get_cookie("tesm_session") - assert after is not None - if before is not None: - assert before.value != after.value - - -def test_session_revocation_takes_effect_immediately(client, core_app, make_user, login): - from tesm_core.security import sessions as session_store - - row = make_user("anna", "Ein-gutes-Passwort-1") - login("anna", "Ein-gutes-Passwort-1") - assert client.get("/konto/").status_code == 200 - - extension = core(core_app) - with extension.database.session() as conn, extension.database.transaction(conn): - session_store.revoke_all_for_user(conn, int(row["id"]), reason="test") - - response = client.get("/konto/") - assert response.status_code == 302 - assert "/login" in response.headers["Location"] - - -def test_password_change_revokes_other_sessions(core_app, make_user): - from tesm_core.security import sessions as session_store - - extension = core(core_app) - row = make_user("anna", "Ein-gutes-Passwort-1") - first = core_app.test_client() - second = core_app.test_client() - for client_instance in (first, second): - page = client_instance.get("/login") - body = page.get_data(as_text=True) - marker = 'name="csrf_token" value="' - start = body.index(marker) + len(marker) - token = body[start : body.index('"', start)] - client_instance.post( - "/login", - data={"username": "anna", "password": "Ein-gutes-Passwort-1", "csrf_token": token}, - follow_redirects=True, - ) - with extension.database.session() as conn: - assert len(session_store.active_for_user(conn, int(row["id"]))) == 2 - - page = first.get("/konto/sicherheit") - body = page.get_data(as_text=True) - marker = 'name="csrf_token" value="' - start = body.index(marker) + len(marker) - token = body[start : body.index('"', start)] - first.post( - "/konto/passwort", - data={ - "current_password": "Ein-gutes-Passwort-1", - "new_password": "Neues-Passwort-2026", - "repeat_password": "Neues-Passwort-2026", - "csrf_token": token, - }, - follow_redirects=True, - ) - assert second.get("/konto/").status_code == 302 - assert first.get("/konto/").status_code == 200 - - -def test_reauth_required_for_sensitive_action(client, make_user, login, csrf_token): - make_user("anna", "Ein-gutes-Passwort-1") - login("anna", "Ein-gutes-Passwort-1") - - # Frisch angemeldet gilt die Bestaetigung -- danach kuenstlich altern lassen. - from flask import session as flask_session - - with client.session_transaction() as sess: - sess["_reauth_at"] = (datetime.now(timezone.utc) - timedelta(hours=2)).isoformat( - timespec="seconds" - ) - token = csrf_token("/konto/sicherheit") - response = client.post("/konto/zwei-faktor/starten", data={"csrf_token": token}) - assert response.status_code == 302 - assert "/bestaetigen" in response.headers["Location"] - assert flask_session is not None - - -# --------------------------------------------------------------------------- # -# Der Rechteweg muss tatsaechlich begehbar sein -# --------------------------------------------------------------------------- # - - -def test_units_do_not_disable_the_privilege_path(): - """``NoNewPrivileges`` schaltet setuid ab -- und damit sudo, und damit den Helfer. - - Auf beiden Testhosts stand "Helfer erreichbar", waehrend in Wahrheit jede - Systemaktion an ``sudo: the "no new privileges" flag is set`` scheiterte: - nginx uebernehmen, Netzwerk aendern, Zertifikat anfordern, Kea schreiben. - Die Haertung hatte genau den Mechanismus abgeschaltet, den sie schuetzen - sollte. - """ - from pathlib import Path - - root = Path(__file__).resolve().parent.parent / "deploy" / "systemd" - units = list(root.glob("*.service")) - assert units - for unit in units: - text = unit.read_text(encoding="utf-8") - assert "NoNewPrivileges=yes" not in text, ( - f"{unit.name}: verhindert sudo und damit jede Systemaktion." - ) - # Gemessen: auch ein eingeschraenktes Bounding Set bricht sudo mit - # "unable to change to root gid" -- inklusive CapabilityBoundingSet=CAP_NET_RAW. - for line in text.splitlines(): - if line.startswith("CapabilityBoundingSet="): - raise AssertionError( - f"{unit.name}: {line!r} nimmt sudo CAP_SETUID/CAP_SETGID." - ) - # Die eigentliche Grenze bleibt bestehen. - # "strict" sperrt auch den Helfer aus /etc aus -- gemessen. - assert "ProtectSystem=full" in text, unit.name - assert "ProtectSystem=strict" not in text, ( - f"{unit.name}: strict sperrt den Helfer aus /etc aus." - ) - assert "PrivateTmp=yes" in text, unit.name - assert "User=root" not in text, unit.name - - -def test_the_helper_is_probed_not_just_looked_for(): - """"Datei liegt da" ist keine Auskunft darueber, ob der Aufruf gelingt.""" - import inspect - - from tesm_core.sysops import SysOps - - assert hasattr(SysOps, "selftest") - source = inspect.getsource(SysOps.selftest) - assert 'self.run("service-status", "nginx")' in source - assert 'self.run("nginx-test")' not in source, ( - "Ein fehlgeschlagenes nginx -t heisst 'Konfiguration kaputt', nicht 'Helfer kaputt'." - ) - # Die beiden Fehlerbilder, die im Betrieb wirklich vorkommen, werden benannt. - assert "no new privileges" in source - assert "a password is required" in source - - -def test_selftest_explains_a_blocked_sudo(monkeypatch): - from tesm_core.sysops import Result, SysOps - - class Blocked(SysOps): - @property - def available(self) -> bool: - return True - - def run(self, verb, *args, stdin="", timeout=None): - return Result( - False, "", 'sudo: The "no new privileges" flag is set, which prevents sudo', 1 - ) - - result = Blocked(app_key="tesm").selftest() - assert not result.ok - assert "NoNewPrivileges" in result.message - assert "systemctl" in result.message - - -def test_selftest_reports_a_missing_sudoers_rule(): - from tesm_core.sysops import Result, SysOps - - class NeedsPassword(SysOps): - @property - def available(self) -> bool: - return True - - def run(self, verb, *args, stdin="", timeout=None): - return Result(False, "", "sudo: a password is required", 1) - - result = NeedsPassword(app_key="tesm").selftest() - assert not result.ok - assert "sudoers" in result.message - - -def test_selftest_says_so_when_the_helper_is_absent(tmp_path): - from tesm_core.sysops import SysOps - - result = SysOps(app_key="tesm", helper=tmp_path / "gibt-es-nicht").selftest() - assert not result.ok - assert "fehlt" in result.message - - -def test_authenticate_keeps_directory_groups(core_app, monkeypatch): - """Die Verzeichnisgruppen duerfen auf dem Weg nach oben nicht verlorengehen. - - ``_verify_credentials`` hat sie korrekt geliefert, ``authenticate`` hat aus - dem Ergebnis aber ein neues Objekt gebaut und das Feld dabei fallen lassen. - Sichtbar war davon nichts: die Anmeldung gelang, nur band sich keine - Freigabe mehr ein. Genau solche stillen Verluste faengt dieser Test. - """ - from tesm_core.auth import directory, service - from tesm_core.extension import core - - profil = directory.DirectoryProfile( - username="mitarbeiter", - dn="CN=mitarbeiter,OU=Benutzer,DC=firma,DC=local", - group_dns=("CN=GG_Technik,OU=Gruppen,DC=firma,DC=local",), - ) - monkeypatch.setattr(directory, "is_enabled", lambda conn: True) - monkeypatch.setattr( - directory, "authenticate", lambda conn, name, pw: directory.DirectoryResult("ok", profile=profil) - ) - - with core_app.test_request_context("/login"): - with core(core_app).database.session() as conn: - outcome = service.authenticate(conn, "mitarbeiter", "egal", ip="127.0.0.1") - - assert outcome.ok, outcome.message - assert outcome.group_dns == profil.group_dns, "die Gruppen sind unterwegs verlorengegangen" diff --git a/tests/test_core_smoke.py b/tests/test_core_smoke.py deleted file mode 100644 index 8f093b9..0000000 --- a/tests/test_core_smoke.py +++ /dev/null @@ -1,246 +0,0 @@ -"""Rauchtest des Grundgeruests: Start, Anmeldung, Rechte, CSRF, Header.""" - -from __future__ import annotations - -import re - - -def test_app_starts_and_health_ok(client): - response = client.get("/gesundheit") - assert response.status_code == 200 - assert response.get_json()["status"] == "ok" - - -def test_login_page_renders_and_sets_csrf(client): - response = client.get("/login") - assert response.status_code == 200 - body = response.get_data(as_text=True) - assert "Anmelden" in body - assert 'name="csrf_token"' in body - - -def test_security_headers_present(client): - response = client.get("/login") - csp = response.headers["Content-Security-Policy"] - assert "default-src 'self'" in csp - assert "'unsafe-inline'" not in csp.split("script-src")[1].split(";")[0] - assert re.search(r"nonce-[A-Za-z0-9_-]{10,}", csp) - assert response.headers["X-Frame-Options"] == "DENY" - assert response.headers["X-Content-Type-Options"] == "nosniff" - assert "camera=()" in response.headers["Permissions-Policy"] - - -def test_login_and_logout(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - response = login("anna", "Ein-gutes-Passwort-1") - assert response.status_code == 200 - assert "Willkommen" in response.get_data(as_text=True) - - page = client.get("/konto/") - assert page.status_code == 200 - assert "anna" in page.get_data(as_text=True) - - -def test_wrong_password_is_generic(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - response = login("anna", "falsch") - body = response.get_data(as_text=True) - assert "Benutzername oder Passwort ist falsch" in body - # Kein Hinweis darauf, dass das Konto existiert. - assert "gesperrt" not in body - - -def test_unknown_user_same_message(client, login): - body = login("gibtsnicht", "irgendwas").get_data(as_text=True) - assert "Benutzername oder Passwort ist falsch" in body - - -def test_locked_account_rejected(client, make_user, login): - make_user("gesperrt", "Ein-gutes-Passwort-1", is_locked=True) - body = login("gesperrt", "Ein-gutes-Passwort-1").get_data(as_text=True) - assert "gesperrt" in body - - -def test_rate_limit_blocks_after_many_attempts(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - messages = [login("anna", "falsch").get_data(as_text=True) for _ in range(9)] - assert any("voruebergehend gesperrt" in body for body in messages) - - -def test_protected_page_redirects_to_login(client): - response = client.get("/widgets") - assert response.status_code == 302 - assert "/login" in response.headers["Location"] - - -def test_permission_required_blocks_without_grant(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - login("anna", "Ein-gutes-Passwort-1") - assert client.get("/verwaltung/benutzer").status_code == 403 - - -def test_permission_granted_allows_access(client, make_user, login): - make_user("bob", "Ein-gutes-Passwort-1", permissions=("users.view",)) - login("bob", "Ein-gutes-Passwort-1") - response = client.get("/verwaltung/benutzer") - assert response.status_code == 200 - assert "Benutzer" in response.get_data(as_text=True) - - -def test_admin_sees_everything(client, make_user, login): - make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - login("root", "Ein-gutes-Passwort-1") - for path in ("/verwaltung/benutzer", "/verwaltung/gruppen", "/papierkorb/", "/diagnose/"): - assert client.get(path).status_code == 200, path - - -def test_csrf_missing_token_is_rejected(client, make_user, login): - make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - login("root", "Ein-gutes-Passwort-1") - response = client.post("/verwaltung/benutzer/neu", data={"username": "boese"}) - assert response.status_code == 403 - assert "Sicherheitstoken" in response.get_data(as_text=True) - - -def test_csrf_wrong_origin_is_rejected(client, make_user, login, csrf_token): - make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - login("root", "Ein-gutes-Passwort-1") - token = csrf_token("/verwaltung/benutzer") - response = client.post( - "/verwaltung/benutzer/neu", - data={"username": "boese", "csrf_token": token}, - headers={"Origin": "https://angreifer.example"}, - ) - assert response.status_code == 403 - - -def test_nav_hides_pages_without_permission(client, make_user, login): - make_user("anna", "Ein-gutes-Passwort-1") - login("anna", "Ein-gutes-Passwort-1") - body = client.get("/").get_data(as_text=True) - assert "Diagnose" not in body - assert "Widgets" not in body - - -def test_nav_shows_pages_with_permission(client, make_user, login): - make_user("bob", "Ein-gutes-Passwort-1", permissions=("widgets.view",)) - login("bob", "Ein-gutes-Passwort-1") - body = client.get("/").get_data(as_text=True) - assert "Widgets" in body - - -def test_default_group_created_from_preset(core_app): - from tesm_core.extension import core - from tesm_core.rbac import service as rbac - - extension = core(core_app) - with extension.database.session() as conn: - groups = rbac.list_groups(conn) - names = {group.name for group in groups} - assert "Benutzer" in names - default = next(group for group in groups if group.name == "Benutzer") - assert default.is_default is True - assert "widgets.view" in default.permissions - assert "demo.view" in default.permissions # Bereichsschalter wird mitgesetzt - - -def test_error_page_shows_request_id(client): - response = client.get("/gibt-es-nicht") - assert response.status_code == 404 - body = response.get_data(as_text=True) - assert "404" in body - assert "Vorgangsnummer" in body - - -# --------------------------------------------------------------------------- # -# Verzeichnisgruppen: suchen statt abtippen -# --------------------------------------------------------------------------- # - - -def test_group_search_is_open_to_fileshare_editors(): - """Wer Freigaben zuordnet, muss die Gruppen finden koennen. - - Sonst braeuchte er zusaetzlich das Recht, den Verzeichnisdienst zu - konfigurieren -- eine Rechteausweitung fuer eine reine Lesesuche. - """ - import inspect - - from tesm_core.views import hostsettings - - source = inspect.getsource(hostsettings.directory_group_search) - assert "fileshare.edit" in source - assert 'mode="any"' in source or "mode='any'" in source - - -def test_the_picker_never_forces_typing_a_dn(): - """Ein abgetippter DN ist ein Tippfehler, der erst bei der Anmeldung auffaellt.""" - from pathlib import Path - - root = Path(__file__).resolve().parent.parent - picker = ( - root / "packages" / "tesm-core" / "src" / "tesm_core" / "templates" - / "tesm_core" / "_group_picker.html" - ).read_text(encoding="utf-8") - assert "directory_group_search" in picker - assert "data-group-query" in picker - # Tippen bleibt trotzdem moeglich: ohne Dienstkonto gibt es keine Suche. - assert 'name="{{ dn_field }}"' in picker - - for template in ( - root / "packages" / "tesm-core" / "src" / "tesm_core" / "templates" - / "tesm_core" / "settings_directory.html", - root / "apps" / "tesm" / "src" / "tesm" / "templates" / "tesm" / "fileshare_mappings.html", - ): - text = template.read_text(encoding="utf-8") - assert "group_picker(" in text, template.name - - -def test_transport_choice_carries_its_port(): - """Umgestellter Transport mit altem Port ergibt einen Fehler ohne Aussage.""" - from pathlib import Path - - root = Path(__file__).resolve().parent.parent - page = ( - root / "packages" / "tesm-core" / "src" / "tesm_core" / "templates" - / "tesm_core" / "settings_directory.html" - ).read_text(encoding="utf-8") - assert 'data-behavior="transport-port"' in page - assert 'data-port-target="#ldap-port"' in page - - script = ( - root / "packages" / "tesm-core" / "src" / "tesm_core" / "static" - / "tesm_core" / "js" / "app.js" - ).read_text(encoding="utf-8") - assert "transport-port" in script - assert 'ldaps: "636"' in script - assert 'starttls: "389"' in script - # Ein absichtlich abweichender Port darf nicht ueberschrieben werden. - assert "known.includes(port.value)" in script - - -def test_permission_matrix_has_no_nameless_checkbox(client, make_user, login): - """In der Rechtematrix darf kein Kaestchen ohne ``name`` stehen. - - Fuer nicht vorgesehene Aktionen stand dort ein deaktiviertes Kaestchen. - Das JS hat beim Einschalten des Bereichs *alle* Kaestchen der Zeile - freigegeben -- auch dieses. Es liess sich anhaken, uebertrug wegen des - fehlenden ``name`` aber nichts: es sah erteilt aus und war nach dem - Speichern verschwunden. Ohne Eingabefeld kann das nicht wieder passieren. - """ - import re - - make_user("root", "Ein-gutes-Passwort-1", is_admin=True) - login("root", "Ein-gutes-Passwort-1") - body = client.get("/verwaltung/gruppen").get_data(as_text=True) - if "permission-matrix" not in body: - gruppe = re.search(r'href="(/verwaltung/gruppen/\d+)"', body) - assert gruppe, "keine Gruppe zum Pruefen gefunden" - body = client.get(gruppe.group(1)).get_data(as_text=True) - - assert "permission-matrix" in body, "Rechtematrix nicht gefunden" - kaestchen = re.findall(r"]*type=\"checkbox\"[^>]*>", body) - assert kaestchen, "keine Kaestchen in der Matrix" - ohne_namen = [k for k in kaestchen if "name=" not in k] - assert not ohne_namen, f"Kaestchen ohne name: {ohne_namen[:3]}" - # Der Platzhalter ist jetzt reine Darstellung. - assert "matrix__na" in body diff --git a/tests/test_legacy_transfer.py b/tests/test_legacy_transfer.py deleted file mode 100644 index 866bc3f..0000000 --- a/tests/test_legacy_transfer.py +++ /dev/null @@ -1,279 +0,0 @@ -"""Der Weg vom alten TESM in dieses: Uebersetzer und Import zusammen. - -Die Testdatei wird **genau so** gebaut, wie der Vorgaenger sie schreibt -(PBKDF2-HMAC-SHA256, 390000 Runden, Fernet, derselbe Umschlag). Damit prueft -der Test das echte Format und nicht meine Vorstellung davon. -""" - -from __future__ import annotations - -import base64 -import json -from typing import Any - -import pytest - -pytest.importorskip("cryptography") - -LEGACY_PASS = "alte-passphrase-2026" -NEW_PASS = "neue-passphrase-2026" - - -@pytest.fixture() -def tesm_app(instance_env: Any): - from tesm import create_app - - return create_app(TESTING=True) - - -def _seal_legacy(payload: dict[str, Any], passphrase: str) -> str: - """Baut einen Umschlag wie ``export_data()`` im alten TESM.""" - import secrets - - from cryptography.fernet import Fernet - from cryptography.hazmat.primitives import hashes - from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - - salt = secrets.token_bytes(16) - kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=390000) - key = base64.urlsafe_b64encode(kdf.derive(passphrase.encode("utf-8"))) - token = Fernet(key).encrypt(json.dumps(payload).encode("utf-8")) - return json.dumps( - { - "app": "tesm", - "version": 2, - "exported_at": "2026-09-02 12:00:00", - "salt": base64.urlsafe_b64encode(salt).decode("utf-8"), - "payload": token.decode("utf-8"), - } - ) - - -#: Ein Ausschnitt, der die Eigenheiten des alten Formats abdeckt: Switch- und -#: Zugangsdatenbezug per Name, geschachtelte DHCP-Optionswerte, "name" statt -#: "hostname" an der Reservierung, sowie Abschnitte, die nicht uebersetzt -#: werden koennen. -LEGACY_PAYLOAD: dict[str, Any] = { - "credentials": [ - {"name": "Switch-Admin", "username": "manager", "password": "geheim-1", "category": "switch"}, - {"name": "Linux-Root", "username": "root", "password": "geheim-2", "category": "linux"}, - {"name": "Windows-Dienst", "username": "svc", "password": "geheim-3", "category": "windows"}, - ], - "switches": [ - {"hostname": "sw-keller", "ip": "192.168.80.2", "ssh_port": 22, "credential_name": "Switch-Admin"}, - ], - "devices": [ - { - "mac": "4c:52:62:25:b9:e4", - "ip": "192.168.80.137", - "port": "12", - "name": "Autodarts", - "switch_hostname": "sw-keller", - "is_active": 1, - "ssh_port": 22, - "credential_name": "Linux-Root", - }, - { - "mac": "aa:bb:cc:00:00:02", - "ip": "192.168.80.50", - "port": "3", - "name": "Kasse", - "switch_hostname": "sw-keller", - "is_active": 1, - "ssh_port": 22, - "credential_name": "Windows-Dienst", - }, - ], - "dhcp": { - "settings": {"dhcp_domain": "firma.local", "dhcp_lease_default": "1200"}, - "subnets": [ - { - "interface": "eth0", - "range_start": "192.168.80.100", - "range_end": "192.168.80.200", - "gateway": "192.168.80.1", - "dns": "192.168.80.1", - "sort_order": 0, - "enabled": 1, - } - ], - "reservations": [{"mac": "aa:bb:cc:00:00:09", "ip": "192.168.80.90", "name": "drucker"}], - "options": [ - { - "code": 225, - "name": "url", - "type": "string", - "description": "Terminal-URL", - "is_standard": 0, - "values": [ - {"device_mac": "4c:52:62:25:b9:e4", "value": "https://play.autodarts.com"}, - {"device_mac": None, "value": "https://intranet.firma.local"}, - ], - }, - # Ohne Wert gibt es nichts zu senden -- wird gemeldet, nicht uebernommen. - {"code": 226, "name": "leer", "type": "string", "description": "", "is_standard": 0, "values": []}, - ], - }, - # Diese Abschnitte kann der Uebersetzer nicht abbilden. - "users": [{"username": "anna", "password_hash": "$2b$12$abcdefghijklmnopqrstuv"}], - "known_hosts": "192.168.80.2 ssh-ed25519 AAAA...\n", -} - - -def test_legacy_envelope_needs_the_right_passphrase(): - from tesm.services.legacy_transfer import LegacyTransferError, open_legacy - - datei = _seal_legacy({"credentials": []}, LEGACY_PASS) - assert open_legacy(datei, LEGACY_PASS) == {"credentials": []} - with pytest.raises(LegacyTransferError, match="Passphrase"): - open_legacy(datei, "falsch") - - -def test_legacy_envelope_of_another_app_is_refused(): - from tesm.services.legacy_transfer import LegacyTransferError, open_legacy - - fremd = json.dumps({"app": "etwas-anderes", "salt": "AAAA", "payload": "x"}) - with pytest.raises(LegacyTransferError, match="nicht zu TESM"): - open_legacy(fremd, LEGACY_PASS) - - -def test_report_names_what_it_cannot_translate(): - """Nicht uebersetzbare Abschnitte muessen benannt werden, nicht verschwinden.""" - from tesm.services.legacy_transfer import convert, open_legacy - - _, bericht = convert(open_legacy(_seal_legacy(LEGACY_PAYLOAD, LEGACY_PASS), LEGACY_PASS)) - text = bericht.as_text() - assert "bcrypt" in text, "der Hinweis zu lokalen Konten fehlt" - assert "Host-Schluessel" in text - assert "ohne Wert" in text, "die wertlose Option wurde nicht gemeldet" - assert bericht.counts["credentials"] == 3 - assert bericht.counts["devices"] == 2 - assert bericht.counts["dhcp-Optionswerte"] == 2 - - -def test_missing_credentials_are_reported(tesm_app): - """Ein zugeordneter, aber nicht exportierter Zugang bleibt beim Import leer.""" - from tesm.services.legacy_transfer import convert, open_legacy - - ohne = {key: value for key, value in LEGACY_PAYLOAD.items() if key != "credentials"} - _, bericht = convert(open_legacy(_seal_legacy(ohne, LEGACY_PASS), LEGACY_PASS)) - hinweise = " ".join(bericht.notes) - assert "Switch-Admin" in hinweise and "Linux-Root" in hinweise - - -def test_full_round_trip_into_the_database(tesm_app): - """Alte Datei -> Uebersetzer -> neues Format -> vorhandener Import.""" - from tesm.services import transfer - from tesm.services.legacy_transfer import convert, open_legacy - from tesm_core.extension import core - - ext = core(tesm_app) - - nutzlast, _ = convert(open_legacy(_seal_legacy(LEGACY_PAYLOAD, LEGACY_PASS), LEGACY_PASS)) - # Ueber Siegel und Entsiegeln, damit auch der neue Umschlag geprueft ist. - datei = transfer.seal(nutzlast, NEW_PASS) - entsiegelt, _kopf = transfer.unseal(datei, NEW_PASS) - - with ext.database.session() as conn, ext.database.transaction(conn): - transfer.apply_import( - conn, - ext.keystore, - entsiegelt, - categories=("credentials", "switches", "devices", "dhcp", "settings"), - set_setting=lambda key, value: ext.settings.set(conn, key, value, actor="test"), - ) - - with ext.database.session() as conn: - # -- Zugangsdaten: Passwort muss wieder lesbar sein ------------------ - from tesm.services import inventory - - zugang = {c.name: c for c in inventory.list_credentials(conn)} - assert set(zugang) == {"Switch-Admin", "Linux-Root", "Windows-Dienst"} - assert zugang["Linux-Root"].category == "linux" - benutzer, passwort = inventory.credential_secret( - conn, ext.keystore, zugang["Linux-Root"].id - ) - assert (benutzer, passwort) == ("root", "geheim-2") - - # -- Switch und Verknuepfung ---------------------------------------- - switch = conn.execute("SELECT * FROM switches WHERE hostname='sw-keller'").fetchone() - assert switch is not None - assert switch["ip"] == "192.168.80.2" - assert switch["credential_id"] == zugang["Switch-Admin"].id - - # -- Clients: Switch, Port, Zugang, abgeleitete Kategorie ------------ - geraet = conn.execute( - "SELECT * FROM devices WHERE mac='4c:52:62:25:b9:e4'" - ).fetchone() - assert geraet["name"] == "Autodarts" - assert geraet["switch_id"] == switch["id"] - assert geraet["port"] == "12" - assert geraet["credential_id"] == zugang["Linux-Root"].id - assert geraet["category"] == "linux", "Kategorie aus den Zugangsdaten abgeleitet" - - windows = conn.execute("SELECT * FROM devices WHERE mac='aa:bb:cc:00:00:02'").fetchone() - assert windows["category"] == "windows" - - # -- DHCP: Subnetz, Reservierung, Optionswerte ---------------------- - subnetz = conn.execute("SELECT * FROM dhcp_subnets").fetchone() - assert (subnetz["range_start"], subnetz["range_end"]) == ( - "192.168.80.100", - "192.168.80.200", - ) - reservierung = conn.execute("SELECT * FROM dhcp_reservations").fetchone() - assert reservierung["hostname"] == "drucker", "alt 'name' -> neu 'hostname'" - - option = conn.execute("SELECT * FROM dhcp_options WHERE code=225").fetchone() - assert option is not None and option["name"] == "url" - werte = { - row["device_id"]: row["value"] - for row in conn.execute( - "SELECT device_id, value FROM dhcp_option_values WHERE option_id=?", - (option["id"],), - ) - } - # Ein globaler Wert und einer, der an genau diesem Geraet haengt. - assert werte[None] == "https://intranet.firma.local" - assert werte[geraet["id"]] == "https://play.autodarts.com" - - # -- Einstellungen: nur die DHCP-Schluessel ------------------------- - assert ext.settings.get(conn, "dhcp_domain") == "firma.local" - - -def test_option_value_of_an_unknown_device_is_not_made_global(tesm_app): - """Ein Wert fuer ein unbekanntes Geraet darf nicht an alle gehen. - - Sonst wuerde aus einer geraetespezifischen Terminal-URL beim Import eine, - die jeder Client bekommt -- die stillste Art, eine Konfiguration zu - verfaelschen. - """ - from tesm.services import transfer - from tesm_core.extension import core - - ext = core(tesm_app) - nutzlast = { - "dhcp": { - "options": [ - { - "code": 225, - "name": "url", - "type": "string", - "description": "", - "is_standard": 0, - "value": "https://nur-fuer-eines", - "device_mac": "ff:ff:ff:ff:ff:ff", - } - ] - } - } - with ext.database.session() as conn, ext.database.transaction(conn): - transfer.apply_import( - conn, - ext.keystore, - nutzlast, - categories=("dhcp",), - set_setting=lambda key, value: None, - ) - - with ext.database.session() as conn: - assert conn.execute("SELECT COUNT(*) FROM dhcp_option_values").fetchone()[0] == 0 diff --git a/tests/test_licensing.py b/tests/test_licensing.py deleted file mode 100644 index a945dc8..0000000 --- a/tests/test_licensing.py +++ /dev/null @@ -1,408 +0,0 @@ -"""Phase-0-Tests: das Lizenzprotokoll isoliert, ohne Flask, ohne Datenbank. - -Deckt bewusst auch die Angriffsfaelle ab, die im Vorgaengerprojekt nicht -abgesichert waren: Signatur-Wiederverwendung ueber Domaenengrenzen, Replay -alter Anfragen/Antworten, Downgrade eines nachgelieferten Lizenz-Updates und -Ausstellung durch einen nicht gepinnten Aussteller. -""" - -from __future__ import annotations - -import copy -from datetime import timedelta - -import pytest - -import tesm_licensing as lic - - -VENDOR = { - "name": "WiS GmbH", - "phone": "+49 000 000", - "email": "lizenzen@example.invalid", - "address": "Musterweg 1, 12345 Musterstadt", - "logo_base64": "", -} -CUSTOMER = {"name": "Beispiel AG", "contact_email": "it@example.invalid", "customer_ref": "K-1"} - - -@pytest.fixture() -def master_keys() -> tuple[str, str]: - return lic.generate_keypair() - - -def issue(master_keys, **overrides): - priv, pub = master_keys - params = dict( - customer=CUSTOMER, - license_type="standard", - modules=[], - valid_days=365, - master_private_key_b64=priv, - master_public_key_b64=pub, - master_endpoint="https://lizenz.example.invalid/", - vendor=VENDOR, - ) - params.update(overrides) - return lic.issue_license(**params) - - -# --------------------------------------------------------------------------- # -# Lizenzdatei -# --------------------------------------------------------------------------- # - - -def test_issued_license_verifies_and_splits(master_keys): - _, pub = master_keys - bundle, client_pub = issue(master_keys, modules=["dhcp", "maintenance"]) - document, client_priv = lic.split_bundle(bundle) - - lic.verify_license(document, trusted_master_keys=[pub]) - assert document["license_pubkey"] == client_pub - assert lic.public_from_private(client_priv) == client_pub - # Der private Schluessel darf nicht im Lizenzdokument stehen. - assert "client_key" not in document - assert "license_privkey" not in document - - status = lic.license_status(document, issuer_pinned=True) - assert status.modules == {"dhcp", "maintenance"} - assert status.modules_active is True - assert status.expired is False - assert status.days_left == 365 - assert status.warnings == () - - -def test_tampered_field_breaks_signature(master_keys): - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - document["modules"] = ["dhcp", "fileshare", "maintenance"] - with pytest.raises(lic.SignatureInvalid): - lic.verify_license(document) - - -def test_swapped_master_key_is_detected(master_keys): - """Ein Angreifer, der Payload *und* master_pubkey ersetzt, faellt am Pinning auf.""" - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - - evil_priv, evil_pub = lic.generate_keypair() - forged = lic.resign_license( - document, master_private_key_b64=evil_priv, changes={"modules": list(lic.ALL_MODULES)} - ) - forged["master_pubkey"] = evil_pub - forged["master_key_id"] = lic.key_id(evil_pub) - forged["signature"] = lic.sign( - {k: v for k, v in forged.items() if k != "signature"}, - evil_priv, - lic.SIG_CONTEXT_LICENSE, - ) - - # In sich konsistent -- genau das war die Schwaeche von V1. - lic.verify_license(forged) - # Mit gepinntem Aussteller fliegt es auf. - _, real_pub = master_keys - with pytest.raises(lic.UntrustedIssuer): - lic.verify_license(forged, trusted_master_keys=[real_pub]) - - -def test_key_id_mismatch_rejected(master_keys): - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - document["master_key_id"] = "0" * 16 - with pytest.raises(lic.InvalidLicenseFile): - lic.verify_license(document) - - -def test_license_signature_not_reusable_as_response(master_keys): - """Domaenentrennung: Lizenz-Signatur darf nicht als Antwort-Signatur gelten.""" - _, pub = master_keys - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - payload = {k: v for k, v in document.items() if k != "signature"} - assert lic.verify(payload, document["signature"], pub, lic.SIG_CONTEXT_LICENSE) is True - assert lic.verify(payload, document["signature"], pub, lic.SIG_CONTEXT_RESPONSE) is False - assert lic.verify(payload, document["signature"], pub, lic.SIG_CONTEXT_REQUEST) is False - - -def test_lifetime_license(master_keys): - bundle, _ = issue(master_keys, license_type="enterprise", lifetime=True, valid_days=0) - document, _ = lic.split_bundle(bundle) - lic.verify_license(document) - status = lic.license_status(document, issuer_pinned=True) - assert status.lifetime is True - assert status.expires_at is not None - assert status.days_left is None - assert status.expired is False - assert status.modules_active is True - - -def test_trial_lifetime_forbidden(master_keys): - with pytest.raises(ValueError): - issue(master_keys, license_type="trial", lifetime=True, valid_days=0) - - -def test_license_server_role_rejects_modules(master_keys): - with pytest.raises(ValueError): - issue(master_keys, role="license_server", modules=["dhcp"]) - - -def test_grace_period_and_expiry(master_keys): - bundle, _ = issue(master_keys, valid_days=10, modules=["dhcp"]) - document, _ = lic.split_bundle(bundle) - issued = lic.parse_iso(document["issued_at"]) - - warn = lic.license_status(document, now=issued + timedelta(days=9), issuer_pinned=True) - assert warn.expiring_soon is True and warn.expired is False and warn.modules_active is True - - grace = lic.license_status(document, now=issued + timedelta(days=25), issuer_pinned=True) - assert grace.expired is True - assert grace.grace_active is True - assert grace.modules_active is True # Kulanz laeuft noch - assert grace.days_since_expiry == 15 - - dead = lic.license_status(document, now=issued + timedelta(days=45), issuer_pinned=True) - assert dead.grace_active is False - assert dead.modules_active is False - - -def test_not_before_blocks_future_license(master_keys): - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - issued = lic.parse_iso(document["issued_at"]) - status = lic.license_status(document, now=issued - timedelta(hours=1), issuer_pinned=True) - assert status.not_yet_valid is True - assert status.modules_active is False - - -def test_status_without_pinning_warns(master_keys): - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - status = lic.license_status(document) - assert any("gepinnt" in w for w in status.warnings) - - -def test_role_defaults_to_customer_when_missing(master_keys): - bundle, _ = issue(master_keys) - document, _ = lic.split_bundle(bundle) - document.pop("role") - assert lic.license_status(document).role == "customer" - - -def test_v1_bundle_compatibility(master_keys): - """Eine alte Datei mit eingebettetem license_privkey laesst sich noch aufteilen.""" - bundle, _ = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - legacy = {**document, "license_privkey": client_priv} - doc2, priv2 = lic.split_bundle(legacy) - assert priv2 == client_priv - assert "license_privkey" not in doc2 - - -# --------------------------------------------------------------------------- # -# Aktivierungsprotokoll -# --------------------------------------------------------------------------- # - - -def test_activation_round_trip(master_keys): - master_priv, master_pub = master_keys - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - - request = lic.build_client_request("activate", document, client_priv, hostname="tesm-kunde") - lic.verify_client_request(request, license_pub, expected_action="activate") - - response = lic.build_master_response( - "activate", - license_id=request["license_id"], - fingerprint=request["fingerprint"], - request_nonce=request["nonce"], - master_private_key=master_priv, - ) - lic.verify_master_response( - response, - master_pub, - expected_license_id=document["license_id"], - expected_fingerprint=request["fingerprint"], - expected_action="activate", - expected_nonce=request["nonce"], - current_seq=document["seq"], - ) - - -def test_offline_code_round_trip_is_identical(master_keys): - master_priv, master_pub = master_keys - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - - request = lic.build_client_request("activate", document, client_priv) - code = lic.encode_code(request) - # Ein per Mail transportierter Code darf Umbrueche/Whitespace enthalten. - wrapped = "\n".join(code[i : i + 48] for i in range(0, len(code), 48)) - assert lic.decode_code(wrapped) == request - lic.verify_client_request(lic.decode_code(wrapped), license_pub) - - response = lic.build_master_response( - "activate", - license_id=request["license_id"], - fingerprint=request["fingerprint"], - request_nonce=request["nonce"], - master_private_key=master_priv, - ) - lic.verify_master_response( - lic.decode_code(lic.encode_code(response)), - master_pub, - expected_license_id=document["license_id"], - expected_fingerprint=request["fingerprint"], - expected_action="activate", - expected_nonce=request["nonce"], - ) - - -def test_request_signature_requires_correct_license_key(master_keys): - bundle, _ = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - other_priv, other_pub = lic.generate_keypair() - request = lic.build_client_request("heartbeat", document, client_priv) - with pytest.raises(lic.SignatureInvalid): - lic.verify_client_request(request, other_pub) - assert other_priv # nur zur Vollstaendigkeit - - -def test_request_replay_is_rejected(master_keys): - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - request = lic.build_client_request("activate", document, client_priv) - - used: set[str] = set() - - def seen(nonce: str) -> bool: - if nonce in used: - return True - used.add(nonce) - return False - - lic.verify_client_request(request, license_pub, seen_nonce=seen) - with pytest.raises(lic.ReplayDetected): - lic.verify_client_request(request, license_pub, seen_nonce=seen) - - -def test_stale_request_is_rejected(master_keys): - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - old = lic.utcnow() - timedelta(seconds=lic.REQUEST_MAX_AGE_SECONDS + 60) - request = lic.build_client_request("heartbeat", document, client_priv, now=old) - with pytest.raises(lic.ReplayDetected): - lic.verify_client_request(request, license_pub) - - -def test_unsigned_request_does_not_burn_nonce(master_keys): - """Nonce-Verbrauch erst nach gueltiger Signatur -- sonst waere die Nonce-Tabelle - ein kostenloser DoS-Vektor.""" - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - request = lic.build_client_request("activate", document, client_priv) - request["signature"] = lic.sign( - {"boese": True}, lic.generate_keypair()[0], lic.SIG_CONTEXT_REQUEST - ) - - calls: list[str] = [] - with pytest.raises(lic.SignatureInvalid): - lic.verify_client_request(request, license_pub, seen_nonce=lambda n: calls.append(n) or False) - assert calls == [] - - -def test_response_bound_to_request_nonce(master_keys): - master_priv, master_pub = master_keys - bundle, _ = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - - first = lic.build_client_request("activate", document, client_priv) - response = lic.build_master_response( - "activate", - license_id=first["license_id"], - fingerprint=first["fingerprint"], - request_nonce=first["nonce"], - master_private_key=master_priv, - ) - second = lic.build_client_request("activate", document, client_priv) - with pytest.raises(lic.ReplayDetected): - lic.verify_master_response( - response, - master_pub, - expected_license_id=document["license_id"], - expected_fingerprint=second["fingerprint"], - expected_action="activate", - expected_nonce=second["nonce"], - ) - - -def test_license_update_downgrade_rejected(master_keys): - master_priv, master_pub = master_keys - bundle, _ = issue(master_keys, modules=["dhcp"]) - document, client_priv = lic.split_bundle(bundle) - - updated = lic.resign_license( - document, master_private_key_b64=master_priv, changes={"modules": ["dhcp", "fileshare"]} - ) - assert updated["seq"] == document["seq"] + 1 - lic.verify_license(updated, trusted_master_keys=[master_pub]) - - request = lic.build_client_request("heartbeat", updated, client_priv) - stale = lic.build_master_response( - "heartbeat", - license_id=request["license_id"], - fingerprint=request["fingerprint"], - request_nonce=request["nonce"], - master_private_key=master_priv, - license_update=document, # alte Revision - ) - with pytest.raises(lic.ReplayDetected): - lic.verify_master_response( - stale, - master_pub, - expected_license_id=updated["license_id"], - expected_fingerprint=request["fingerprint"], - expected_action="heartbeat", - expected_nonce=request["nonce"], - current_seq=updated["seq"], - ) - - -def test_report_size_limit(master_keys): - bundle, license_pub = issue(master_keys, role="license_server") - document, client_priv = lic.split_bundle(bundle) - report = {"customers": [{"name": f"K{i}"} for i in range(lic.MAX_REPORT_ROWS + 1)]} - request = lic.build_client_request("heartbeat", document, client_priv, report=report) - with pytest.raises(lic.ProtocolViolation): - lic.verify_client_request(request, license_pub) - - -def test_malformed_fingerprint_and_nonce_rejected(master_keys): - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - base = lic.build_client_request("activate", document, client_priv) - - bad_fp = copy.deepcopy(base) - bad_fp["fingerprint"] = "nicht-hex" - with pytest.raises(lic.ProtocolViolation): - lic.verify_client_request(bad_fp, license_pub) - - bad_nonce = copy.deepcopy(base) - bad_nonce["nonce"] = "kurz" - with pytest.raises(lic.ProtocolViolation): - lic.verify_client_request(bad_nonce, license_pub) - - -def test_wrong_action_for_endpoint(master_keys): - bundle, license_pub = issue(master_keys) - document, client_priv = lic.split_bundle(bundle) - request = lic.build_client_request("heartbeat", document, client_priv) - with pytest.raises(lic.ProtocolViolation): - lic.verify_client_request(request, license_pub, expected_action="activate") - - -def test_fingerprint_is_stable_and_hex(): - first = lic.system_fingerprint() - assert first == lic.system_fingerprint() - assert len(first) == 64 - int(first, 16) diff --git a/tests/test_repo_hygiene.py b/tests/test_repo_hygiene.py deleted file mode 100644 index 3b48772..0000000 --- a/tests/test_repo_hygiene.py +++ /dev/null @@ -1,326 +0,0 @@ -"""Prueft das Repository selbst -- die Fehlerklassen, die erst beim Ausrollen auffallen. - -Der Vorgaenger hat sich hier zweimal die Finger verbrannt: CRLF-Zeilenenden aus -einer Windows-Buildmaschine machten Shell-Skripte auf dem Ziel unbrauchbar -("bad interpreter"), und Schluesseldateien landeten versehentlich im Repo. -Beides faellt hier auf, bevor ein Paket gebaut wird. -""" - -from __future__ import annotations - -import os -import re -import shutil -import subprocess -from pathlib import Path - -import pytest - -REPO_ROOT = Path(__file__).resolve().parent.parent - -SKIP_DIRS = { - ".git", - ".venv", - "__pycache__", - "instance", - ".pytest_cache", - ".ruff_cache", - "node_modules", -} -TEXT_SUFFIXES = { - ".py", ".sh", ".html", ".css", ".js", ".json", ".toml", ".md", - ".yaml", ".yml", ".cfg", ".ini", ".txt", ".service", ".conf", -} -#: Dateien ohne Endung, die trotzdem Text sind. -EXTENSIONLESS_TEXT = {"tesm-helper", "tesm", "tesm-license"} - -SECRET_NAMES = { - "data.keys", - "secret.key", - "license.json", - "license_key.json", - "master_signing_key.json", - "fernet.key", - "known_hosts", -} -SECRET_SUFFIXES = {".db", ".db-wal", ".db-shm", ".pem", ".p12", ".pfx"} - - -def _repo_files() -> list[Path]: - files: list[Path] = [] - for path in REPO_ROOT.rglob("*"): - if not path.is_file(): - continue - if any(part in SKIP_DIRS for part in path.relative_to(REPO_ROOT).parts): - continue - files.append(path) - return files - - -def _text_files() -> list[Path]: - return [ - path - for path in _repo_files() - if path.suffix in TEXT_SUFFIXES or path.name in EXTENSIONLESS_TEXT - ] - - -def test_no_crlf_line_endings(): - """CRLF im Paket macht Shell-Skripte auf dem Linux-Ziel unbrauchbar.""" - offenders = [ - str(path.relative_to(REPO_ROOT)) - for path in _text_files() - if b"\r\n" in path.read_bytes() - ] - assert not offenders, ( - "Diese Dateien haben CRLF-Zeilenenden und wuerden auf dem Zielsystem brechen:\n " - + "\n ".join(offenders) - ) - - -def test_no_secrets_committed(): - """Schluessel, Datenbanken und Lizenzdateien gehoeren nie ins Repo.""" - offenders = [ - str(path.relative_to(REPO_ROOT)) - for path in _repo_files() - if path.name in SECRET_NAMES or path.suffix in SECRET_SUFFIXES - ] - assert not offenders, "Geheimnisse im Repository:\n " + "\n ".join(offenders) - - -def test_shell_scripts_have_shebang_and_strict_mode(): - scripts = [path for path in _repo_files() if path.suffix == ".sh" or path.name == "tesm-helper"] - assert scripts, "Es wurden keine Shell-Skripte gefunden -- Test prueft ins Leere." - for script in scripts: - text = script.read_text(encoding="utf-8") - assert text.startswith("#!"), f"{script.name}: Shebang fehlt" - assert "set -euo pipefail" in text, f"{script.name}: 'set -euo pipefail' fehlt" - - -def test_helper_validates_every_verb(): - """Jedes Verb des privilegierten Helfers muss im case-Block auftauchen. - - Der Helfer ist die einzige Stelle mit Root-Rechten. Ein Verb, das die - Anwendung kennt, der Helfer aber nicht, waere entweder tot -- oder, - schlimmer, ein Hinweis auf eine ungeprueft durchgereichte Aktion. - """ - from tesm_core.sysops import HELPER_VERBS - - helper = (REPO_ROOT / "deploy" / "tesm-helper").read_text(encoding="utf-8") - # case-Muster koennen mehrere Verben mit | buendeln. - patterns: set[str] = set() - for line in helper.splitlines(): - match = re.match(r"^\s{2,4}([a-z|-]+)\)\s*$", line) - if match: - patterns.update(match.group(1).split("|")) - missing = [verb for verb in HELPER_VERBS if verb not in patterns] - assert not missing, f"Verben fehlen im Helfer: {missing}" - - -def test_helper_has_no_unquoted_argument_passthrough(): - """Der Helfer darf Argumente nie ungeprueft an eine Shell weiterreichen.""" - helper = (REPO_ROOT / "deploy" / "tesm-helper").read_text(encoding="utf-8") - for forbidden in ("eval ", "bash -c", "sh -c"): - assert forbidden not in helper, f"Der Helfer enthaelt {forbidden!r}" - - -def test_helper_blocks_path_traversal_everywhere(): - """Jede Pfadpruefung im Helfer muss ".." abweisen. - - Ohne diese Sperre laesst sich eine Positivliste ueber den Basisnamen - unterlaufen: ``/etc/nginx/sites-available/../../../root/tesm`` hat den - zulaessigen Basisnamen ``tesm``, liest aber ``/root/tesm``. - """ - helper = (REPO_ROOT / "deploy" / "tesm-helper").read_text(encoding="utf-8") - checks = re.findall( - r"^check_\w*(?:path|target|webroot)\w*\(\) \{(.*?)^\}", helper, re.S | re.M - ) - assert checks, "Es wurden keine Pfadpruefungen gefunden -- Test prueft ins Leere." - for body in checks: - assert '*".."*' in body, "Pfadpruefung ohne ..-Sperre: " + body - - -@pytest.mark.skipif(shutil.which("bash") is None, reason="bash nicht verfuegbar") -def test_helper_accepts_instances_but_no_invented_keys(): - """Der Namensraum einer Instanz ist eng begrenzt. - - Er landet ungefiltert in Pfaden wie ``/etc/nginx/sites-available/``. - """ - script = (REPO_ROOT / "deploy" / "tesm-helper").read_text(encoding="utf-8") - start = script.index("check_app_key() {") - end = script.index(chr(10) + "}", start) + 2 - prelude = 'APP_KEYS_ALLOWED=("tesm" "tesm-license")' + chr(10) + "fail() { exit 1; }" + chr(10) - snippet = prelude + script[start:end] - - def accepted(key: str) -> bool: - command = snippet + chr(10) + 'check_app_key "' + key + '"' - return subprocess.run(["bash", "-c", command], capture_output=True).returncode == 0 - - for good in ("tesm", "tesm-license", "tesm-opus", "tesm-license-opus", "tesm-test2"): - assert accepted(good), good - for bad in ("", "foo", "tesm-", "tesm-../etc", "../tesm", "tesm-GROSS", "tesm-" + "x" * 20): - assert not accepted(bad), bad - - - -def test_systemd_units_are_hardened(): - units = list((REPO_ROOT / "deploy" / "systemd").glob("*.service")) - assert units - for unit in units: - text = unit.read_text(encoding="utf-8") - assert "User=root" not in text, f"{unit.name} laeuft als root" - # ProtectSystem=full, nicht strict: strict sperrt auch den privilegierten - # Helfer aus /etc aus -- gemessen, siehe test_core_security.py. - for directive in ("ProtectSystem=full", "PrivateTmp=yes"): - assert directive in text, f"{unit.name}: {directive} fehlt" - # NoNewPrivileges waere hier kontraproduktiv: es schaltet setuid ab und - # damit sudo -- der Dienst koennte den privilegierten Helfer nicht mehr - # aufrufen, und jede Systemaktion aus der Oberflaeche waere tot. Genau - # so war es, und die Diagnoseseite meldete trotzdem "Helfer erreichbar". - assert "NoNewPrivileges=yes" not in text, ( - f"{unit.name}: NoNewPrivileges=yes verhindert sudo und damit den Helfer." - ) - - -def test_sudoers_grants_only_the_helper(): - for name in ("tesm", "tesm-license"): - raw = (REPO_ROOT / "deploy" / "sudoers" / name).read_text(encoding="utf-8") - # Kommentare erklaeren, was bewusst *nicht* getan wird -- sie duerfen den - # Test nicht ausloesen. - rules = [line for line in raw.splitlines() if line.strip() and not line.startswith("#")] - body = "\n".join(rules) - assert "NOPASSWD: ALL" not in body, f"{name}: zu weite sudo-Regel" - assert "/usr/local/lib/tesm/tesm-helper" in body - for line in rules: - if "NOPASSWD" in line: - assert line.strip().endswith("/usr/local/lib/tesm/tesm-helper"), ( - f"{name}: NOPASSWD-Regel erlaubt mehr als den Helfer: {line}" - ) - - -@pytest.mark.parametrize("app", ["tesm", "tesm-license"]) -def test_nginx_template_serves_static_from_app_root(app: str): - """Der alias-Pfad ist genau der Fehler, der im Vorgaenger jahrelang ueberlebte.""" - text = (REPO_ROOT / "deploy" / "nginx" / f"{app}.conf").read_text(encoding="utf-8") - assert f"alias /srv/{app}/static/;" in text - assert f"alias /srv/{app}/static-core/;" in text - - -def test_documentation_referenced_by_the_units_exists(): - """``Documentation=`` in der Unit muss auf eine Datei zeigen, die es gibt. - - Sonst laeuft ``systemctl help tesm`` ins Leere -- und schlimmer: der - Verweis suggeriert eine Anleitung, die niemand geschrieben hat. - """ - for unit in (REPO_ROOT / "deploy" / "systemd").glob("*.service"): - for line in unit.read_text(encoding="utf-8").splitlines(): - if not line.startswith("Documentation=file:"): - continue - target = line.split("file:", 1)[1].strip() - # /srv//docs/X -> docs/X im Repository - relative = target.split("/docs/", 1)[1] - assert (REPO_ROOT / "docs" / relative).is_file(), ( - f"{unit.name} verweist auf {target}, docs/{relative} fehlt" - ) - - -def test_readme_links_point_to_existing_files(): - readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8") - for target in re.findall(r"\]\((docs/[^)#]+)\)", readme): - assert (REPO_ROOT / target).is_file(), f"README verweist auf {target}" - - -def _templates() -> list[Path]: - return [ - path - for path in _repo_files() - if path.suffix == ".html" and "templates" in path.parts - ] - - -def test_no_html_entities_inside_escaped_macro_calls(): - """Makros escapen ihren Text -- Entities darin erscheinen woertlich. - - Auf der Wartungsseite stand deshalb ``„Linux-Client“`` als - sichtbarer Text. Der Fehler ist unauffaellig, weil dieselbe Schreibweise - im uebrigen Vorlagentext voellig richtig ist. - """ - # Genau bis zum schliessenden "}}" des Aufrufs -- ein greedy Ausdruck - # verschluckte sonst den nachfolgenden Seiteninhalt und meldete dessen - # Tabellenmarkup als Treffer. - pattern = re.compile( - r"\{\{\s*(?:alert|empty_state|pill|stat)\((?:[^{}]|\{[^{}]*\})*?\)\s*" - r"(?:\|\s*safe\s*)?\}\}", - re.S, - ) - offenders: list[str] = [] - for path in _templates(): - for match in pattern.finditer(path.read_text(encoding="utf-8")): - call = match.group(0) - # Frueher stand hier eine Ausnahme fuer "| safe". Sie war falsch: - # das Filter wirkt auf das *Ergebnis* des Makros, escaped wurde aber - # schon beim Einsetzen des Arguments. Auf der Geraeteseite stand - # deshalb woertlich "und" im Text. - if re.search(r"&[a-zA-Z]{2,10};|", call): - offenders.append( - f"{path.relative_to(REPO_ROOT)}: {' '.join(call.split())[:100]}" - ) - assert not offenders, ( - "HTML im escapten Makrotext -- das erscheint woertlich auf der Seite:\n " - + "\n ".join(offenders) - ) - - -def test_refresh_regions_never_point_at_a_whole_page(): - """``data-refresh-url`` schreibt die Antwort in ``innerHTML``. - - Zeigt die URL auf eine vollstaendige Seite, landet die ganze Seite in - einem Ausschnitt -- bei jedem Durchlauf eine Ebene tiefer. Genau das ist - auf der Wartungsseite passiert: die Karte "Auftraege" holte sich - ``maintenance.overview``, also sich selbst. - - Erlaubt sind nur Endpunkte, deren Name sie als Ausschnitt ausweist. - """ - fragment_markers = ("fragment", "tiles", "output", "status", "partial", "ausschnitt") - pattern = re.compile(r"data-refresh-url=\"\{\{\s*url_for\('([^']+)'") - found = 0 - for path in _templates(): - for match in pattern.finditer(path.read_text(encoding="utf-8")): - endpoint = match.group(1) - found += 1 - assert any(marker in endpoint.lower() for marker in fragment_markers), ( - f"{path.relative_to(REPO_ROOT)}: data-refresh-url zeigt auf {endpoint!r} -- " - "das sieht nach einer ganzen Seite aus, nicht nach einem Ausschnitt." - ) - assert found, "Keine Aktualisierungsbereiche gefunden -- Test prueft ins Leere." - - -def test_refresh_loop_treats_zero_as_off(): - """``Number(x || 15000)`` macht aus einer 0 klammheimlich 15000.""" - script = ( - REPO_ROOT - / "packages" - / "tesm-core" - / "src" - / "tesm_core" - / "static" - / "tesm_core" - / "js" - / "app.js" - ).read_text(encoding="utf-8") - assert "refreshInterval || " not in script - assert "interval <= 0" in script, "Ein Intervall von 0 muss die Aktualisierung abschalten." - - -def test_licensing_package_is_shared_not_copied(): - """Es darf keine zweite Kopie des Lizenzprotokolls geben. - - Genau diese Doppelung -- zwei byte-identisch zu haltende ``licensing.py`` -- - war im Vorgaenger die gefaehrlichste Invariante. - """ - copies = [ - str(path.relative_to(REPO_ROOT)) - for path in _repo_files() - if path.name == "licensing.py" - ] - assert not copies, f"Kopien des Lizenzmoduls gefunden: {copies}" diff --git a/tests/test_tesm_restarts_ssh.py b/tests/test_tesm_restarts_ssh.py deleted file mode 100644 index 2540b91..0000000 --- a/tests/test_tesm_restarts_ssh.py +++ /dev/null @@ -1,1376 +0,0 @@ -"""Neustarts und SSH-Zugang zu Clients. - -Zwei Luecken, die erst im Betrieb auffielen: - -1. **Die Neustart-Historie zeigte nur die halbe Wahrheit.** Sie hiess - "PoE-Vorgaenge" und enthielt auch nur PoE-Vorgaenge. Ein Geraet, das die - Wartung per ``reboot`` neu gestartet hatte, fehlte dort vollstaendig -- - obwohl genau dort nachsieht, wer wissen will, warum ein Geraet weg war. - -2. **Fuer Clients gab es keine Host-Schluessel-Freigabe.** Die Wartung - verweigerte den Dienst ("Schluessel unbekannt"), und es gab keine Stelle in - der Oberflaeche, an der man ihn haette ansehen und freigeben koennen. Eine - Sackgasse, aus der nur ein Shell-Zugang auf den Server herausfuehrte. -""" - -from __future__ import annotations - -from typing import Any - -import pytest - - -@pytest.fixture() -def tesm_app(instance_env: Any): - from tesm import create_app - - return create_app(TESTING=True) - - -@pytest.fixture() -def tclient(tesm_app: Any): - return tesm_app.test_client() - - -@pytest.fixture() -def admin(tesm_app: Any): - from tesm_core.extension import core - from tesm_core.security import passwords - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - conn.execute( - "INSERT INTO users (username, password_hash, is_admin, auth_source, created_at, " - "updated_at) VALUES ('root',?,1,'local',datetime('now'),datetime('now'))", - (passwords.hash_password("Ein-gutes-Passwort-1"),), - ) - return {"username": "root", "password": "Ein-gutes-Passwort-1"} - - -def _token(client: Any, path: str = "/login") -> str: - body = client.get(path).get_data(as_text=True) - for marker in ('name="csrf_token" value="', 'data-csrf="'): - if marker in body: - start = body.index(marker) + len(marker) - return body[start : body.index('"', start)] - raise AssertionError(f"Kein CSRF-Token in {path}") - - -@pytest.fixture() -def session(tclient: Any, admin: dict[str, str]): - tclient.post( - "/login", - data={ - "username": admin["username"], - "password": admin["password"], - "csrf_token": _token(tclient), - }, - follow_redirects=True, - ) - return tclient - - -@pytest.fixture() -def device(tesm_app: Any): - """Ein Client mit Zugangsdaten -- Voraussetzung fuer Wartung und Konsole.""" - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - conn.execute( - "INSERT INTO credentials (name, username, secret_encrypted, category, created_at, " - "updated_at) VALUES ('linux-login','wartung','', 'linux', datetime('now'), " - "datetime('now'))" - ) - conn.execute( - "INSERT INTO devices (name, mac, ip, ssh_port, credential_id, is_active, " - "created_at, updated_at) VALUES ('Kamera-1','aa:bb:cc:dd:ee:01','10.9.9.9',22,1,1," - "datetime('now'), datetime('now'))" - ) - return {"id": 1, "ip": "10.9.9.9", "port": 22} - - -# --------------------------------------------------------------------------- # -# Neustart-Historie -# --------------------------------------------------------------------------- # - - -def test_restart_log_replaces_the_poe_only_page(session): - """Die alte Adresse ist weg, die neue zeigt Neustarts jeder Art.""" - assert session.get("/protokolle/poe").status_code == 404 - response = session.get("/protokolle/neustarts") - assert response.status_code == 200 - body = response.get_data(as_text=True) - assert "Neustarts" in body - assert "PoE-Vorgaenge" not in body - - -def test_restart_log_shows_both_methods(tesm_app, session, device): - from tesm.services import monitor - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - monitor.record_restart( - conn, - device_id=1, - action="cycle", - result="ok", - trigger="auto", - actor="system", - duration_ms=1200, - detail="Port getrennt und wieder zugeschaltet", - ) - monitor.record_restart( - conn, - device_id=1, - action="reboot", - result="ok", - trigger="maintenance", - actor="root", - duration_ms=45000, - detail="Neustart ueber SSH", - method=monitor.METHOD_SSH, - ) - - body = session.get("/protokolle/neustarts").get_data(as_text=True) - assert "PoE-Port" in body - assert "SSH" in body - assert "Kamera-1" in body - - # Filter je Methode - only_ssh = session.get("/protokolle/neustarts?methode=ssh").get_data(as_text=True) - assert "maintenance" in only_ssh - assert only_ssh.count(" None: - self._output = output - self._status = status - self._sent = False - self.closed = False - - def settimeout(self, _value: float) -> None: - pass - - def set_combine_stderr(self, _value: bool) -> None: - pass - - def exec_command(self, _command: str) -> None: - pass - - def recv_ready(self) -> bool: - return not self._sent - - def recv(self, _size: int) -> bytes: - self._sent = True - return self._output - - def exit_status_ready(self) -> bool: - return self._sent - - def recv_exit_status(self) -> int: - return self._status - - def close(self) -> None: - self.closed = True - - -class FakeTransport: - def __init__(self, channel: FakeChannel) -> None: - self.channel = channel - - def is_active(self) -> bool: - return True - - def open_session(self) -> FakeChannel: - return self.channel - - def close(self) -> None: - pass - - -def test_shell_session_returns_output_and_status(): - from tesm.services.shellcli import ShellSession - - channel = FakeChannel(b"Linux poetest 6.8.0\n", 0) - session = ShellSession(None, host="h", port=22, username="u", password="p") - session._transport = FakeTransport(channel) # noqa: SLF001 - - result = session.run("uname -sr") - assert result["output"].startswith("Linux poetest") - assert result["status"] == 0 - assert result["duration_ms"] >= 0 - assert channel.closed, "Der Kanal muss geschlossen werden." - - -def test_shell_session_reports_a_failing_command(): - from tesm.services.shellcli import ShellSession - - session = ShellSession(None, host="h", port=22, username="u", password="p") - session._transport = FakeTransport(FakeChannel(b"bash: nope: not found\n", 127)) # noqa: SLF001 - result = session.run("nope") - assert result["status"] == 127 - - -def test_shell_session_refuses_a_dead_transport(): - from tesm.services.shellcli import ShellSession - from tesm.services.ssh import SshError - - session = ShellSession(None, host="h", port=22, username="u", password="p") - with pytest.raises(SshError): - session.run("uptime") - - -def test_shell_session_truncates_a_flood(): - from tesm.services.shellcli import MAX_OUTPUT_BYTES, ShellSession - - flood = b"x" * (MAX_OUTPUT_BYTES + 1024) - session = ShellSession(None, host="h", port=22, username="u", password="p") - session._transport = FakeTransport(FakeChannel(flood, 0)) # noqa: SLF001 - result = session.run("yes") - assert "abgeschnitten" in result["output"] - assert len(result["output"]) < len(flood.decode()) - - -# --------------------------------------------------------------------------- # -# Auftragsausgabe -# --------------------------------------------------------------------------- # - - -def test_job_output_keeps_one_line_per_entry(tesm_app): - """Jede Ausgabe gehoert auf eine eigene Zeile. - - ``splitlines()`` wirft das abschliessende Zeilenende weg. Ohne Korrektur - klebte jede neue Zeile an der vorherigen, und das ganze Protokoll stand am - Ende auf einer einzigen Zeile -- genau so im Betrieb aufgefallen. - """ - from tesm.services import maintenance - from tesm_core.db import Database - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - handle = maintenance.create_job( - conn, kind=maintenance.JOB_KIND_UPDATE, subject="Testgeraet", actor="root" - ) - - for line in ("--- Paketlisten aktualisieren ---", "sudo: a password is required", - "Schritt endete mit Rueckgabewert 1."): - maintenance.update_job(extension.database, handle.id, append_output=f"12:00:00 {line}\n") - - with extension.database.session() as conn: - output = str(Database.value(conn, "SELECT output FROM jobs WHERE id=?", (handle.id,))) - - lines = output.splitlines() - assert len(lines) == 3, f"Erwartet drei Zeilen, bekommen: {output!r}" - assert lines[0].endswith("--- Paketlisten aktualisieren ---") - assert lines[1].endswith("sudo: a password is required") - assert "---sudo:" not in output, "Zeilen kleben aneinander." - - -def test_job_output_is_trimmed_but_stays_line_wise(tesm_app): - from tesm.services import maintenance - from tesm_core.db import Database - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - handle = maintenance.create_job( - conn, kind=maintenance.JOB_KIND_UPDATE, subject="Flut", actor="root" - ) - for index in range(maintenance.MAX_OUTPUT_LINES + 50): - maintenance.update_job(extension.database, handle.id, append_output=f"Zeile {index}\n") - - with extension.database.session() as conn: - output = str(Database.value(conn, "SELECT output FROM jobs WHERE id=?", (handle.id,))) - lines = output.splitlines() - assert len(lines) == maintenance.MAX_OUTPUT_LINES - # Die juengsten Zeilen bleiben stehen, nicht die aeltesten. - assert lines[-1] == f"Zeile {maintenance.MAX_OUTPUT_LINES + 49}" - - -def test_known_failures_get_an_actionable_hint(): - """Die rohe Meldung sagt die Wahrheit, aber nicht, was zu tun ist.""" - from tesm.services.maintenance import hint_for - - # Passwort verlangt, aber keines hinterlegt -> auf die Zugangsdaten zeigen. - hint = hint_for("sudo: a password is required") - assert "Zugangsdaten" in hint - assert "NOPASSWD" not in hint, "Der Weg ueber die Zugangsdaten ist der einfachere." - - # Passwort abgewiesen -> nicht die Zugangsdaten, sondern sudo ist das Problem. - assert "abgewiesen" in hint_for("sudo: 1 incorrect password attempt") - - # Konto darf gar kein sudo -> sudoers-Regel noetig. - not_allowed = hint_for("wartung is not in the sudoers file. This incident will be reported.") - assert "sudoers" in not_allowed - assert "NOPASSWD" in not_allowed - - assert "requiretty" in hint_for("sudo: no tty present and no askpass program specified") - assert "apt-get" in hint_for("bash: apt-get: command not found") - assert "unattended" in hint_for("E: Could not get lock /var/lib/dpkg/lock-frontend") - assert hint_for("alles in Ordnung") == "" - - -def test_hint_survives_surrounding_output(): - from tesm.services.maintenance import hint_for - - output = "\n".join( - ["--- Paketlisten aktualisieren ---", "sudo: a password is required", "Ende"] - ) - assert hint_for(output) - - -# --------------------------------------------------------------------------- # -# sudo mit dem hinterlegten Passwort -# --------------------------------------------------------------------------- # - - -class SudoTransport: - """Zeichnet auf, welche Kommandos laufen und was ueber stdin hereinkommt.""" - - def __init__(self, passwordless_ok: bool) -> None: - self.passwordless_ok = passwordless_ok - self.commands: list[str] = [] - self.stdin: list[str] = [] - - def is_active(self) -> bool: - return True - - def open_session(self): # type: ignore[no-untyped-def] - return SudoChannel(self) - - def close(self) -> None: - pass - - -class SudoChannel: - def __init__(self, transport: SudoTransport) -> None: - self.transport = transport - self.command = "" - self.written = b"" - self._done = False - - def settimeout(self, _v: float) -> None: - pass - - def set_combine_stderr(self, _v: bool) -> None: - pass - - def exec_command(self, command: str) -> None: - self.command = command - self.transport.commands.append(command) - - def sendall(self, data: bytes) -> None: - self.written += data - - def shutdown_write(self) -> None: - self.transport.stdin.append(self.written.decode()) - - def recv_ready(self) -> bool: - return False - - def exit_status_ready(self) -> bool: - return True - - def recv(self, _n: int) -> bytes: - return b"" - - def recv_exit_status(self) -> int: - if self.command.startswith("sudo -n true"): - return 0 if self.transport.passwordless_ok else 1 - return 0 - - def close(self) -> None: - pass - - -def test_passwordless_sudo_keeps_the_password_on_the_server(): - """Geht es ohne, verlaesst das Passwort den Server gar nicht erst.""" - from tesm.services.maintenance import SUDO_PASSWORDLESS, _sudo_mode - - transport = SudoTransport(passwordless_ok=True) - lines: list[str] = [] - sudo, stdin = _sudo_mode(transport, "geheim", lines.append) - - assert sudo == SUDO_PASSWORDLESS - assert stdin == "" - assert transport.stdin == [], "Ohne Not wird kein Passwort uebertragen." - assert "ohne Passwort" in " ".join(lines) - - -def test_sudo_falls_back_to_the_stored_password(): - from tesm.services.maintenance import SUDO_WITH_PASSWORD, _sudo_mode - - transport = SudoTransport(passwordless_ok=False) - lines: list[str] = [] - sudo, stdin = _sudo_mode(transport, "geheim", lines.append) - - assert sudo == SUDO_WITH_PASSWORD - assert stdin == "geheim\n" - assert "Zugangsdaten" in " ".join(lines) - - -def test_password_never_appears_on_the_command_line(): - """Auf dem Zielgeraet steht die Kommandozeile in ``ps`` -- fuer jeden lesbar.""" - from tesm.services.maintenance import ( - REBOOT_COMMAND, - UPDATE_COMMANDS, - _run_remote_command, - _sudo_mode, - ) - - transport = SudoTransport(passwordless_ok=False) - sudo, stdin = _sudo_mode(transport, "streng-geheim", lambda _line: None) - - for _label, template in UPDATE_COMMANDS: - _run_remote_command( - transport, template.format(sudo=sudo), timeout=5, on_line=lambda _l: None, - stdin_text=stdin, - ) - _run_remote_command( - transport, REBOOT_COMMAND.format(sudo=sudo), timeout=5, on_line=lambda _l: None, - stdin_text=stdin, - ) - - assert all("streng-geheim" not in command for command in transport.commands), ( - "Das Passwort darf nie in einem Kommando stehen." - ) - # Es geht ausschliesslich ueber stdin. - assert transport.stdin, "Das Passwort muss ueber stdin gehen." - assert all(entry == "streng-geheim\n" for entry in transport.stdin) - - -def test_sudo_prompt_is_suppressed(): - """Ohne ``-p ''`` landet die Passwortaufforderung mitten in der Ausgabe.""" - from tesm.services.maintenance import SUDO_WITH_PASSWORD - - assert "-S" in SUDO_WITH_PASSWORD - assert "-p ''" in SUDO_WITH_PASSWORD - assert "-k" in SUDO_WITH_PASSWORD - - -def test_commands_carry_no_hardcoded_sudo(): - """Das Verfahren wird zur Laufzeit gewaehlt -- nicht in der Konstante festgelegt.""" - from tesm.services.maintenance import REBOOT_COMMAND, UPDATE_COMMANDS - - for _label, template in UPDATE_COMMANDS: - assert "{sudo}" in template - assert "sudo -n" not in template - assert "{sudo}" in REBOOT_COMMAND - - -def test_without_a_stored_password_nothing_is_invented(): - from tesm.services.maintenance import SUDO_PASSWORDLESS, _sudo_mode - - transport = SudoTransport(passwordless_ok=False) - lines: list[str] = [] - sudo, stdin = _sudo_mode(transport, "", lines.append) - assert sudo == SUDO_PASSWORDLESS - assert stdin == "" - assert "keines hinterlegt" in " ".join(lines) - - -# --------------------------------------------------------------------------- # -# Rueckkehr nach einem Neustart -# --------------------------------------------------------------------------- # - - -def test_reboot_waits_on_tcp_not_on_icmp(): - """ICMP kann der Webprozess gar nicht. - - Die systemd-Unit der Weboberflaeche hat ein leeres - Die systemd-Unit der Weboberflaeche bekommt keine AmbientCapabilities -- - damit greift die Datei-Capability von ``/usr/bin/ping`` nicht. Jeder Ping - schlaegt dort fehl, und der Wartungsauftrag lief deshalb *immer* in die - Zeitueberschreitung, obwohl das Geraet laengst zurueck war. - """ - import inspect - - from tesm.services import maintenance - - source = inspect.getsource(maintenance.run_reboot) - assert "tcp_reachable(" in source - assert "ping(" not in source, "ICMP ist im Webprozess nicht verfuegbar." - - -def test_web_unit_has_no_net_raw_but_the_monitor_does(): - """Die Aufteilung ist Absicht und muss so bleiben.""" - from pathlib import Path - - root = Path(__file__).resolve().parent.parent / "deploy" / "systemd" - web = (root / "tesm.service").read_text(encoding="utf-8") - monitor = (root / "tesm-monitor.service").read_text(encoding="utf-8") - - # Kein CapabilityBoundingSet mehr -- es bricht sudo (gemessen). Was den - # Webprozess von ICMP fernhaelt, ist das Fehlen von AmbientCapabilities. - assert "CapabilityBoundingSet=" not in web - assert "AmbientCapabilities=CAP_NET_RAW" not in web, ( - "Der Webprozess soll keine Rohsockets duerfen -- er ist der exponierte." - ) - assert "AmbientCapabilities=CAP_NET_RAW" in monitor, ( - "Der Ueberwachungsdienst braucht CAP_NET_RAW fuer ICMP." - ) - - -def test_tcp_probe_needs_no_privileges(): - import socket - import threading - - from tesm.services.monitor import tcp_reachable - - server = socket.socket() - server.bind(("127.0.0.1", 0)) - server.listen(1) - port = server.getsockname()[1] - threading.Thread(target=lambda: server.accept(), daemon=True).start() - - assert tcp_reachable("127.0.0.1", port, timeout=2) - server.close() - - -def test_tcp_probe_reports_a_closed_port(): - from tesm.services.monitor import tcp_reachable - - # Port 1 ist auf keinem sinnvoll konfigurierten System offen. - assert not tcp_reachable("127.0.0.1", 1, timeout=1) - - -# --------------------------------------------------------------------------- # -# Keine geschachtelten Datenbankverbindungen -# --------------------------------------------------------------------------- # - - -def test_restart_takes_the_keystore_not_a_callback(): - """Ein Rueckruf kannte die offene Verbindung nicht und oeffnete eine neue. - - Der Ueberwachungsdienst starb dadurch bei jedem automatischen PoE-Neustart - mit ``NestedConnectionError`` -- und der Geraetestatus blieb stehen. Mit dem - Schluesselspeicher als Argument gibt es die falsche Variante nicht mehr. - """ - import inspect - - from tesm.services import monitor - - for function in (monitor.restart_device_poe, monitor.run_sweep): - signature = inspect.signature(function) - assert "keystore" in signature.parameters, function.__name__ - assert "decrypt_secret" not in signature.parameters, function.__name__ - - source = inspect.getsource(monitor.restart_device_poe) - assert "credential_secret(conn," in source, "Die vorhandene Verbindung muss benutzt werden." - - -def test_no_caller_passes_a_decrypt_callback(): - from pathlib import Path - - root = Path(__file__).resolve().parent.parent / "apps" / "tesm" / "src" / "tesm" - offenders = [ - str(path.relative_to(root)) - for path in root.rglob("*.py") - if "__pycache__" not in path.parts and "decrypt_secret" in path.read_text(encoding="utf-8") - ] - assert not offenders, f"Rueckruf lebt noch in: {offenders}" - - -def test_sweep_survives_a_restart_without_nested_connection(tesm_app, device): - """Der ganze Durchlauf, inklusive Neustartversuch, ohne zweite Verbindung.""" - from tesm.services import monitor - from tesm_core.extension import core - - extension = core(tesm_app) - live = extension.registry["live_log"] - - with tesm_app.app_context(): - report = monitor.run_sweep( - database=extension.database, - live=live, - keystore=extension.keystore, - license_allows_auto_restart=True, - failures_before_restart=1, - ) - # Das Geraet ist nicht erreichbar -- entscheidend ist, dass der Durchlauf - # ueberhaupt durchlaeuft und nicht an einer geschachtelten Verbindung stirbt. - assert report.checked >= 1 - - -# --------------------------------------------------------------------------- # -# Windows: kein SSH, kein Host-Schluessel -# --------------------------------------------------------------------------- # - - -def test_windows_is_not_an_ssh_target(): - from tesm.services import inventory - - assert inventory.is_ssh_managed("linux") - assert inventory.is_ssh_managed("switch") - assert inventory.is_ssh_managed(None) - assert not inventory.is_ssh_managed("windows") - - -def test_windows_devices_do_not_appear_in_the_host_key_page(tesm_app, session): - """Sonst stuenden sie fuer immer auf "Freigabe offen".""" - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - conn.execute( - "INSERT INTO credentials (name, username, secret_encrypted, category, created_at, " - "updated_at) VALUES ('win','admin','','windows',datetime('now'),datetime('now'))" - ) - conn.execute( - "INSERT INTO credentials (name, username, secret_encrypted, category, created_at, " - "updated_at) VALUES ('lin','wartung','','linux',datetime('now'),datetime('now'))" - ) - conn.execute( - "INSERT INTO devices (name, mac, ip, ssh_port, credential_id, is_active, created_at, " - "updated_at) VALUES ('PC-Win','aa:bb:cc:00:00:01','10.5.5.1',22,1,1,datetime('now')," - "datetime('now'))" - ) - conn.execute( - "INSERT INTO devices (name, mac, ip, ssh_port, credential_id, is_active, created_at, " - "updated_at) VALUES ('PC-Linux','aa:bb:cc:00:00:02','10.5.5.2',22,2,1,datetime('now')," - "datetime('now'))" - ) - - body = session.get("/hostschluessel/").get_data(as_text=True) - assert "PC-Linux" in body - assert "PC-Win" not in body, "Windows braucht keinen Host-Schluessel." - - -def test_windows_reboot_uses_rpc_not_ssh(): - import inspect - - from tesm.services import maintenance - - source = inspect.getsource(maintenance.run_reboot) - assert "_reboot_windows(" in source - assert "WINDOWS_PROBE_PORT" in source - # Der SSH-Zweig darf fuer Windows gar nicht erst betreten werden. - assert "if is_windows:" in source - - -def test_windows_password_never_reaches_the_command_line(monkeypatch, tmp_path): - """``net -U benutzer%pass`` waere in ``ps`` sichtbar -- auf beiden Seiten.""" - import subprocess - - from tesm.services import windowsops - - seen: dict[str, object] = {} - - def fake_run(command, **kwargs): - seen["command"] = command - authfile = command[command.index("--authentication-file") + 1] - seen["authfile_content"] = pathlib_read(authfile) - return subprocess.CompletedProcess(command, 0, "Shutdown ausgeloest", "") - - def pathlib_read(path: str) -> str: - with open(path, encoding="utf-8") as handle: - return handle.read() - - monkeypatch.setattr(windowsops.shutil, "which", lambda _name: "/usr/bin/net") - monkeypatch.setattr(windowsops.subprocess, "run", fake_run) - - result = windowsops.reboot("10.5.5.1", "administrator", "streng-geheim") - assert result.ok - - command = " ".join(str(part) for part in seen["command"]) - assert "streng-geheim" not in command, "Passwort steht in der Kommandozeile." - assert "%" not in command - # Es geht ausschliesslich ueber die Anmeldedatei. - assert "password=streng-geheim" in str(seen["authfile_content"]) - - -def test_windows_auth_file_is_removed_afterwards(monkeypatch): - import os - import subprocess - - from tesm.services import windowsops - - captured: dict[str, str] = {} - - def fake_run(command, **kwargs): - captured["path"] = command[command.index("--authentication-file") + 1] - return subprocess.CompletedProcess(command, 0, "", "") - - monkeypatch.setattr(windowsops.shutil, "which", lambda _name: "/usr/bin/net") - monkeypatch.setattr(windowsops.subprocess, "run", fake_run) - windowsops.reboot("10.5.5.1", "administrator", "geheim") - - assert not os.path.exists(captured["path"]), "Die Anmeldedatei muss verschwinden." - - -def test_windows_domain_prefix_is_split(): - from tesm.services import windowsops - - captured: dict[str, str] = {} - - class FakeCompleted: - returncode = 0 - stdout = "" - stderr = "" - - def fake_run(command, **kwargs): - with open(command[command.index("--authentication-file") + 1], encoding="utf-8") as handle: - captured["content"] = handle.read() - return FakeCompleted() - - import subprocess - - original_which = windowsops.shutil.which - original_run = windowsops.subprocess.run - windowsops.shutil.which = lambda _name: "/usr/bin/net" - windowsops.subprocess.run = fake_run - try: - windowsops.reboot("10.5.5.1", "EERTMOED" + chr(92) + "administrator", "geheim") - finally: - windowsops.shutil.which = original_which - windowsops.subprocess.run = original_run - - assert "username=administrator" in captured["content"] - assert "domain=EERTMOED" in captured["content"] - assert subprocess is not None - - -def test_missing_samba_is_explained_not_silently_ignored(monkeypatch): - from tesm.services import windowsops - - monkeypatch.setattr(windowsops.shutil, "which", lambda _name: None) - result = windowsops.reboot("10.5.5.1", "administrator", "geheim") - assert not result.ok - assert windowsops.REQUIRED_PACKAGE in result.hint - - -def test_windows_errors_get_a_hint(): - from tesm.services.windowsops import hint_for - - assert "DOMAENE" in hint_for("NT_STATUS_LOGON_FAILURE") - assert "Remotesystem" in hint_for("NT_STATUS_ACCESS_DENIED") - assert "445" in hint_for("NT_STATUS_CONNECTION_REFUSED") - assert hint_for("alles gut") == "" - - -def test_samba_package_is_installable_through_the_helper(): - from pathlib import Path - - helper = (Path(__file__).resolve().parent.parent / "deploy" / "tesm-helper").read_text( - encoding="utf-8" - ) - from tesm.services.windowsops import REQUIRED_PACKAGE - - assert REQUIRED_PACKAGE in helper, "Sonst laesst es sich nicht nachinstallieren." - - -# --------------------------------------------------------------------------- # -# Seiten mit Inhalt rendern -# --------------------------------------------------------------------------- # - - -@pytest.fixture() -def populated(tesm_app: Any): - """Ein Bestand, der jede Verzweigung der Vorlagen erreicht. - - Ein Rauchtest auf leeren Seiten prueft zu wenig: Schleifenkoerper laufen - dort nie. Genau so ist eine fehlende Vorlagenvariable durchgerutscht -- - lokal gruen, auf dem Zielsystem HTTP 500, sobald der erste Datensatz da war. - """ - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - for index, (name, category) in enumerate( - [("sw-login", "switch"), ("linux-login", "linux"), ("win-login", "windows")], start=1 - ): - conn.execute( - "INSERT INTO credentials (name, username, secret_encrypted, category, " - "created_at, updated_at) VALUES (?,?,'',?,datetime('now'),datetime('now'))", - (name, f"user{index}", category), - ) - conn.execute( - "INSERT INTO switches (hostname, ip, ssh_port, credential_id, created_at, updated_at) " - "VALUES ('sw-1','10.7.0.1',22,1,datetime('now'),datetime('now'))" - ) - for index, (name, credential) in enumerate( - [("Linux-PC", 2), ("Windows-PC", 3), ("Ohne-Login", None)], start=1 - ): - conn.execute( - "INSERT INTO devices (name, mac, ip, ssh_port, switch_id, port, credential_id, " - "is_active, created_at, updated_at) " - "VALUES (?,?,?,22,1,?,?,1,datetime('now'),datetime('now'))", - (name, f"aa:bb:cc:00:0{index}:01", f"10.7.0.{10 + index}", str(index), credential), - ) - conn.execute( - "INSERT INTO switch_host_keys (host, port, key_type, fingerprint, added_at, added_by) " - "VALUES ('10.7.0.1',22,'ssh-ed25519',?,datetime('now'),'test')", - ("SHA256:" + "A" * 43,), - ) - return True - - -PAGES_WITH_CONTENT = [ - "/", - "/clients/", - "/clients/1", - "/switche/", - "/switche/1", - "/zugangsdaten/", - "/hostschluessel/", - "/protokolle/neustarts", - "/papierkorb/", - "/diagnose/", -] - - -@pytest.mark.parametrize("path", PAGES_WITH_CONTENT) -def test_pages_render_with_real_data(session, populated, path): - response = session.get(path) - body = response.get_data(as_text=True) - assert response.status_code == 200, f"{path}: HTTP {response.status_code}" - assert "Interner Fehler" not in body, path - # Eine fehlende Vorlagenvariable schlaegt als UndefinedError durch -- die - # Fehlerseite nennt sie beim Namen. - assert "is undefined" not in body, path - - -def test_maintenance_pages_render_with_real_data(session, populated, licensed): - for path in ("/wartung/", "/dateifreigaben/", "/dhcp/"): - response = session.get(path) - assert response.status_code == 200, f"{path}: HTTP {response.status_code}" - assert "Interner Fehler" not in response.get_data(as_text=True), path - - -def test_device_form_offers_creating_credentials(session, populated): - body = session.get("/clients/").get_data(as_text=True) - assert "neue Zugangsdaten anlegen" in body - assert "new_credential_name" in body - assert "new_credential_secret" in body - - -def test_new_device_can_bring_its_own_credentials(session, populated, tesm_app): - from tesm_core.db import Database - from tesm_core.extension import core - - response = session.post( - "/clients/neu", - data={ - "csrf_token": _token(session, "/clients/"), - "name": "Frisch", - "mac": "aa:bb:cc:11:22:33", - "ip": "10.7.0.90", - "ssh_port": "22", - "credential_id": "__neu__", - "new_credential_name": "Frische Zugangsdaten", - "new_credential_username": "wartung", - "new_credential_secret": "Ein-Passwort-1", - "new_credential_category": "linux", - "is_active": "1", - }, - follow_redirects=True, - ) - assert "angelegt" in response.get_data(as_text=True) - - extension = core(tesm_app) - with extension.database.session() as conn: - credential = Database.one( - conn, "SELECT * FROM credentials WHERE name='Frische Zugangsdaten'" - ) - device = Database.one(conn, "SELECT * FROM devices WHERE name='Frisch'") - assert credential is not None, "Die Zugangsdaten muessen mit angelegt werden." - assert device is not None - assert device["credential_id"] == credential["id"], "Sie muessen auch zugeordnet sein." - assert credential["secret_encrypted"], "Das Passwort muss verschluesselt gespeichert sein." - - -def test_a_nameless_new_credential_blocks_the_device(session, populated, tesm_app): - """Halb ausgefuehrt waere schlimmer als gar nicht: das Geraet haette dann - keine Zugangsdaten, obwohl welche gewollt waren.""" - from tesm_core.db import Database - from tesm_core.extension import core - - response = session.post( - "/clients/neu", - data={ - "csrf_token": _token(session, "/clients/"), - "name": "Halb", - "mac": "aa:bb:cc:44:55:66", - "ip": "10.7.0.91", - "ssh_port": "22", - "credential_id": "__neu__", - "new_credential_name": "", - "is_active": "1", - }, - follow_redirects=True, - ) - assert "Bezeichnung" in response.get_data(as_text=True) - - extension = core(tesm_app) - with extension.database.session() as conn: - assert Database.one(conn, "SELECT * FROM devices WHERE name='Halb'") is None - - -def test_sweep_holds_no_write_lock_across_the_ssh_session(): - """Eine SSH-Sitzung zum Switch dauert Sekunden -- eine Schreibsperre auch. - - Auf POETEST dauerte ein Durchlauf mit drei nicht erreichbaren Switchen - knapp 40 Sekunden, und die Oberflaeche antwortete waehrenddessen mit - "database is locked". Die langsame Netzarbeit gehoert deshalb ausserhalb - der Transaktion; geschrieben wird kurz und am Ende. - """ - import inspect - - from tesm.services import monitor - - source = inspect.getsource(monitor.run_sweep) - assert "with database.session() as conn, database.transaction(conn):\n outcome" not in source - assert "database=database," in source, "Die Aufzeichnung braucht eigene Transaktionen." - - restart = inspect.getsource(monitor.restart_device_poe) - assert "with write():" in restart - # Die SSH-Sitzung darf in keiner Transaktion stehen. - ssh_line = restart.index("with SwitchCli(") - assert "with write():" not in restart[max(0, ssh_line - 400) : ssh_line] - - -# --------------------------------------------------------------------------- # -# Neustart: mehrere Wege, klare Rangfolge -# --------------------------------------------------------------------------- # - - -def _device(**overrides): - base = { - "id": 1, - "name": "Testgeraet", - "switch_id": None, - "port": "", - "credential_id": None, - "credential_category": "", - } - return {**base, **overrides} - - -def test_poe_wins_when_several_ways_are_open(): - """PoE zuerst: es wirkt auch, wenn das Betriebssystem nicht mehr antwortet.""" - from tesm.services import restart - - device = _device(switch_id=1, port="7", credential_id=2, credential_category="linux") - options = restart.available(device) - assert [entry.key for entry in options] == ["poe", "ssh"] - assert restart.preferred(device).key == "poe" - - -def test_only_ssh_when_there_is_no_port(): - from tesm.services import restart - - device = _device(credential_id=2, credential_category="linux") - assert [entry.key for entry in restart.available(device)] == ["ssh"] - - -def test_windows_offers_rpc_never_ssh(): - from tesm.services import restart - - device = _device(credential_id=2, credential_category="windows") - assert [entry.key for entry in restart.available(device)] == ["rpc"] - - -def test_a_switch_without_a_port_is_not_a_way(): - """Ein Knopf, der verlaesslich nichts bewirkt, ist schlimmer als kein Knopf.""" - from tesm.services import restart - - assert restart.available(_device(switch_id=1, port="")) == [] - assert restart.available(_device(switch_id=None, port="7")) == [] - - -def test_nothing_configured_means_no_button(): - from tesm.services import restart - - assert restart.available(_device()) == [] - assert restart.preferred(_device()) is None - - -def test_ssh_and_rpc_need_the_maintenance_module(): - """PoE bleibt frei -- ein haengendes Geraet muss man immer neu starten koennen.""" - from tesm.services import restart - - linux = _device(switch_id=1, port="7", credential_id=2, credential_category="linux") - assert [entry.key for entry in restart.available(linux, maintenance_active=False)] == ["poe"] - - windows = _device(credential_id=2, credential_category="windows") - assert restart.available(windows, maintenance_active=False) == [] - - -def test_an_impossible_request_is_refused_not_substituted(): - """Sonst startet jemand ein Geraet anders neu, als er dachte.""" - from tesm.services import restart - - device = _device(switch_id=1, port="7") - assert restart.resolve(device, "ssh") is None - assert restart.resolve(device, "poe").key == "poe" - # Ohne Angabe gilt die Rangfolge. - assert restart.resolve(device, "").key == "poe" - - -def test_restart_route_refuses_an_unavailable_method(session, populated): - """Geraet 1 ("Linux-PC") haengt an Switch 1 Port 1 und hat Linux-Zugangsdaten.""" - response = session.post( - "/clients/3/neustart", - data={"csrf_token": _token(session, "/clients/"), "method": "poe"}, - follow_redirects=True, - ) - body = response.get_data(as_text=True) - # Geraet 3 ("Ohne-Login") hat Switch und Port, aber keine Zugangsdaten -- - # PoE geht, SSH nicht. Umgekehrt geprueft: - response = session.post( - "/clients/3/neustart", - data={"csrf_token": _token(session, "/clients/"), "method": "ssh"}, - follow_redirects=True, - ) - assert "steht fuer dieses Geraet nicht zur Verfuegung" in response.get_data(as_text=True) - assert body is not None - - -def test_device_list_shows_the_credential_category(session, populated): - body = session.get("/clients/").get_data(as_text=True) - assert "Linux-Client" in body - assert "Windows-Client" in body - - -def test_restart_buttons_appear_per_available_way(session, populated, licensed): - body = session.get("/clients/").get_data(as_text=True) - assert "/neustart" in body - assert "PoE-Port" in body - assert "Neustart ueber RPC" in body - - -def test_the_old_poe_only_endpoint_is_gone(): - """Ein zweiter Endpunkt fuer denselben Zweck waere eine Verzweigung zu viel.""" - from tesm import create_app - - rules = {rule.rule for rule in create_app(TESTING=True).url_map.iter_rules()} - assert "/clients//neustart" in rules - assert "/clients//poe-neustart" not in rules diff --git a/tests/test_tesm_smoke.py b/tests/test_tesm_smoke.py deleted file mode 100644 index 4c4b0b6..0000000 --- a/tests/test_tesm_smoke.py +++ /dev/null @@ -1,968 +0,0 @@ -"""Rauchtest der TESM-Anwendung: jede Seite rendert, jede Aktion greift. - -Deckt bewusst auch die Faelle ab, die im Vorgaengerprojekt kaputtgingen: -Rechteschranken, Lizenzgates auf Modulrouten, MAC-Normalisierung, und die -Trennung zwischen oeffentlicher und angemeldeter Statusuebersicht. -""" - -from __future__ import annotations - -from typing import Any - -import pytest - - -@pytest.fixture() -def tesm_app(instance_env: Any): - from tesm import create_app - - app = create_app(TESTING=True) - yield app - - -@pytest.fixture() -def tclient(tesm_app: Any): - return tesm_app.test_client() - - -@pytest.fixture() -def admin(tesm_app: Any): - from tesm_core.extension import core - from tesm_core.security import passwords - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - conn.execute( - "INSERT INTO users (username, password_hash, is_admin, auth_source, created_at, " - "updated_at) VALUES ('root',?,1,'local',datetime('now'),datetime('now'))", - (passwords.hash_password("Ein-gutes-Passwort-1"),), - ) - return {"username": "root", "password": "Ein-gutes-Passwort-1"} - - -def _token(client: Any, path: str = "/login") -> str: - body = client.get(path).get_data(as_text=True) - for marker in ('name="csrf_token" value="', 'data-csrf="'): - if marker in body: - start = body.index(marker) + len(marker) - return body[start : body.index('"', start)] - raise AssertionError(f"Kein CSRF-Token in {path}") - - -@pytest.fixture() -def login_admin(tclient: Any, admin: dict[str, str]): - def do() -> Any: - return tclient.post( - "/login", - data={ - "username": admin["username"], - "password": admin["password"], - "csrf_token": _token(tclient), - }, - follow_redirects=True, - ) - - return do - - -# --------------------------------------------------------------------------- # -# Start und Grundzustand -# --------------------------------------------------------------------------- # - - -def test_app_boots_and_migrations_applied(tesm_app): - from tesm_core.extension import core - - status = core(tesm_app).migration_status() - components = {row["component"] for row in status} - assert components == {"core", "tesm"} - names = {(row["component"], row["name"]) for row in status} - assert ("tesm", "baseline") in names - assert ("tesm", "seed-dhcp-options") in names - - -def test_dhcp_option_catalogue_seeded(tesm_app): - from tesm_core.db import Database - from tesm_core.extension import core - - extension = core(tesm_app) - with extension.database.session() as conn: - count = Database.value(conn, "SELECT COUNT(*) FROM dhcp_options WHERE is_standard=1") - host_name = Database.value(conn, "SELECT COUNT(*) FROM dhcp_options WHERE code=12") - assert count == 56 - # host-name (12) ist bewusst nicht im Katalog -- Kea beantwortet das ueber - # das hostname-Feld der Reservierung. - assert host_name == 0 - - -def test_role_presets_created(tesm_app): - from tesm_core.extension import core - from tesm_core.rbac import service as rbac - - extension = core(tesm_app) - with extension.database.session() as conn: - names = {group.name for group in rbac.list_groups(conn)} - assert {"Benutzer", "Betrieb", "Support", "Netzwerk", "Revision", "Administration"} <= names - - -def test_public_dashboard_hides_details(tclient): - response = tclient.get("/") - assert response.status_code == 200 - body = response.get_data(as_text=True) - assert "Statusuebersicht" in body - assert "oeffentliche Ansicht" in body - - -def test_health_endpoint(tclient): - assert tclient.get("/gesundheit").get_json()["status"] == "ok" - - -# --------------------------------------------------------------------------- # -# Seiten -# --------------------------------------------------------------------------- # - - -ADMIN_PAGES = [ - "/", - "/clients/", - "/switche/", - "/zugangsdaten/", - "/protokolle/laufend", - "/protokolle/verlauf", - "/protokolle/aenderungen", - "/protokolle/neustarts", - "/einstellungen/system", - "/einstellungen/netzwerk", - "/einstellungen/webserver", - "/einstellungen/verzeichnisdienst", - "/lizenz/", - "/sicherung/", - "/papierkorb/", - "/diagnose/", - "/verwaltung/benutzer", - "/verwaltung/gruppen", - "/verwaltung/sitzungen", - "/konto/", - "/konto/sicherheit", -] - - -@pytest.mark.parametrize("path", ADMIN_PAGES) -def test_admin_pages_render(tclient, login_admin, path): - login_admin() - response = tclient.get(path) - assert response.status_code == 200, f"{path} -> {response.status_code}" - assert "Interner Fehler" not in response.get_data(as_text=True) - - -def test_licensed_modules_are_hidden_without_license(tclient, login_admin): - """Ohne Lizenz sind DHCP, Wartung und Dateifreigaben komplett unerreichbar.""" - login_admin() - for path in ("/dhcp/", "/wartung/", "/dateifreigaben/", "/protokolle/kea"): - assert tclient.get(path).status_code == 404, path - body = tclient.get("/").get_data(as_text=True) - assert "DHCP-Server" not in body - assert "Wartung" not in body - - -# --------------------------------------------------------------------------- # -# Geraete -# --------------------------------------------------------------------------- # - - -def test_device_lifecycle(tclient, login_admin): - login_admin() - token = _token(tclient, "/clients/") - - created = tclient.post( - "/clients/neu", - data={ - "csrf_token": token, - "name": "Kamera Eingang", - "mac": "AA-BB-CC-DD-EE-FF", - "ip": "192.168.1.50", - "is_active": "1", - "auto_restart": "1", - "ssh_port": "22", - }, - follow_redirects=True, - ) - assert created.status_code == 200 - body = created.get_data(as_text=True) - assert "angelegt" in body - # MAC wird normalisiert. - assert "aa:bb:cc:dd:ee:ff" in tclient.get("/clients/").get_data(as_text=True) - - listing = tclient.get("/clients/").get_data(as_text=True) - assert "Kamera Eingang" in listing - - # Loeschen legt in den Papierkorb, MAC bleibt reserviert. - tclient.post("/clients/1/loeschen", data={"csrf_token": token}, follow_redirects=True) - trash = tclient.get("/papierkorb/").get_data(as_text=True) - assert "Kamera Eingang" in trash - - again = tclient.post( - "/clients/neu", - data={ - "csrf_token": token, - "name": "Zweite Kamera", - "mac": "aa:bb:cc:dd:ee:ff", - "ip": "192.168.1.51", - "ssh_port": "22", - }, - follow_redirects=True, - ) - assert "Papierkorb" in again.get_data(as_text=True) - - -def test_device_requires_port_when_switch_assigned(tclient, login_admin): - login_admin() - token = _token(tclient, "/switche/") - tclient.post( - "/switche/neu", - data={"csrf_token": token, "hostname": "sw01", "ip": "192.168.1.2", "ssh_port": "22"}, - follow_redirects=True, - ) - response = tclient.post( - "/clients/neu", - data={ - "csrf_token": token, - "name": "Ohne Port", - "mac": "11:22:33:44:55:66", - "ip": "192.168.1.60", - "switch_id": "1", - "ssh_port": "22", - }, - follow_redirects=True, - ) - assert "Portbezeichnung" in response.get_data(as_text=True) - - -def test_invalid_mac_rejected(tclient, login_admin): - login_admin() - token = _token(tclient, "/clients/") - response = tclient.post( - "/clients/neu", - data={"csrf_token": token, "name": "Kaputt", "mac": "xyz", "ip": "192.168.1.70"}, - follow_redirects=True, - ) - assert "hexadezimale" in response.get_data(as_text=True) - - -def test_invalid_ip_rejected(tclient, login_admin): - login_admin() - token = _token(tclient, "/clients/") - response = tclient.post( - "/clients/neu", - data={"csrf_token": token, "name": "Kaputt", "mac": "11:22:33:44:55:77", "ip": "999.1.1.1"}, - follow_redirects=True, - ) - assert "gueltige IP-Adresse" in response.get_data(as_text=True) - - -# --------------------------------------------------------------------------- # -# Zugangsdaten und Verschluesselung -# --------------------------------------------------------------------------- # - - -def test_credential_secret_is_encrypted_at_rest(tclient, tesm_app, login_admin): - from tesm_core.db import Database - from tesm_core.extension import core - - login_admin() - token = _token(tclient, "/zugangsdaten/") - tclient.post( - "/zugangsdaten/neu", - data={ - "csrf_token": token, - "name": "Switch-Admin", - "username": "admin", - "secret": "SuperGeheim-2026", - "category": "switch", - }, - follow_redirects=True, - ) - extension = core(tesm_app) - with extension.database.session() as conn: - stored = Database.value(conn, "SELECT secret_encrypted FROM credentials WHERE name=?", ("Switch-Admin",)) - assert stored - assert "SuperGeheim" not in stored - assert stored.startswith("v1.") - - -def test_credential_reveal_requires_permission(tclient, tesm_app, login_admin): - from tesm_core.extension import core - from tesm_core.security import passwords - - login_admin() - token = _token(tclient, "/zugangsdaten/") - tclient.post( - "/zugangsdaten/neu", - data={ - "csrf_token": token, - "name": "Nur-Lesen", - "username": "admin", - "secret": "Geheim-Passwort-9", - "category": "switch", - }, - follow_redirects=True, - ) - tclient.get("/logout") - - # Ein Konto mit credentials.view aber ohne credentials.secrets. - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - cursor = conn.execute( - "INSERT INTO users (username, password_hash, auth_source, created_at, updated_at) " - "VALUES ('lisa',?,'local',datetime('now'),datetime('now'))", - (passwords.hash_password("Ein-gutes-Passwort-1"),), - ) - user_id = int(cursor.lastrowid or 0) - group = conn.execute( - "INSERT INTO groups (name, created_at, updated_at) " - "VALUES ('nur-lesen',datetime('now'),datetime('now'))" - ) - group_id = int(group.lastrowid or 0) - conn.executemany( - "INSERT INTO group_permissions (group_id, permission) VALUES (?,?)", - [(group_id, "monitoring.view"), (group_id, "credentials.view")], - ) - conn.execute("INSERT INTO user_groups (user_id, group_id) VALUES (?,?)", (user_id, group_id)) - - client = tclient - client.post( - "/login", - data={"username": "lisa", "password": "Ein-gutes-Passwort-1", "csrf_token": _token(client)}, - follow_redirects=True, - ) - assert client.get("/zugangsdaten/").status_code == 200 - response = client.post("/zugangsdaten/1/anzeigen", data={"csrf_token": _token(client, "/zugangsdaten/")}) - assert response.status_code == 403 - - -def test_credential_reveal_is_audited(tclient, tesm_app, login_admin): - from tesm_core import audit - from tesm_core.extension import core - - login_admin() - token = _token(tclient, "/zugangsdaten/") - tclient.post( - "/zugangsdaten/neu", - data={ - "csrf_token": token, - "name": "Geheim", - "username": "admin", - "secret": "Geheim-Passwort-9", - "category": "switch", - }, - follow_redirects=True, - ) - response = tclient.post( - "/zugangsdaten/1/anzeigen", data={"csrf_token": token}, follow_redirects=True - ) - assert response.status_code == 200 - assert "Geheim-Passwort-9" in response.get_data(as_text=True) - - extension = core(tesm_app) - with extension.database.session() as conn: - rows = audit.search(conn, action="credential.secret_revealed") - assert rows and rows[0]["severity"] == "warning" - - -# --------------------------------------------------------------------------- # -# Switche und Host-Schluessel -# --------------------------------------------------------------------------- # - - -def test_switch_created_without_trusted_host_key(tclient, login_admin): - login_admin() - token = _token(tclient, "/switche/") - response = tclient.post( - "/switche/neu", - data={"csrf_token": token, "hostname": "sw-kern", "ip": "10.0.0.2", "ssh_port": "22"}, - follow_redirects=True, - ) - assert "freigegeben werden" in response.get_data(as_text=True) - detail = tclient.get("/switche/1").get_data(as_text=True) - assert "Noch kein Schluessel hinterlegt" in detail - - -def test_switch_delete_blocked_while_devices_attached(tclient, login_admin): - login_admin() - token = _token(tclient, "/switche/") - tclient.post( - "/switche/neu", - data={"csrf_token": token, "hostname": "sw01", "ip": "10.0.0.3", "ssh_port": "22"}, - follow_redirects=True, - ) - tclient.post( - "/clients/neu", - data={ - "csrf_token": token, - "name": "Kamera", - "mac": "aa:11:22:33:44:55", - "ip": "10.0.0.50", - "switch_id": "1", - "port": "1", - "ssh_port": "22", - }, - follow_redirects=True, - ) - response = tclient.post("/switche/1/loeschen", data={"csrf_token": token}, follow_redirects=True) - assert "zugeordnet" in response.get_data(as_text=True) - - -def test_command_not_on_allowlist_rejected(tclient, login_admin): - login_admin() - token = _token(tclient, "/switche/") - tclient.post( - "/switche/neu", - data={"csrf_token": token, "hostname": "sw01", "ip": "10.0.0.4", "ssh_port": "22"}, - follow_redirects=True, - ) - response = tclient.post( - "/switche/1/kommando", - data={"csrf_token": token, "command": "erase startup-config"}, - follow_redirects=True, - ) - assert "Positivliste" in response.get_data(as_text=True) - - -# --------------------------------------------------------------------------- # -# Rechte -# --------------------------------------------------------------------------- # - - -def test_viewer_cannot_reach_admin_pages(tclient, tesm_app): - from tesm_core.extension import core - from tesm_core.rbac import service as rbac - from tesm_core.security import passwords - - extension = core(tesm_app) - with extension.database.session() as conn, extension.database.transaction(conn): - cursor = conn.execute( - "INSERT INTO users (username, password_hash, auth_source, created_at, updated_at) " - "VALUES ('gast',?,'local',datetime('now'),datetime('now'))", - (passwords.hash_password("Ein-gutes-Passwort-1"),), - ) - user_id = int(cursor.lastrowid or 0) - default = next(group for group in rbac.list_groups(conn) if group.is_default) - conn.execute( - "INSERT INTO user_groups (user_id, group_id) VALUES (?,?)", (user_id, default.id) - ) - - tclient.post( - "/login", - data={"username": "gast", "password": "Ein-gutes-Passwort-1", "csrf_token": _token(tclient)}, - follow_redirects=True, - ) - assert tclient.get("/clients/").status_code == 200 - for path in ("/zugangsdaten/", "/verwaltung/benutzer", "/einstellungen/system", "/diagnose/"): - assert tclient.get(path).status_code == 403, path - - -def test_permission_matrix_columns_per_area(tesm_app): - from tesm.permissions import PERMISSIONS - from tesm_core.rbac.model import Action - - monitoring = PERMISSIONS.area("monitoring") - assert Action.SECRETS in monitoring.columns() # Zugangsdaten - assert Action.DELETE in monitoring.columns() - - network = PERMISSIONS.area("network") - assert Action.CREATE not in network.columns() # DHCP kennt kein "Anlegen" - assert Action.SECRETS not in network.columns() - - -def test_delete_is_a_separate_right(tesm_app): - """Kernaenderung gegenueber dem Vorgaenger: Aendern deckt nicht mehr Loeschen ab.""" - from tesm.permissions import PERMISSIONS - - granted = {"monitoring.view", "devices.view", "devices.edit"} - effective = PERMISSIONS.effective(granted) - assert "devices.edit" in effective - assert "devices.delete" not in effective - - -# --------------------------------------------------------------------------- # -# Offline-Aktivierung: der Code muss sichtbar sein -# --------------------------------------------------------------------------- # - - -def test_offline_request_keeps_its_code(): - """Die Seite forderte zum Uebermitteln eines Codes auf -- und zeigte keinen. - - ``handshake`` erzeugte ihn, der Blueprint verwarf ihn, und nach der - Weiterleitung war er weg. Er gehoert deshalb zur gespeicherten Anfrage: - zwischen Anfrage und Antwort liegen in der Praxis eine E-Mail und ein - Arbeitstag. - """ - import inspect - - from tesm_core import licensing_client - - source = inspect.getsource(licensing_client.LicenseManager.handshake) - assert '"code": code,' in source - assert "code = lic.encode_code(request_payload)" in source - - -def test_the_reason_for_going_offline_is_named(): - """"War nicht erreichbar" ist bei einem Zertifikatsfehler schlicht falsch.""" - from tesm_core.licensing_client import describe_transport_error - - certificate = describe_transport_error( - Exception("[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate"), - "https://10.0.0.1:8443", - ) - assert "selbstsigniertes Zertifikat" in certificate - assert "https://10.0.0.1:8443" in certificate - - assert "aufloesen" in describe_transport_error( - Exception("Name or service not known"), "https://nope.invalid" - ) - assert "nimmt keine Verbindung an" in describe_transport_error( - Exception("Connection refused"), "https://10.0.0.1:8443" - ) - assert "rechtzeitig" in describe_transport_error( - Exception("operation timed out"), "https://10.0.0.1:8443" - ) - - -def test_license_page_shows_the_request_code(): - from pathlib import Path - - template = ( - Path(__file__).resolve().parent.parent - / "apps" / "tesm" / "src" / "tesm" / "templates" / "tesm" / "license.html" - ).read_text(encoding="utf-8") - assert "pending.code" in template - assert "Anfragecode" in template - assert 'data-copy="#offline-request"' in template - - -# --------------------------------------------------------------------------- # -# Dateifreigaben -# --------------------------------------------------------------------------- # - - -def _license_all_modules(tesm_app): - """Alle Module freischalten -- die Freigabeseiten haengen an der Lizenz.""" - from tesm_core.extension import core - - manager = core(tesm_app).license - if manager is not None: - manager.module_active = lambda _name: True # type: ignore[method-assign] - - -def test_share_setup_page_is_reachable_and_searches_groups(tesm_app, tclient, login_admin): - """Die Einrichtung war nur ueber die Adresszeile erreichbar. - - Der Menuepunkt haengte an vorhandenen Einbindungen -- die es ohne - Zuordnung nie gibt. Wer Zuordnungen pflegen darf, muss die Seite auch - finden, und der Gruppen-DN muss suchbar sein statt abtippbar. - """ - _license_all_modules(tesm_app) - login_admin() - - page = tclient.get("/dateifreigaben/zuordnungen") - assert page.status_code == 200 - body = page.get_data(as_text=True) - assert "data-group-query" in body, "Gruppensuche fehlt" - assert 'name="ad_group_dn"' in body - - nav = tclient.get("/").get_data(as_text=True) - assert "/dateifreigaben/zuordnungen" in nav, "Zuordnungen fehlen in der Navigation" - - -def test_share_mapping_round_trip(tesm_app, tclient, login_admin): - _license_all_modules(tesm_app) - login_admin() - - token = _token(tclient, "/dateifreigaben/zuordnungen") - response = tclient.post( - "/dateifreigaben/zuordnungen", - data={ - "label": "Technik", - "unc": r"\\fileserver\technik", - "ad_group_name": "GG_Technik", - "ad_group_dn": "CN=GG_Technik,OU=Gruppen,DC=firma,DC=local", - "csrf_token": token, - }, - follow_redirects=True, - ) - assert response.status_code == 200 - assert "Zuordnung gespeichert" in response.get_data(as_text=True) - - from tesm.services import fileshare - from tesm_core.extension import core - - with core(tesm_app).database.session() as conn: - assert len(fileshare.list_mappings(conn)) == 1 - # Der Anspruch entsteht ueber die Verzeichnisgruppe -- und nur darueber. - assert fileshare.shares_for_groups( - conn, ["cn=gg_technik,ou=gruppen,dc=firma,dc=local"] - ) - assert not fileshare.shares_for_groups(conn, []) - - -def test_login_triggers_share_mounting(tesm_app, tclient, login_admin, monkeypatch): - """Der Ausloeser fehlte: Zuordnungen bestanden, eingebunden wurde nie. - - Ohne diesen Haken blieb der gesamte Freigabe-Teil wirkungslos -- und weil - der Menuepunkt an Einbindungen haengt, war er auch unsichtbar. - """ - from tesm_core.extension import core - - seen: list[dict] = [] - - def fake_mount(conn, **kwargs): - seen.append(kwargs) - return [] - - _license_all_modules(tesm_app) - monkeypatch.setattr("tesm.services.fileshare.mount_for_login", fake_mount) - assert core(tesm_app).login_hooks, "kein Anmeldehaken registriert" - - login_admin() - assert seen, "die Anmeldung hat die Einbindung nicht ausgeloest" - # Das Passwort wird durchgereicht, aber nirgends abgelegt. - assert seen[0]["password"] - with tclient.session_transaction() as session: - assert not any("passw" in str(key).lower() for key in session) - - -def test_logout_releases_shares(tesm_app, tclient, login_admin, monkeypatch): - """Was die Anmeldung eingebunden hat, muss die Abmeldung wieder loesen. - - Sonst bliebe der Zugriff mit dem Konto des Benutzers offen, bis die - Zeitgrenze der Einbindung greift -- Stunden spaeter. - """ - from tesm_core.extension import core - - _license_all_modules(tesm_app) - released: list[int] = [] - monkeypatch.setattr( - "tesm.services.fileshare.mount_for_login", lambda conn, **kw: [] - ) - monkeypatch.setattr( - "tesm.services.fileshare.release_all_for_user", - lambda conn, user_id: released.append(user_id) or 1, - ) - assert core(tesm_app).logout_hooks, "kein Abmeldehaken registriert" - - login_admin() - token = _token(tclient, "/") - tclient.post("/logout", data={"csrf_token": token}) - assert released, "die Abmeldung hat die Freigaben nicht geloest" - - -@pytest.mark.parametrize( - "eingabe", - [ - r"\\dc2022\daten", - "//dc2022/daten", - r"\\dc2022/daten", - "//dc2022/daten/", - "\\\\dc2022\\daten\\", # Trenner am Ende - "//dc2022//daten", - " //dc2022/daten ", - ], -) -def test_unc_accepts_both_separators(eingabe): - """Backslash und Schraegstrich muessen beide gehen -- und zwar gleich. - - Wer den Pfad aus dem Explorer kopiert, bekommt Backslashes; wer ihn aus - einer Weboberflaeche oder von einem Linux-Kollegen bekommt, Schraegstriche. - Ueberzaehlige Trenner und einer am Ende gehoeren zum Alltag und duerfen - keine Ablehnung ausloesen. - """ - from tesm.services.fileshare import validate_unc - - assert validate_unc(eingabe) == "\\\\dc2022\\daten" - - -@pytest.mark.parametrize( - "eingabe", - [ - "/dc2022/daten", # ein Trenner: absoluter Pfad auf diesem Rechner - "//dc2022", # ohne Freigabenamen - "dc2022/daten", - "//dc 2022/daten", - ], -) -def test_unc_rejects_what_is_not_a_share(eingabe): - from tesm.services.fileshare import FileshareError, validate_unc - - with pytest.raises(FileshareError): - validate_unc(eingabe) - - -# --------------------------------------------------------------------------- # -# DHCP: Reservierungen und Optionen je Geraet -# --------------------------------------------------------------------------- # - - -def _netz(cidr: str = "192.168.80.0/24", interface: str = "eth0"): - from tesm.services.kea import InterfaceNetwork - - return InterfaceNetwork(interface=interface, cidr=cidr) - - -def _geraet(conn, name: str, mac: str, ip: str) -> int: - cur = conn.execute( - "INSERT INTO devices (name, mac, ip, is_active, created_at, updated_at) " - "VALUES (?,?,?,1,datetime('now'),datetime('now'))", - (name, mac, ip), - ) - return int(cur.lastrowid) - - -def test_reservations_only_for_existing_ranges(tesm_app): - """Eine Reservierung ausserhalb aller Bereiche liefert Kea nie aus. - - Sie trotzdem zu zeigen, hat Reservierungen versprochen, die es nicht gab -- - in der Liste standen Adressen, die in der erzeugten Konfiguration fehlten. - """ - from tesm.services import kea - from tesm_core.extension import core - - ext = core(tesm_app) - with ext.database.session() as conn, ext.database.transaction(conn): - _geraet(conn, "drinnen", "aa:bb:cc:dd:ee:01", "192.168.80.50") - _geraet(conn, "draussen", "aa:bb:cc:dd:ee:02", "10.99.0.50") - conn.execute( - "INSERT INTO dhcp_subnets (interface, range_start, range_end, enabled, " - "created_at, updated_at) VALUES ('eth0','192.168.80.100','192.168.80.200',1," - "datetime('now'),datetime('now'))" - ) - - with ext.database.session() as conn: - netze = kea.configured_networks(conn, [_netz()]) - assert [str(n) for n in netze] == ["192.168.80.0/24"] - - uebersprungen: list[dict[str, str]] = [] - eintraege = kea.collect_reservations( - conn, only_networks=netze, skipped=uebersprungen - ) - namen = {e["name"] for e in eintraege} - assert namen == {"drinnen"}, namen - assert [e["ip"] for e in uebersprungen] == ["10.99.0.50"] - - # Ohne Eingrenzung bleibt das alte Verhalten -- der Erzeuger filtert selbst. - assert len(kea.collect_reservations(conn)) == 2 - - -def test_device_option_lands_in_its_reservation(tesm_app): - """Ein Wert je Geraet gehoert in dessen Reservierung, nicht in den globalen Teil.""" - from tesm.services import kea - from tesm_core.extension import core - - ext = core(tesm_app) - with ext.database.session() as conn, ext.database.transaction(conn): - device_id = _geraet(conn, "autodarts", "4c:52:62:25:b9:e4", "192.168.80.137") - conn.execute( - "INSERT INTO dhcp_subnets (interface, range_start, range_end, enabled, " - "created_at, updated_at) VALUES ('eth0','192.168.80.100','192.168.80.200',1," - "datetime('now'),datetime('now'))" - ) - option_id = conn.execute( - "INSERT INTO dhcp_options (code, name, type, is_standard) VALUES (225,'url','string',0)" - ).lastrowid - conn.execute( - "INSERT INTO dhcp_option_values (option_id, device_id, value) VALUES (?,?,?)", - (option_id, device_id, "http://autodarts.local"), - ) - conn.execute( - "INSERT INTO dhcp_option_values (option_id, device_id, value) VALUES (?,NULL,?)", - (option_id, "http://global.local"), - ) - - with ext.database.session() as conn: - config, _ = kea.render_config(conn, networks=[_netz()], settings={}) - - subnet = config["Dhcp4"]["subnet4"][0] - reservierung = subnet["reservations"][0] - assert reservierung["ip-address"] == "192.168.80.137" - assert reservierung["option-data"] == [{"name": "url", "data": "http://autodarts.local"}] - # Der globale Wert bleibt global und wird nicht verdraengt. - assert {"name": "url", "data": "http://global.local"} in config["Dhcp4"]["option-data"] - - -def test_reservation_state_from_leases(): - """Wie im Windows-DHCP: eine Reservierung ist aktiv, wenn sie benutzt wird. - - Ohne diese Angabe sah eine nie abgeholte Reservierung genauso aus wie eine - benutzte -- und ein Geraet, das noch auf einer alten Adresse haengt und die - Reservierung deshalb gar nicht uebernimmt, fiel ueberhaupt nicht auf. - """ - import time - - from tesm.services.kea import Lease, annotate_reservations - - jetzt = int(time.time()) - benutzt = Lease("192.168.80.10", "aa:bb:cc:00:00:01", "a", jetzt + 3600, "0") - veraltet = Lease("192.168.80.99", "aa:bb:cc:00:00:03", "c", jetzt + 3600, "0") - abgelaufen = Lease("192.168.80.12", "aa:bb:cc:00:00:04", "d", jetzt - 60, "0") - - eintraege = annotate_reservations( - [ - {"mac": "aa:bb:cc:00:00:01", "ip": "192.168.80.10"}, - {"mac": "aa:bb:cc:00:00:02", "ip": "192.168.80.11"}, - {"mac": "aa:bb:cc:00:00:03", "ip": "192.168.80.30"}, - {"mac": "aa:bb:cc:00:00:04", "ip": "192.168.80.12"}, - ], - [benutzt, veraltet, abgelaufen], - ) - assert [e["state"] for e in eintraege] == [ - "aktiv", - "unbenutzt", - "andere_adresse", - "abgelaufen", - ] - # Der Hinweis erklaert den Zustand, statt ihn nur zu benennen. - assert all(e["state_hint"] for e in eintraege) - assert eintraege[2]["lease"].address == "192.168.80.99" - - -def test_lease_expiry_is_readable(): - """Der rohe Unix-Zeitstempel stand vorher unveraendert in der Tabelle.""" - import time - from datetime import datetime - - from tesm.services.kea import Lease - - jetzt = int(time.time()) - lease = Lease("192.168.80.10", "aa:bb:cc:00:00:01", "a", jetzt + 5400, "0") - erwartet = datetime.fromtimestamp(jetzt + 5400).strftime("%d.%m.%Y %H:%M") - assert lease.expires_text == erwartet - assert lease.remaining_text.startswith("in 1 h") - assert str(jetzt) not in lease.expires_text - - assert Lease("x", "aa:bb:cc:00:00:01", "a", 0, "0").expires_text == "unbegrenzt" - assert Lease("x", "aa:bb:cc:00:00:01", "a", jetzt - 5, "0").remaining_text == "abgelaufen" - - -def test_custom_option_used_only_in_reservation_is_declared(tesm_app): - """Eine eigene Option muss deklariert sein, auch ohne globalen Wert. - - Genau das hat den Dienst umgebracht: der Wert stand nur in einer - Reservierung, die ``option-def`` wurde aber nur fuer Optionen mit globalem - Wert erzeugt. Kea startet dann nicht mehr -- - "definition for the option 'dhcp4.url' does not exist" -- und weil das erst - beim naechsten Start auffiel, lag der DHCP-Server stundenlang. - """ - from tesm.services import kea - from tesm_core.extension import core - - ext = core(tesm_app) - with ext.database.session() as conn, ext.database.transaction(conn): - device_id = _geraet(conn, "autodarts", "4c:52:62:25:b9:e4", "192.168.80.137") - conn.execute( - "INSERT INTO dhcp_subnets (interface, range_start, range_end, enabled, " - "created_at, updated_at) VALUES ('eth0','192.168.80.100','192.168.80.200',1," - "datetime('now'),datetime('now'))" - ) - option_id = conn.execute( - "INSERT INTO dhcp_options (code, name, type, is_standard) " - "VALUES (225,'url','string',0)" - ).lastrowid - # Bewusst *kein* globaler Wert -- nur der Wert fuer dieses eine Geraet. - conn.execute( - "INSERT INTO dhcp_option_values (option_id, device_id, value) VALUES (?,?,?)", - (option_id, device_id, "https://play.autodarts.com"), - ) - - with ext.database.session() as conn: - config, _ = kea.render_config(conn, networks=[_netz()], settings={}) - - dhcp4 = config["Dhcp4"] - benutzt = dhcp4["subnet4"][0]["reservations"][0]["option-data"] - assert benutzt == [{"name": "url", "data": "https://play.autodarts.com"}] - assert "option-data" not in dhcp4, "kein globaler Wert erwartet" - - definitionen = {entry["name"]: entry for entry in dhcp4["option-def"]} - assert "url" in definitionen, "ohne option-def verweigert Kea den Start" - assert definitionen["url"] == { - "name": "url", - "code": 225, - "type": "string", - "space": "dhcp4", - } - - -def test_restart_marks_device_as_restarting(tesm_app): - """Nach einem Neustart muss die Kachel das sofort zeigen. - - Der Zustand wechselte vorher erst beim naechsten Pruefdurchlauf -- Vorgabe - fuenf Minuten. Ein Neustart ueber SSH ist in vierzig Sekunden durch: das - Geraet war weg und wieder da, ohne dass es je jemand gesehen haette. Die - Kachel blieb die ganze Zeit auf "online". - """ - from tesm.services import monitor - from tesm_core.extension import core - - ext = core(tesm_app) - with ext.database.session() as conn, ext.database.transaction(conn): - device_id = _geraet(conn, "kasse", "aa:bb:cc:00:00:07", "192.168.80.7") - conn.execute( - "INSERT INTO device_status (device_id, state, last_change_at) " - "VALUES (?, 'online', datetime('now'))", - (device_id,), - ) - monitor.record_restart( - conn, - device_id=device_id, - action="reboot", - result="ok", - trigger="manuell", - actor="test", - duration_ms=100, - detail="", - method=monitor.METHOD_SSH, - ) - - with ext.database.session() as conn: - zeile = conn.execute( - "SELECT state, restart_count FROM device_status WHERE device_id=?", (device_id,) - ).fetchone() - assert zeile["state"] == monitor.STATE_RESTARTING - assert zeile["restart_count"] == 1 - assert monitor.dashboard_counts(conn)["restarting"] == 1 - - # Solange es schweigt, bleibt es "startet neu" -- nicht "ausgefallen". - vorher, nachher = monitor.record_status( - conn, device_id, monitor.PingResult(ok=False, latency_ms=None, detail="") - ) - assert nachher == monitor.STATE_RESTARTING, "ein laufender Neustart ist kein Ausfall" - - # Antwortet es wieder, ist es online. - _, nachher = monitor.record_status( - conn, device_id, monitor.PingResult(ok=True, latency_ms=3, detail="") - ) - assert nachher == "online" - - -def test_restart_state_gives_up_after_the_grace_period(tesm_app): - """Ein Neustart, der fuenf Minuten dauert, ist keiner mehr.""" - from datetime import datetime, timedelta, timezone - - from tesm.services import monitor - from tesm_core.extension import core - - ext = core(tesm_app) - lange_her = ( - datetime.now(timezone.utc) - timedelta(seconds=monitor.RESTART_GRACE_SECONDS + 60) - ).isoformat() - with ext.database.session() as conn, ext.database.transaction(conn): - device_id = _geraet(conn, "haenger", "aa:bb:cc:00:00:08", "192.168.80.8") - conn.execute( - "INSERT INTO device_status (device_id, state, last_restart_at, last_change_at) " - "VALUES (?, ?, ?, datetime('now'))", - (device_id, monitor.STATE_RESTARTING, lange_her), - ) - - with ext.database.session() as conn: - _, nachher = monitor.record_status( - conn, device_id, monitor.PingResult(ok=False, latency_ms=None, detail="") - ) - assert nachher == "offline" diff --git a/tests/test_webserver.py b/tests/test_webserver.py deleted file mode 100644 index 964e046..0000000 --- a/tests/test_webserver.py +++ /dev/null @@ -1,521 +0,0 @@ -"""Die nginx-Vorlage und die Trennung mehrerer Instanzen auf einem Host. - -Zwei Fehlerklassen werden hier festgenagelt: - -1. Der alias-Fehler des Vorgaengers -- statische Dateien zeigten auf ein - Verzeichnis, das es nicht gab, und die Oberflaeche kam jahrelang ohne - Design. Die erzeugte Datei muss die richtigen Pfade enthalten. -2. Zwei Installationen derselben Anwendung auf einem Host. Solange alles - ueber den ``app_key`` benannt wurde, hat eine Testinstanz beim Speichern - der Webserver-Einstellungen die Site-Datei der Produktion ueberschrieben - -- und beide teilten sich dasselbe Sitzungscookie, weil Cookies keine - Ports unterscheiden. -""" - -from __future__ import annotations - -import re - -import pytest -from tesm_core.system import webserver -from tesm_core.sysops import HELPER_VERBS, Result, SysOps - - -def make_config(**overrides) -> webserver.WebConfig: - base = { - "app_key": "tesm", - "upstream": "127.0.0.1:5000", - "static_root": "/srv/tesm/static", - } - return webserver.WebConfig(**{**base, **overrides}) - - -# --------------------------------------------------------------------------- # -# Nur HTTP -# --------------------------------------------------------------------------- # - - -def test_http_only_has_no_tls_block(): - text = webserver.render(make_config()) - assert "listen 80 default_server;" in text - assert "ssl_certificate" not in text - assert "return 308" not in text - - -def test_static_aliases_point_into_the_installation(): - text = webserver.render(make_config()) - assert "alias /srv/tesm/static/;" in text - assert "alias /srv/tesm/static-core/;" in text - - -def test_proxy_passes_the_host_including_port(): - """``$host`` verwirft den Port -- damit scheitert die Origin-Pruefung. - - Genau daran ist die Anmeldung hinter dem Reverse Proxy auf einem - abweichenden Port real gescheitert. - """ - text = webserver.render(make_config()) - assert "proxy_set_header Host $http_host;" in text - assert "proxy_set_header Host $host;" not in text - assert "proxy_set_header X-Forwarded-Port $server_port;" in text - - -def test_acme_path_is_always_reachable(): - text = webserver.render( - make_config(https_enabled=True, cert_path="/c.pem", key_path="/k.pem") - ) - # Auch bei aktiver Umleitung -- sonst kann Let's Encrypt nie erneuern. - assert "/.well-known/acme-challenge/" in text - challenge = text.index("acme-challenge") - redirect = text.index("return 308") - assert challenge < redirect - - -# --------------------------------------------------------------------------- # -# HTTPS -# --------------------------------------------------------------------------- # - - -def test_https_listens_on_443_and_redirects_80(): - text = webserver.render( - make_config(https_enabled=True, cert_path="/c.pem", key_path="/k.pem") - ) - assert "listen 443 ssl default_server http2;" in text - assert "listen [::]:443 ssl default_server http2;" in text - assert "return 308 https://$host$request_uri;" in text - assert "ssl_protocols TLSv1.2 TLSv1.3;" in text - - -def test_hsts_only_when_asked(): - without = webserver.render( - make_config(https_enabled=True, cert_path="/c.pem", key_path="/k.pem") - ) - assert "Strict-Transport-Security" not in without - - with_hsts = webserver.render( - make_config( - https_enabled=True, hsts_enabled=True, cert_path="/c.pem", key_path="/k.pem" - ) - ) - assert "Strict-Transport-Security" in with_hsts - # HSTS ohne TLS waere ein Selbstschuss: der Browser merkt sich, dass er die - # Seite nur noch ueber HTTPS aufrufen darf. - assert "ssl_certificate" in with_hsts - - -def test_hsts_is_ignored_without_https(): - text = webserver.render(make_config(hsts_enabled=True)) - assert "Strict-Transport-Security" not in text - - -def test_redirect_can_be_switched_off(): - text = webserver.render( - make_config( - https_enabled=True, redirect_http=False, cert_path="/c.pem", key_path="/k.pem" - ) - ) - assert "return 308" not in text - # Dann muss Port 80 die Anwendung weiter bedienen. - assert text.count("proxy_pass http://127.0.0.1:5000;") >= 2 - - -def test_deviating_ports_are_rendered_and_redirect_keeps_the_port(): - text = webserver.render( - make_config( - https_enabled=True, - cert_path="/c.pem", - key_path="/k.pem", - http_port=8080, - https_port=8443, - ) - ) - assert "listen 8080 default_server;" in text - assert "listen 8443 ssl default_server http2;" in text - # Ohne den Port im Ziel liefe die Umleitung ins Leere. - assert "return 308 https://$host:8443$request_uri;" in text - - -def test_standard_port_leaves_no_suffix(): - assert make_config(https_port=443).https_suffix == "" - assert make_config(https_port=8443).https_suffix == ":8443" - - -def test_websocket_location_only_when_configured(): - assert "proxy_set_header Upgrade" not in webserver.render(make_config()) - text = webserver.render(make_config(websocket_path="/ws/")) - assert "location /ws/ {" in text - assert "proxy_read_timeout 3600s;" in text - - -def _location_blocks(text: str) -> list[list[str]]: - """Zerlegt die erzeugte Datei in ihre ``location``-Bloecke.""" - blocks: list[list[str]] = [] - current: list[str] | None = None - for line in text.splitlines(): - stripped = line.strip() - if stripped.startswith("location ") and stripped.endswith("{"): - current = [] - elif stripped == "}" and current is not None: - blocks.append(current) - current = None - elif current is not None and stripped and not stripped.startswith("#"): - current.append(stripped) - return blocks - - -@pytest.mark.parametrize( - "config", - [ - make_config(websocket_path="/ws/"), - make_config( - websocket_path="/ws/", https_enabled=True, cert_path="/c.pem", key_path="/k.pem" - ), - make_config(https_enabled=True, hsts_enabled=True, cert_path="/c.pem", key_path="/k.pem"), - ], -) -def test_no_directive_appears_twice_in_one_block(config: webserver.WebConfig): - """nginx weist eine doppelte Direktive im selben Block ab. - - Auf POETEST real passiert: der Websocket-Block erbte ``proxy_read_timeout - 120s`` aus dem gemeinsamen Teil und setzte danach ``3600s``. nginx meldete - "directive is duplicate" und liess sich nicht mehr starten -- nicht nur - fuer diese Site, sondern fuer alle. - """ - for block in _location_blocks(webserver.render(config)): - directives = [line.split()[0] for line in block if line.endswith(";")] - repeated = { - name - for name in directives - # Wiederholung ist nur bei den Direktiven zulaessig, die nginx als - # Liste versteht. - if directives.count(name) > 1 and name not in {"proxy_set_header", "add_header"} - } - assert not repeated, f"Mehrfach im selben Block: {sorted(repeated)}" - - -def test_websocket_block_keeps_the_long_timeout(): - """Die lange Wartezeit ist der Zweck des Blocks -- sie darf nicht verlorengehen.""" - blocks = { - block[0]: block - for block in _location_blocks(webserver.render(make_config(websocket_path="/ws/"))) - } - websocket = next(b for b in blocks.values() if "proxy_set_header Upgrade $http_upgrade;" in b) - assert "proxy_read_timeout 3600s;" in websocket - assert "proxy_read_timeout 120s;" not in websocket - - -def test_http2_uses_the_old_spelling_by_default(): - """``http2 on;`` kennt erst nginx 1.25.1. - - Auf POETEST (nginx 1.24) fuehrte die neue Direktive zu "unknown directive" - -- und damit zu einem Webserver, der sich nicht mehr starten liess. Die - alte Schreibweise laeuft ueberall. - """ - text = webserver.render( - make_config(https_enabled=True, cert_path="/c.pem", key_path="/k.pem") - ) - assert "listen 443 ssl default_server http2;" in text - assert "http2 on;" not in text - - -def test_http2_directive_on_request(): - text = webserver.render( - make_config( - https_enabled=True, cert_path="/c.pem", key_path="/k.pem", http2_directive=True - ) - ) - assert " http2 on;" in text - assert "listen 443 ssl default_server;" in text - assert "ssl http2;" not in text - - -def test_stapling_only_with_a_real_certificate(): - """Ein selbstsigniertes Zertifikat hat keinen OCSP-Responder.""" - self_signed = webserver.render( - make_config(https_enabled=True, cert_path="/c.pem", key_path="/k.pem") - ) - assert "ssl_stapling" not in self_signed - - public = webserver.render( - make_config( - https_enabled=True, - domain="tesm.example.org", - cert_path="/c.pem", - key_path="/k.pem", - ) - ) - assert "ssl_stapling on;" in public - - -# --------------------------------------------------------------------------- # -# Domain -# --------------------------------------------------------------------------- # - - -@pytest.mark.parametrize( - "value", ["tesm.example.org", "a.b.c.de", "TESM.Example.ORG", "tesm.example.org."] -) -def test_valid_domains(value: str): - assert webserver.validate_domain(value) == value.lower().rstrip(".") - - -@pytest.mark.parametrize( - "value", ["kein-punkt", "-start.de", "ende-.de", "raum .de", "a..b", "http://x.de"] -) -def test_invalid_domains(value: str): - with pytest.raises(ValueError): - webserver.validate_domain(value) - - -def test_empty_domain_becomes_catch_all(): - assert make_config().server_name == "_" - assert make_config(domain="tesm.example.org").server_name == "tesm.example.org" - - -def test_only_the_nameless_site_claims_default_server(): - """Sonst entscheidet der Dateiname in sites-enabled, wer die Vorgabe wird. - - ``server_name _;`` ist bei nginx kein Sonderfall -- ohne - ``default_server`` gewinnt schlicht die zuerst geladene Datei. Bei zwei - Anwendungen auf einem Host waere das reiner Zufall. - """ - nameless = webserver.render(make_config()) - assert "listen 80 default_server;" in nameless - - named = webserver.render(make_config(domain="lizenz.example.org")) - assert "default_server" not in named - assert "server_name lizenz.example.org;" in named - - -# --------------------------------------------------------------------------- # -# Selbstsigniertes Zertifikat -# --------------------------------------------------------------------------- # - - -class FakeSysOps: - """Merkt sich den Aufruf, statt den Helfer wirklich zu starten.""" - - def __init__(self, result: Result) -> None: - self.result = result - self.calls: list[tuple[str, ...]] = [] - self.available = True - self.site_key = "tesm-opus" - - def issue_self_signed(self, common_name: str, *alt_names: str) -> Result: - self.calls.append((common_name, *alt_names)) - return self.result - - -def test_self_signed_returns_both_paths(): - sysops = FakeSysOps(Result(True, "/etc/tesm/certs/tesm/fullchain.pem\n/etc/tesm/certs/tesm/privkey.pem\n")) - result = webserver.create_self_signed( - sysops, common_name="poetest", alt_names=["poetest.local", "192.168.82.202"] - ) - assert result["ok"] - assert result["cert_path"].endswith("fullchain.pem") - assert result["key_path"].endswith("privkey.pem") - assert sysops.calls == [("poetest", "poetest.local", "192.168.82.202")] - - -def test_self_signed_rejects_a_hostile_name(): - """Der Name landet in einem openssl-Argument -- er wird hier geprueft.""" - sysops = FakeSysOps(Result(True, "a\nb\n")) - for hostile in ("a b", "a;rm -rf /", "$(whoami)", "../../etc", ""): - result = webserver.create_self_signed(sysops, common_name=hostile) - assert not result["ok"], hostile - assert sysops.calls == [] - - -def test_self_signed_drops_unusable_alternative_names(): - sysops = FakeSysOps(Result(True, "a\nb\n")) - webserver.create_self_signed( - sysops, common_name="poetest", alt_names=["poetest", "", "bad name", "ok.local"] - ) - assert sysops.calls == [("poetest", "ok.local")] - - -def test_self_signed_reports_a_failing_helper(): - sysops = FakeSysOps(Result(False, "", "openssl fehlt", 2)) - result = webserver.create_self_signed(sysops, common_name="poetest") - assert not result["ok"] - assert "openssl fehlt" in result["detail"] - - -def test_self_signed_rejects_an_unexpected_answer(): - sysops = FakeSysOps(Result(True, "nur eine Zeile\n")) - result = webserver.create_self_signed(sysops, common_name="poetest") - assert not result["ok"] - - -def test_certificate_info_survives_an_unreadable_file(tmp_path, monkeypatch): - """Ein unlesbares Zertifikat darf die Einstellungsseite nicht abwerfen. - - Auf POETEST real passiert: das Zertifikatsverzeichnis war 0750 root:root, - der Dienstbenutzer bekam beim blossen ``stat`` einen PermissionError -- und - die Seite "Webserver und TLS" antwortete mit HTTP 500. - """ - target = tmp_path / "fullchain.pem" - target.write_text("egal", encoding="utf-8") - - def deny(*_args, **_kwargs): - raise PermissionError(13, "Permission denied") - - monkeypatch.setattr("pathlib.Path.is_file", deny) - info = webserver.certificate_info(str(target)) - assert info["present"] is True - assert "nicht lesen" in info["error"] - - -def test_certificate_info_reports_a_missing_file(): - assert webserver.certificate_info("/gibt/es/nicht.pem") == {"present": False} - - -def test_certificate_info_reports_garbage_without_raising(tmp_path): - target = tmp_path / "fullchain.pem" - target.write_text("kein Zertifikat", encoding="utf-8") - info = webserver.certificate_info(str(target)) - assert info["present"] is True - assert info["error"] - - -def test_local_names_are_usable_in_a_certificate(): - names = webserver.local_names() - assert names, "Es sollte mindestens der Hostname gefunden werden." - for name in names: - assert re.fullmatch(r"[A-Za-z0-9]([A-Za-z0-9.-]{0,251}[A-Za-z0-9])?", name), name - - -# --------------------------------------------------------------------------- # -# Instanztrennung -# --------------------------------------------------------------------------- # - - -def test_apply_config_is_one_call_with_rollback(): - """Getrennte Aufrufe liessen eine abgelehnte Konfiguration aktiviert liegen. - - ``nginx-write`` verlinkt sofort nach ``sites-enabled``. Schlug danach - ``nginx -t`` fehl, blieb die kaputte Datei aktiv -- der laufende nginx - merkte nichts, aber der naechste Reload durch irgendwen legte den - Webserver lahm. Die Uebernahme muss deshalb *eine* privilegierte Operation - sein, die sich selbst zuruecknimmt. - """ - calls: list[str] = [] - - class Recording(SysOps): - def run(self, verb: str, *args: str, stdin: str = "", timeout: int | None = None) -> Result: - calls.append(verb) - return Result(True, "uebernommen") - - result = webserver.apply_config(Recording(app_key="tesm"), "server {}") - assert result["ok"] - assert calls == ["nginx-apply"] - assert "nginx-write" not in calls - - -def test_apply_config_reports_a_rejection_without_claiming_success(): - class Failing(SysOps): - def run(self, verb: str, *args: str, stdin: str = "", timeout: int | None = None) -> Result: - return Result(False, "", "unknown directive \"http2\"", 2) - - result = webserver.apply_config(Failing(app_key="tesm"), "server {}") - assert not result["ok"] - assert "alte Stand" in result["message"] - assert "http2" in result["detail"] - - -def test_sysops_writes_system_files_under_the_site_key(): - """Sonst ueberschreibt eine Testinstanz die Systemdateien der Produktion.""" - calls: list[tuple[str, ...]] = [] - - class Recording(SysOps): - def run(self, verb: str, *args: str, stdin: str = "", timeout: int | None = None) -> Result: - calls.append((verb, *args)) - return Result(True) - - sysops = Recording(app_key="tesm", site_key="tesm-opus") - sysops.write_nginx("x") - sysops.write_netplan("x") - sysops.write_logrotate("x") - sysops.issue_self_signed("poetest") - - assert calls == [ - ("nginx-write", "tesm-opus"), - ("netplan-write", "tesm-opus"), - ("logrotate-write", "tesm-opus"), - ("selfsigned-issue", "tesm-opus", "poetest"), - ] - - -def test_upstream_follows_the_configured_bind_address(monkeypatch): - """Die nginx-Site muss auf *diese* Instanz zeigen, nicht auf Port 5000. - - Mit fest verdrahteter 5000 erzeugte eine Instanz auf Port 5100 eine - Konfiguration, die Anfragen an die *andere* Installation weiterreichte -- - auf POETEST reproduziert: HTTPS stand, lieferte aber die alte Anwendung. - """ - from tesm_core.config import load_core_config - - monkeypatch.setenv("TESM_BIND", "127.0.0.1:5100") - config = load_core_config(app_key="tesm", app_name="TESM", env_prefix="TESM_") - assert config.bind == "127.0.0.1:5100" - - text = webserver.render(make_config(upstream=config.bind)) - assert "proxy_pass http://127.0.0.1:5100;" in text - assert "127.0.0.1:5000" not in text - - -def test_bind_address_is_validated(monkeypatch): - from tesm_core.config import load_core_config - - monkeypatch.setenv("TESM_BIND", "127.0.0.1:5100; rm -rf /") - with pytest.raises(ValueError): - load_core_config(app_key="tesm", app_name="TESM", env_prefix="TESM_") - - -def test_site_key_is_validated(monkeypatch): - from tesm_core.config import load_core_config - - monkeypatch.setenv("TESM_SITE_KEY", "../../etc/nginx") - with pytest.raises(ValueError): - load_core_config(app_key="tesm", app_name="TESM", env_prefix="TESM_") - - -def test_site_key_defaults_to_the_app_key(): - assert SysOps(app_key="tesm").site_key == "tesm" - - -def test_self_signed_verb_is_known(): - assert "selfsigned-issue" in HELPER_VERBS - - -def test_service_state_finds_unit_without_suffix(monkeypatch): - """``list-unit-files`` braucht ein Muster mit Endung. - - Ohne sie fand die Abfrage nichts und der Zustand lautete - "installiert: nein" -- auch bei laufendem Dienst. Auf der DHCP-Seite lud - das zum Klick auf "Kea installieren" ein. - """ - import tesm_core.sysops as sysops_modul - from tesm_core.sysops import Result, SysOps - - # Ohne systemctl bricht die Abfrage vorher ab -- auf dem Entwicklungsrechner - # gibt es keines. - monkeypatch.setattr(sysops_modul.shutil, "which", lambda name: "/bin/" + name) - - gesehen: list[list[str]] = [] - - class Attrappe(SysOps): - def __init__(self): - pass - - def read_command(self, argv): - gesehen.append(list(argv)) - if "list-unit-files" in argv: - treffer = "kea-dhcp4-server.service" in argv - return Result(True, "kea-dhcp4-server.service enabled" if treffer else "", "") - return Result(True, "active", "") - - zustand = Attrappe().service_state("kea-dhcp4-server") - assert zustand["installed"] == "ja", gesehen - # Eine bereits vollstaendige Unit bleibt unveraendert. - assert Attrappe().service_state("kea-dhcp4-server.service")["installed"] == "ja"