Reaktion auf mehrere Bug-Reports und Feature-Wuensche zum bestehenden Ticket-System (v1.1.0): 'Logs gehen nicht', 'Ticketverwaltung fehlt', 'Lizenzen koennen nicht vom Dashboard geloescht werden', plus eine Reihe konkreter neuer Anforderungen. ## Bugfixes - /logs: behob einen Absturz (jinja2.exceptions.UndefinedError: 'global_check_interval' is undefined) UND die eigentliche Ursache dahinter -- logs.html war unveraendert aus dem TESM-Fork uebernommen (PoE-Geraete-Polling-Text/-Farblogik, ein Template-Feld, das app.py nie befuellt hat) und ausserdem schrieb auf diesem Server ueberhaupt nichts in live.log (kein poe.sh-Aequivalent vorhanden). Live-Log zeigt jetzt echte Ticket-/Lizenzaktionen, mit passendem Hinweistext und Faerbung. - Lizenz-Loeschung war bereits vorhanden, aber nur ueber Ticket- -> Lizenz-Detailseite erreichbar -- jetzt zusaetzlich inline in der Lizenzhistorie jedes Tickets. ## Ticketverwaltung (neu: eigene Seite /tickets) - Volle CRUD: Liste mit Suche, Bearbeiten (Typ/Module/Laufzeit/Lifetime, wirkt nur auf die naechste Ausstellung), Loeschen (nur wenn keine offene Lizenz mehr besteht, dann inkl. Historie). - Dashboard (/) zeigt seitdem NICHT mehr die Ticket-Tabelle, sondern eine rein informative Kachel-Uebersicht der aktuell AKTIVEN Lizenzen, exakt im Kachel-Stil von TESMs eigenem Geraete-Dashboard: Ampelfarben (gruen >90 Tage, orange 30-90, rot <30/abgelaufen), gruppiert, Firma + Hostname der Kundeninstanz. Hostname kommt neu vom TESM-Client per Activate/ Heartbeat (srv/tesm/licensing.py, abwaerts-kompatibel: alte Clients ohne das Feld ueberschreiben nie einen bereits bekannten Hostnamen). ## Kundenstammdaten - Neue Felder: Strasse/Hausnummer/PLZ/Ort/Ansprechpartner. E-Mail ist beim Anlegen jetzt Pflicht; beim Bearbeiten bewusst nur validiert, wenn ausgefuellt (sonst waeren Bestandskunden aus der Zeit vor dieser Umstellung fuer JEDE Aenderung blockiert gewesen). ## Kundenportal (neu, per Magic-Link, kein Login) - /portal/<token>: zeigt einem Kunden ALLE seine Tickets/Lizenzen auf einen Blick (ergaenzt den bestehenden Pro-Ticket-Link /self-service/<ticket_id>) und erlaubt ihm, seine eigenen Kontakt-/ Anschriftdaten selbst zu pflegen -- Firmenname und kommerzielle Ticket-Eckdaten bleiben bewusst admin-verwaltet. token = eigene Spalte license_customers.portal_token, rueckwirkend fuer Bestandskunden erzeugt. ## Lifetime-Lizenzen - Neues Ticket-Flag 'Lifetime': ignoriert die Laufzeit fuer jede REGULAERE Ausstellung (Sentinel-Ablaufdatum 2099-12-31, licensing. LIFETIME_EXPIRES_AT) -- eine zusaetzlich erzeugte Trial-Lizenz aus demselben Ticket bleibt immer die normale 30-Tage-Variante. Trial ALS TICKET-GRUNDTYP + Lifetime wird serverseitig verhindert (haette eine dauerhafte, nie ablaufende Lizenz ganz ohne Module ergeben). ## Absicherung nach Review Ein Adversarial-Review-Durchlauf ueber den kompletten Diff deckte vor dem Deploy zusaetzlich auf und wurde behoben: - TOCTOU-Race: zwei nahezu gleichzeitige 'Lizenz erstellen'-Anfragen fuer dasselbe Ticket (z.B. Doppelklick im Self-Service) konnten beide durchkommen. Jetzt durch einen partiellen Unique-Index (idx_one_open_license_per_ticket) auf DB-Ebene ausgeschlossen; die zweite Anfrage bekommt sauber die bereits erstellte Lizenz zurueck statt eine zweite zu erzeugen. - Analoger Unique-Index auf license_customers.portal_token als Backstop fuer den neuen Magic-Link. - tickets.html nutzte noch die alte, feste '<=30 Tage'-Schwelle statt der gemeinsamen Ampelfarbe -- Lizenzen im 30-90-Tage-Bereich blieben dort unmarkiert, obwohl das Dashboard sie schon als orange zeigte. SCHEMA_VERSION 2 -> 3 (neue Spalten/Tabellen, ueber install.sh mit Backup+Health-Check abgesichert). Verifiziert: vollstaendige Migration gegen eine echte Kopie der Live-Datenbank, anschliessend 22 Funktionstests direkt gegen die echten Routen (Validierung, Lifetime-Ausstellung, Sperr-/Kaskadenregeln bei Loeschungen, Kundenportal inkl. ungueltiger Eingaben) sowie ein Playwright- Seitenladetest ohne Konsolenfehler -- beides nach den Review-Fixes erneut komplett durchlaufen, inkl. direktem Nachweis, dass der neue Unique-Index eine doppelte offene Lizenz pro Ticket tatsaechlich auf DB-Ebene ablehnt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TESM-Lizenzserver
Master-Lizenzserver für TESM (TimEShepManager) — verwaltet Kunden und deren Lizenzen, statt Netzwerkgeräte. Ursprünglich als Teil des TESM-Hauptrepos gebaut (gleiche Codebasis: Login/Benutzer/Gruppen, LDAP/AD, NGINX- und Systemeinstellungen, Live-Log/Verlauf/Auditlog, Im-/Export-Grundgerüst, das komplette Rechtesystem) und in dieses eigene Repo ausgelagert.
Das Projekt ist ausschließlich für Linux ausgelegt (Zielsystem: eine Linux-VM, identisches Deployment-Muster wie TESM selbst).
Kernfunktionen
- Kunden-/Lizenzübersicht als Dashboard (Typ, Module, Ablaufdatum, Aktivierungsstatus, letzter Heartbeat)
- Lizenz-Ausstellung: Trial/Standard/Custom/Enterprise, frei wählbare Gültigkeitsdauer, bei Custom einzeln wählbare Module
- Ed25519-signierte Lizenzdateien (siehe
srv/tesm-license/licensing.py) — dieselbe Datei existiert byte-identisch im TESM-Repo, da Client (TESM) und Master (dieses Repo) exakt dasselbe Signier-/ Verifikationsprotokoll sprechen müssen - Online-Aktivierung/-Deaktivierung/-Heartbeat über eine schlanke
JSON-API (
/api/activate,/api/deactivate,/api/heartbeat, unauthentifiziert per Design — die Signatur der Anfrage selbst ist der Berechtigungsnachweis) - Offline-Fallback: dieselbe Aktivierungs-/Deaktivierungs-/Heartbeat- Logik auch als manuell kopierbarer Code für Kunden ohne Netzwerkzugriff auf den Lizenzserver
- E-Mail-Versand ausgestellter Lizenzen per Microsoft Graph (Client-Credentials-Flow, keine zusätzliche Abhängigkeit) inkl. Einrichtungsanleitung und Verbindungstest in der GUI
- Eigene Bootstrap-Lizenz des Masters selbst (
create_master_license.py, rein lokal, kein externer Super-Master nötig)
Installation
sudo ./install.sh
Richtet System-Pakete, die Flask-App (systemd: tesm-license.service)
sowie nginx als Reverse-Proxy unter /srv/tesm-license ein. Erkennt
selbstständig, ob dort bereits eine Installation existiert, und
aktualisiert sie entsprechend (In-Place bei unverändertem
Datenbank-Schema, sonst mit automatischem Backup + Health-Check +
Rückroll bei Fehlschlag — siehe Kommentarkopf in install.sh).
Nach der Erstinstallation:
sudo /srv/tesm-license/venv/bin/python3 /srv/tesm-license/create_admin.py
sudo /srv/tesm-license/venv/bin/python3 /srv/tesm-license/create_master_license.py
Update (von einem bereits installierten System aus)
sudo ./update.sh
# oder für eine bestimmte Version statt "latest":
TESM_RELEASE_TAG=v1.0.0 sudo -E ./update.sh
Lädt das aktuelle Gitea-Release herunter und übergibt an install.sh.
Zusammenspiel mit TESM
Eine TESM-Instanz aktiviert sich gegen genau einen Lizenzserver
(master_endpoint, in der Lizenzdatei hinterlegt). Ein Protokoll, zwei
Transportwege: online automatisch per HTTPS/JSON, offline als manuell
auszutauschender Code — beide Seiten nutzen dieselben Funktionen aus
licensing.py. Details zum kryptografischen Format und Ablauf siehe die
Docstrings in srv/tesm-license/licensing.py bzw. dem identischen Modul
im TESM-Repo.