635722089edae8db94e36b59a4740d0c91868df7
126
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
635722089e |
rename.sh: aus einer Instanz die Hauptinstallation machen
Die laufenden Installationen hiessen "tesm-opus" und "tesm-license-opus" -- ein Instanzname aus der Zeit, in der sie neben der Fassung 1.x getestet wurden. Jetzt heissen sie "tesm" und "tesm-license". Umbenennen ist mehr als ein verschobenes Verzeichnis, und drei Stellen sind leicht zu uebersehen: * SITE_KEY in der Umgebungsdatei -- daraus entsteht der Name des Sitzungscookies. Bleibt er alt, heisst das Cookie weiter wie die alte Instanz: unauffaellig und falsch. * web_cert_path/web_key_path/web_acme_webroot in der Datenbank enthalten den Namen. Ein Zertifikat unter dem alten Pfad findet nginx nicht mehr. * server_name der nginx-Site. install.sh schreibt die Datei aus seiner Vorlage neu, sobald sich die alias-Pfade aendern -- und die aendern sich hier zwangslaeufig. Ohne den Namen aus der Datenbank stehen danach zwei Sites mit "server_name _" auf Port 80, und die erste gewinnt. web-setup braucht dafuer --apply; ohne ihn speichert es nur. Zwei Fehler hat erst der Lauf am System gezeigt: * Das venv wandert nicht mit. Seine Konsolenskripte tragen den absoluten Pfad im Shebang, und danach zeigt venv/bin/gunicorn auf einen Interpreter, den es nicht mehr gibt -- systemd meldet "No such file or directory" fuer eine Datei, die sichtbar da ist. Es wird jetzt verworfen und neu gebaut. * Den Zielnamen belegt nicht nur /srv/<name>, sondern auch /var/log/<name>, der ACME-Pfad und die logrotate-Datei. Der Umzug brach mitten drin ab. Jetzt wandert alles, was den Namen belegt, in ein Archiv -- geloescht wird nichts, die Units und die Site der alten Fassung liegen darin unter systemd-alt/. Ein Test vergleicht die Pfade, die install.sh unter dem Namen anlegt, mit denen, die rename.sh kennt. Genau diese Luecke war der zweite Fehler; ohne den Test faellt der naechste erst wieder auf einem Server auf. Version 2.0.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>v2.0.6 |
||
|
|
6dff1ed137 |
Banner: Einzahl, wo eines gemeint ist
"1 Clients online" liest man jeden Tag einmal zu oft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>v2.0.5 |
||
|
|
b4529a0e31 |
Anmeldebanner wieder in Betrieb -- ohne die Anwendung zu starten
Das Banner der Vorgaengerfassung hing an /srv/tesm/motd_banner.py und importierte dafuer die ganze Flask-Anwendung samt deren venv. Nach dem Umstieg zeigte es damit die Zahlen einer Datenbank, die niemand mehr fuellt -- eingefroren, aber unverdaechtig. Aufgefallen ist es erst, als der alte Ueberwachungsdienst abgeschaltet wurde. deploy/tesm-motd loest das anders: **nur Standardbibliothek**, kein venv, kein Import der Anwendung, die Datenbank wird nur lesend geoeffnet (mode=ro). Ein Lauf dauert Millisekunden statt eines Flask-Starts. install.sh legt das Skript nach /usr/local/lib/tesm/ und schreibt /etc/update-motd.d/50-<name>, abgesichert mit "timeout 3" und "|| true" -- ein Banner darf eine Anmeldung nie blockieren. Ein Banner der Vorgaengerfassung wird entfernt, aber nur, wenn es genau auf motd_banner.py zeigt. Damit es nicht zwei Wahrheiten ueber "online" gibt, steht die Zaehlung der Statusuebersicht jetzt als Konstante DASHBOARD_COUNT_SQL in monitor.py; das Banner traegt sie wortgleich, und ein Hygienetest vergleicht beide. Dasselbe fuer die Zeile unter der Wortmarke. Ein weiterer Test verbietet dem Banner jeden Import ausserhalb der Standardbibliothek -- genau dieser Import war die Ursache. Der Lizenzserver bekommt sein eigenes Banner (Kunden und ausgestellte Lizenzen). Eine Tagline hat er bewusst nicht: die Funktionsliste von TESM waere dort falsch, und eine erfundene waere schlimmer als keine. Version 2.0.5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
313eb4bbf9 |
Leerlaufabmeldung: die zweite Schreibstelle und ein Tab, der das alte Skript faehrt
Die Abmeldung hat weiterhin nicht ausgeloest. Zwei Gruende, beide fuer sich ausreichend: * **save_session** schreibt ``idle_expires_at`` bei jeder Anfrage, die die Sitzung anfasst -- ein neu erzeugtes CSRF-Token genuegt. Die Pruefung in ``open_session`` lief damit ins Leere. Sitzungsdaten werden weiter gespeichert, nur die Fristen bleiben stehen. Dieselbe Stelle benutzte ausserdem ``idle_minutes`` statt der einstellbaren Zeit; bei der Vorgabe von dreissig Minuten fiel das nicht auf. * **Der Kopf allein reicht nicht.** Er setzt voraus, dass der Browser das aktuelle Skript faehrt -- ein Tab, der seit gestern Abend offen ist, faehrt das alte und schickt ihn nicht. Genau dieser Tab ist der Fall, um den es geht. Der Server weiss es ohne Zutun des Browsers: die Endpunkte, die eine Kachel oder ein Protokoll nachlaedt, sind als Aktualisierungsausschnitt gekennzeichnet (``@fragment``) und gelten damit immer als automatisch. ``request.endpoint`` steht in ``open_session`` noch nicht -- Flask oeffnet die Sitzung vor dem Routing --, deshalb sieht die Erkennung dort selbst nach. Dass es auf dem Lizenzserver zuverlaessig funktionierte, war der Hinweis: dort pollt keine Kachel. Dazu ein Kopf ``X-Session-Expired`` fuer Seiten, die auch oeffentlich antworten. Die Statusuebersicht liefert nach dem Ablauf eine 200 mit den oeffentlichen Kacheln -- am Status ist der Verlust der Sitzung also nicht zu erkennen, und der offene Tab zeigte weiter eine Oberflaeche, als sei alles in Ordnung. Jetzt laedt er neu und zeigt die Anmeldemaske. Version 2.0.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>v2.0.4 |
||
|
|
51ae39dd53 |
Upgrade von 1.x, Leerlaufabmeldung, Mitschrift im Audit
upgrade.sh -- ein Weg vom Stand SONNET5 auf diese Fassung, mit Daten. Der Ablauf braucht keine Schalter: alte Installation sichern, abschalten, neu installieren, exportieren, uebersetzen, einspielen. Die Sicherung steht bewusst vor dem Export -- ohne --instance installiert install.sh in dasselbe Verzeichnis, und wer erst exportiert und dann sichert, sichert im schlechtesten Fall nichts mehr. Gelesen wird nur aus der Kopie. Dafuer zwei neue Befehle: "legacy-export" liest eine alte Installation unmittelbar (der Vorgaenger kann nur ueber die Weboberflaeche exportieren -- fuer ein Upgrade der falsche Weg, und unmoeglich, wenn die alte Anwendung nicht mehr startet) und schreibt eine Datei im alten Format, die deshalb auch als Rueckweg taugt. "legacy-import" liest eine Datei im neuen Format ein, ueber transfer.apply_import -- dieselbe Funktion wie die Seite "Sicherung", kein zweiter Schreibpfad. Am Lizenzserver hat der Lauf gegen echte Altdaten zwei Fehler gezeigt, die jede Uebernahme unmoeglich machten: * Der alte Signaturschluessel ist URL-sicher kodiert (- und _). Das neue Protokoll prueft Base64 streng -- richtig so, aber beim Lesen einer alten Datei brach die Uebernahme mit einem Traceback ueber "kein gueltiges Base64" ab. Umkodiert wird jetzt an der Grenze zum Altformat; der kanonische Decoder bleibt streng. * Der Anbieterblock wurde aus den Einstellungen der *neuen* Installation gebaut, und ohne Endpunkt brach die Uebernahme ab. Bei einem Upgrade ist die neue Installation frisch -- dort steht nichts. Endpunkt und Anbieterdaten kommen jetzt aus der alten Installation, aber nur dort, wo neu nichts steht. Der alte Signaturschluessel wird uebernommen: sonst muesste jeder Kunde einen Ausstellerwechsel bestaetigen. Leerlaufabmeldung: sie hat nie ausgeloest. Ein offener Tab fragt seine Kacheln alle 15 Sekunden neu ab, und jede Abfrage verlaengerte das Leerlauffenster -- nachweisbar an einer Sitzung, deren last_seen_at eine Minute alt war, obwohl seit zehn Stunden niemand am Rechner sass. Der Client kennzeichnet automatische Anfragen jetzt mit einem Kopf, der Server verlaengert daraufhin nichts, und last_seen_at bleibt, was der Name sagt. Faellt die Sitzung waehrend des Pollens, antwortet der Server mit 401 statt mit einer Umleitung: der Tab laedt neu und zeigt die Anmeldemaske, statt stumm mit alten Zahlen stehenzubleiben. Im Audit ist der Name der Mitschrift jetzt ein Verweis auf die Datei -- es ist derselbe Weg wie zuvor, nur einen Klick weit. Verlinkt wird nur, wenn die Datei existiert und der Betrachter Protokolle lesen darf; der Rest des Eintrags wird maskiert, denn ein Auditeintrag enthaelt Fremdeingaben. Ausserdem: Version 2.0.3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>v2.0.3 |
||
|
|
a8bd5763fb |
Aktualisierung: zwei Pruefungen, die nie zugegriffen haben
Der Lauf gegen die echte Installation hat zwei Fehler gezeigt, die den
Aktualisierungsweg fuer jede Version unbrauchbar machten:
* Die Suche nach install.sh im entpackten Paket lief mit "-maxdepth 2".
release.sh packt mit dem Praefix "<name>/", das Skript liegt also drei
Ebenen tief -- gefunden wurde nie etwas, und die Meldung schob es auf den
Download ("Im Paket fehlt deploy/install.sh").
* Die CRLF-Pruefung rief "file" auf. Das Paket gehoert nicht zur
Grundinstallation eines Servers; fehlt es, endete die Pipeline in "command
not found", grep bekam nichts zu sehen, der if-Zweig wurde nicht betreten --
und genau der Fehler, den die Pruefung abfangen soll, ging unbemerkt durch.
Der Wagenruecklauf wird jetzt ohne Zusatzpaket gesucht, mit "-U": unter Linux
ohne Wirkung, auf einer Windows-Buildmaschine entscheidend, weil grep dort
den Wagenruecklauf beim Lesen entfernt.
Beide Pruefungen fuehren die echten Zeilen aus dem Skript aus -- die eine
gegen den Verzeichnisaufbau, den release.sh erzeugt, die andere gegen zwei
Dateien mit und ohne Wagenruecklauf.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v2.0.2
|
||
|
|
d6ba98782e |
Release-Pakete gehoeren nicht ins Repository
Der gebaute Tarball lag im Arbeitsbaum und wanderte in den Commit; das naechste Paket enthielt daraufhin das vorige -- doppelte Groesse, verschachtelter Inhalt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
47ab97a0ac |
Aktualisierungsweg gangbar machen
Drei Fehler, die erst beim Aktualisieren auffallen -- also genau dann, wenn niemand sie sehen will: * update.sh zeigte auf "gitea.example.invalid" und den Besitzer "wis". Auf keinem Host konnte das funktionieren, und die Doku erwaehnte die Variablen nicht, mit denen man es haette umlenken koennen. Die Vorgaben zeigen jetzt dorthin, wo die Releases liegen; das Repository heisst wie die Anwendung. * update.sh gab nichts an install.sh weiter. Eine benannte Instanz war damit nicht aktualisierbar: ohne --instance entsteht neben "tesm-opus" eine zweite Installation namens "tesm", mit eigener Datenbank und eigener nginx-Site. * install.sh setzte bei einer Instanz die Ports auf 8080/8443 zurueck, auch wenn sie seit der Installation auf Port 80 lief. Ein Reverse Proxy davor haette danach ins Leere gezeigt. Bestehende Ports werden jetzt aus Umgebungsdatei und nginx-Site uebernommen -- dieselbe Regel wie beim Secure-Flag: eine Aktualisierung nimmt nichts weg, was nicht ausdruecklich neu gesetzt wird. Zwei Tests fuehren dazu die echten Zeilen aus den Skripten aus, statt sie nachzubauen. Beide fallen ohne die Korrektur durch. Ausserdem: die vier subprocess-Aufrufe im Textmodus nageln die Kodierung auf UTF-8 fest. "text=True" allein nimmt die Locale des Prozesses -- unter systemd haeufig C/ASCII --, und das Lesen bricht ab, sobald ein Werkzeug einen Umlaut ausgibt. Der Fehler entstand im Leser-Thread von subprocess und damit weit weg von seiner Ursache; im Testlauf war er als Warnung sichtbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fbbf8eb87b |
Umlaute in der Oberflaeche, Sitzungskopplung, Statusanzeigen
Echte Umlaute in allen Texten der Oberflaeche und in den Markdown-Dateien. Die Einbindung von Dateifreigaben haengt jetzt an der Sitzung: wird sie widerrufen oder laeuft sie ab, verschwinden die Freigaben mit ihr. Neu einstellbar ist die automatische Abmeldung bei Leerlauf (Vorgabe 30 Minuten). Ein Geraet, das nicht antwortet, bietet nur noch "Einschalten (PoE)" an -- Neustart ueber SSH oder RPC braucht ein erreichbares Geraet. Der Zeitgeber der naechsten Pruefung und die Restlaufzeit der Lizenz stehen jetzt oben in der Kopfzeile; der doppelte Knopf darunter ist weg. Millisekunden werden ab einer Sekunde als Sekunden und ab einer Minute als m:ss dargestellt. Ausserdem: Release-Tarball nicht mehr im Repository -- er haengt am Release. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9a760274f8 |
Herkunft eines gepinnten Ausstellers in Klartext
In der Spalte "Quelle" stand das Kuerzel des Protokolls: "tofu". Das ist Fachjargon (Trust On First Use) und als Wert in einer Tabelle unbrauchbar -- es sieht aus wie ein Tippfehler. Jetzt: "Erstkontakt" (beim ersten Einspielen automatisch uebernommen) und "Bestaetigt" (Ausstellerwechsel von einem Administrator freigegeben). Die Uebersetzung liegt in tesm_core.ui als ISSUER_SOURCES mit zwei Jinja-Filtern, nicht in zwei Vorlagen: der Lizenzserver zeigt dieselbe Angabe unter "Eigene Lizenz", und zwei Texte fuer denselben Sachverhalt laufen auseinander. Unbekannte Werte bleiben unveraendert sichtbar statt hinter einem Platzhalter zu verschwinden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cf16809ee2 |
Kurze Hostnamen in Freigaben aufloesen, Protokolle am Ende oeffnen
Freigaben mit kurzem Hostnamen (\S2025\Share) scheiterten mit "could not resolve address for S2025". Ursache: dieser Server hat keine Suchdomaene (search . in resolv.conf), ein Windows-Client hat eine. Der Name ist also richtig, nur hier nicht auflösbar. Die AD-Domaene ist aus den Einstellungen des Verzeichnisdienstes bekannt und wird jetzt zum Vervollstaendigen benutzt; laesst sich der Name gar nicht aufloesen, sagt die Meldung was zu tun ist statt "Unknown error". Auf POETEST gegen echtes DNS geprueft: \S2025\LDAP_Share -> \S2025.ad.eertmoed.net\LDAP_Share, und dieser Pfad bindet ein. Protokollansichten standen beim Oeffnen oben. Die neueste Zeile steht aber unten, und weil die Aktualisierung nur mitzieht, wenn man ohnehin am Ende ist, zog sie danach nie mit -- es sah aus wie ein kaputter Autoscroll, war aber nur die falsche Anfangsposition. Die Protokollansichten sind jetzt per data-tail markiert und springen beim Laden nach unten. Konfigurationsvorschauen (nginx, Kea) und der Offline-Code sind bewusst nicht markiert: die beginnen oben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d8368fc200 |
Windows-Konto: ein Backslash im Hinweis, toleranter Split
Der Hinweistext empfahl "DOMAENE\benutzer" mit zwei Backslashes. Genau so wurde es dann eingetragen -- und der einfache split() schickte daraufhin "\benutzer" an Windows. Die Anmeldung scheiterte mit LOGON_FAILURE bei formal richtigen Zugangsdaten, und die Meldung zeigte auf die falsche Ursache. Der Fehler in der Anleitung wurde damit zum Fehler in den Daten. Behoben an beiden Enden: der Hinweis nennt jetzt ausdruecklich *einen* Backslash, und der Split fasst mehrfache Backslashes zusammen, damit ein bereits falsch eingetragenes Konto trotzdem funktioniert. Dazu Tests, die die Anmeldedatei pruefen statt nur den Code: ein Backslash, zwei Backslashes, Konto ohne Domaene, und die beiden Hinweistexte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e343aee026 |
Dateifeld sichtbar machen, RPC-Fehler verstaendlich melden
Der Knopf im Dateifeld kommt vom Browser und ignoriert jede Aussenfarbe: im dunklen Thema ein hellgraues Kaestchen mit dunkler Schrift, kaum zu lesen und passend zu nichts. Jetzt explizit wie ein Sekundaerknopf gestaltet -- einmal zentral, denn es betrifft vier Felder (Sicherung, Lizenz, Profilbild, Dateifreigaben). Dazu zwei neue Hinweise fuer den Windows-Neustart. "Could not initialise pipe winreg / NT_STATUS_OBJECT_NAME_NOT_FOUND" liest sich wie ein technisches Detail, hat aber fast immer eine Ursache: das Konto ist auf dem Zielgeraet kein Administrator. An einem echten Fall nachgemessen -- die Anmeldung gelang, aber schon "net rpc info" und der Dienstmanager antworteten mit ACCESS_DENIED. Ein Standardkonto bekommt die winreg-Pipe gar nicht zu sehen. Die Meldung sagt das jetzt und nennt den Pruefbefehl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4065b3586b |
Freigaben einbinden, apt-Installation und RPC-Neustart korrigiert
Drei Fehler, alle drei gemessen statt vermutet.
1. Freigaben wurden nie eingebunden ("mount error(1): Operation not
permitted"). Ursache war NICHT der Mount-Namensraum, sondern
SystemCallFilter=@system-service: darin fehlen mount/umount2, und
SystemCallErrorNumber=EPERM macht daraus genau diese Meldung. Nachgewiesen
in einem nachgebauten Sandkasten: mit "@mount" gelingt die Einbindung,
ohne nicht. Die Unit von TESM erlaubt jetzt "@system-service @mount"; alles
andere an der Haertung bleibt. Der zuvor eingebaute Umweg ueber nsenter ist
zurueckgenommen -- RestrictNamespaces=yes verbietet setns, er konnte nie
funktionieren.
Eingebunden wird damit im Namensraum des Webprozesses. Deshalb ist die
Aufraeumaufgabe fuer abgelaufene Einbindungen aus dem Ueberwachungsdienst in
den Webprozess gewandert: ein anderer Dienst sieht diese Einbindungen nicht
und haette Datenbankzeilen als geloest markiert, waehrend die Freigabe
eingebunden blieb.
2. apt-Installationen aus der Oberflaeche scheiterten mit "dpkg returned an
error code (1)". Der Sandkasten wird an dpkg und dessen Maintainer-Skripte
weitervererbt; mit ProtectKernelLogs=yes liefert "dmesg" ein EPERM und ein
Postinst-Skript bricht daran ab. apt laeuft jetzt ueber systemd-run, also
von PID 1 gestartet und damit ohne unsere Haertung.
3. Der RPC-Neustart loeste SSH aus. run_reboot entschied den Weg anhand von
device["category"] -- und dieser Schluessel bedeutet je Herkunft etwas
anderes: die Wartungsabfrage legt die Kategorie der *Zugangsdaten* darunter,
inventory.get_device die des *Geraets*. Ueber die Statusuebersicht kam ein
Windows-Geraet mit leerer Geraetekategorie an, wurde fuer Linux gehalten und
ueber SSH auf Port 22 angesprochen, bis das Zeitlimit griff. Der Weg wird
jetzt uebergeben -- entschieden wird er ohnehin schon vorher in
restart.resolve. Ausserdem zeigte die Neustart-Historie alles ausser "ssh"
als "PoE-Port"; RPC hat jetzt einen eigenen Eintrag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
5bb0809009 |
Freigaben einbinden: Mount im Namensraum des Wirts
Eine Freigabe wurde nie eingebunden -- "mount error(1): Operation not permitted". Gemessen: mit denselben Optionen und Zugangsdaten gelingt die Einbindung aus einer normalen Root-Shell sofort. Ursache ist der eigene Mount-Namensraum des Dienstes (ProtectSystem, ProtectHome, PrivateTmp, ReadWritePaths); darin laesst sich kein neues Dateisystem einbinden. Nachgebaut und belegt: derselbe Aufruf scheitert in einem transienten Unit mit dieser Haertung und gelingt, sobald er ueber nsenter im Namensraum des Wirts laeuft. Von dort wird der Mount in den Namensraum des Dienstes weitergegeben, die Anwendung sieht ihn also -- geprueft, inklusive Lesbarkeit fuer den Dienstbenutzer. mount-cifs und umount laufen deshalb ueber nsenter. Ohne nsenter im System bleibt das Verhalten unveraendert. Die Haertung des Dienstes bleibt vollstaendig erhalten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>v2.0.1 |
||
|
|
64e25bb9ae |
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 <noreply@anthropic.com>v2.0.0 |
||
|
|
7354ff352b |
TESM 2.0.0 -- Neubau
Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert im Zweig SONNET5 erhalten. Aufbau: apps/tesm (Anwendung), packages/tesm-core (gemeinsamer Kern), packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd, privilegierter Helfer), docs, tests. Der Lizenzserver liegt in seinem eigenen Repository; beide Repositorien bringen die gemeinsamen Pakete mit, damit sich jedes allein installieren laesst. Die wichtigsten Unterschiede zum Vorgaenger, jeweils an der Stelle im Code kommentiert, an der der Fehler entstanden ist: * Der Webprozess laeuft unprivilegiert. Alles, was Root braucht, geht ueber einen einzigen Helfer mit Positivlisten fuer jedes Argument. * CSRF-Schutz ueberhaupt -- der Vorgaenger hatte keinen. * Rechte werden serverseitig geprueft, nicht nur im Template ausgeblendet. * Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert. * Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft (nginx, Kea). * Kein Hostname im Lizenz-Fingerabdruck. * Zwei Installationen auf einem Host stoeren sich nicht (eigener SITE_KEY). * Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a193b9d6e9 |
licensing.py: optionales report-Feld fuer Sub-Lizenzserver-Meldungen
Rein synchronisierende Aenderung (harte Invariante: licensing.py bleibt byte-identisch in beiden Repos). build_client_request() akzeptiert jetzt einen optionalen report-Parameter, der bei Heartbeats einer role='license_server'-Lizenz den eigenen Kunden-/Ticket-/Lizenzstand signiert mitschickt -- Kern des neuen Sub-Lizenzserver-Reportings auf dem Lizenzserver (siehe dortiges Release). Bestehende Aufrufer (TESM- Client-Heartbeats, activate/deactivate) uebergeben report nie -- das Feld fehlt dann wie bisher komplett in der Payload, keine Verhaltensaenderung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.8 |
||
|
|
56974a4308 |
licensing.py: neues role-Feld fuer Lizenzserver-Lizenzen (Master-Feature)
Rein synchronisierende Aenderung, um die harte Invariante 'licensing.py
byte-identisch in beiden Repos' zu wahren -- der TESM-Client selbst
nutzt role aktuell nicht aktiv (er ist immer 'customer'), aber das
Sub-Lizenzserver-Feature auf dem Lizenzserver (siehe dortiges Release)
braucht die Erweiterung in derselben Datei.
- LICENSE_ROLES = ('master', 'license_server', 'customer'), neues
role-Feld in der signierten Lizenzdatei-Payload (issue_license()).
- license_status() liefert role mit, faellt bei fehlendem Feld
(aeltere Lizenzdateien) auf 'customer' zurueck.
- Verifiziert: role ist Teil der signierten Payload -- eine nachtraeg-
liche Manipulation der Rolle macht die Signatur ungueltig.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.2.7
|
||
|
|
b69d0745dd |
Eigenes Recht fuer die Lizenzseite (settings_license.view/.edit)
Die Lizenzseite (Einstellungen -> Lizenz) hatte im Rechtebaum keinen eigenen Eintrag -- sie lief komplett ueber settings_system.view/.edit mit. Damit konnte 'Lizenz verwalten' nicht unabhaengig von 'Systemein- stellungen verwalten' vergeben/eingeschraenkt werden (Hostname/Zeitzone/ Pruefintervall/Log-Rotation/Papierkorb-Frist/Nav-Reihenfolge). - Neues Rechtepaar settings_license.view/settings_license.edit (PERMISSIONS/PERMISSION_LABELS), Property can_view_settings_license. - Nav-Sichtbarkeit und alle /settings/license*-Routen auf das neue Recht umgestellt (view-Route: can_view_settings_license; alle mutierenden Routen -upload/-activate(/confirm)/-deactivate(/confirm): settings_license.edit). Das "auch ohne aktive Lizenz sichtbar bleiben"-Verhalten bleibt unveraendert (andere Pruefung, license_active()). - settings_license.html: can_edit auf settings_license.edit umgestellt. - Migration (_migrated_settings_license_permission_v1, einmalig geguarded): jede Gruppe mit settings_system.view/.edit bekommt automatisch settings_license.view/.edit dazu -- niemand verliert beim Update stillschweigend Zugriff, den er vorher implizit hatte. - Getestet gegen isolierte Kopie der Dev-DB: Backfill korrekt (inkl. Gruppe mit nur .view, die korrekt KEIN .edit bekommt), idempotent bei zweitem Lauf, has_permission()/_nav_key_visible() liefern die erwarteten Werte je Testgruppe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.6 |
||
|
|
7ab6358b9f |
Anbieter-Logo auf der Lizenz-Seite anzeigen (v1.2.5)
Der Lizenzserver erlaubt bereits laenger das Hochladen eines Anbieter- Logos (Einstellungen -> Lizenz -> Anbieter & Lizenzserver) und bettet es seit jeher als vendor.logo_base64 in jede ausgestellte Lizenzdatei ein (siehe _get_vendor_info() im Lizenzserver) -- die Kundeninstanz zeigte auf ihrer eigenen Lizenz-Seite zwar bereits Firma/Telefon/E-Mail/ Anschrift des Anbieters an (dieselbe eingebettete vendor-Struktur), aber nie das Logo selbst. Reine Anzeige-Ergaenzung, keine Aenderung am Lizenzformat oder an der Uebertragung noetig -- das Logo steckt in jeder schon ausgestellten Lizenz bereits drin, muss also fuer bestehende Kunden nicht neu ausgestellt werden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.5 |
||
|
|
f02db4c6b5 |
Meldet Hostname bei Aktivierung/Heartbeat an den Lizenzserver (v1.2.4)
Der neue Master-Lizenzserver (tesm-license v1.2.0) zeigt auf seinem Dashboard jetzt eine Kachel-Uebersicht der aktiven Kundeninstanzen inkl. Hostname -- dafuer braucht er den Hostnamen jeder Instanz, den der Master bisher gar nicht kannte (nur den gehashten Fingerprint, siehe licensing.system_fingerprint()). build_client_request() (licensing.py, identisch auf Client UND Master gepflegt) sendet jetzt zusaetzlich socket.gethostname() mit -- rein informativ, fliesst in keine Sicherheitsentscheidung ein (das bleibt Sache des Fingerprints), wird aber wie jedes andere Feld mitsigniert. Abwaerts-kompatibel in beide Richtungen: ein alter Client ohne dieses Feld liefert dem (bereits aktualisierten) Master einfach '', der das dann per COALESCE(NULLIF(...,''), hostname) NICHT als Ueberschreiben eines schon bekannten Hostnamens wertet; ein neuer Client gegen einen noch nicht aktualisierten Master aendert an dessen Verhalten nichts (zusaetzliches Feld wird dort schlicht ignoriert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.4 |
||
|
|
c7ad0d3d49 |
KRITISCH: install.sh loeschte license.json bei jedem Update (v1.2.3)
Live auf POETEST reproduziert: rsync --delete im App-Verzeichnis-Schritt schliesst venv/sqlite.db/fernet.key/secret.key/known_hosts explizit aus, vergisst dabei aber license.json (in Phase 1 neu hinzugekommen, als Ausschluss schlicht uebersehen). Da license.json nur auf dem Zielsystem existiert (nie im Release-Paket enthalten, korrekt .gitignored), loescht --delete sie bei JEDEM 'sudo ./update.sh' auf einer bereits aktivierten Instanz kommentarlos mit -- die Lizenz war danach weg, ohne dass update.sh oder install.sh irgendeinen Fehler gemeldet haetten. Der Master-seitige Installer (install.sh im tesm-license-Repo) hatte diesen Ausschluss von Anfang an korrekt gesetzt -- betraf ausschliesslich TESM-Kundeninstanzen. Fix: license.json zur rsync --exclude-Liste hinzugefuegt, analog zu den anderen laufzeit-generierten Secrets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.3 |
||
|
|
f09aaae8a9 |
Lizenz: sofortiger erster Heartbeat direkt nach Aktivierung (v1.2.2)
Root Cause fuer 'Letzter Heartbeat: noch nie' trotz funktionierender Verbindung (live auf POETEST untersucht und bestaetigt): der Heartbeat- Hintergrund-Thread feuert einmal SOFORT beim Start von tesm.service und danach erst wieder alle 6h. Im realistischen Ablauf (Dienst starten -> danach erst Lizenz hochladen+aktivieren) faellt genau dieser erste, sofortige Versuch immer ins Leere (noch keine Lizenz vorhanden, daher stiller Abbruch ohne Fehlermeldung) -- der naechste ECHTE Versuch waere erst bis zu 6h spaeter gekommen. Manuell nachgestellt: ein direkter Aufruf von _send_heartbeat() nach erfolgter Aktivierung funktioniert einwandfrei (Master verzeichnet den Heartbeat sofort) -- das Protokoll selbst war nie kaputt, nur die Beobachtbarkeit unmittelbar nach der Aktivierung. Fix: beide Aktivierungs-Erfolgspfade (online + Offline-Code-Bestaetigung) loesen jetzt selbst sofort einen ersten Heartbeat aus, statt auf den naechsten planmaessigen 6h-Durchlauf zu warten. Ein Fehlschlag hier (z.B. Kunde tatsaechlich offline) wird wie jeder andere Heartbeat-Fehlschlag behandelt -- rein informativ, schaltet nichts ab. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.2 |
||
|
|
36816c57ef |
Version 1.2.1: update.sh-Fix fuer 'latest'-Aufloesung
Kein App-Code geaendert (app.py unveraendert) -- reiner Versions-/Release- Bump, damit 'latest' auch als heruntergeladenes Paket das bereits korrigierte update.sh enthaelt (siehe vorheriger Commit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.1 |
||
|
|
4a77a3ba32 |
update.sh: 'latest' zuverlaessig ueber die Gitea-API aufloesen
Bug (live gefunden): /releases/download/latest/tesm-latest.tar.gz liefert HTTP 200 -- aber nicht das erwartete Release-Asset, sondern (weil kein Asset exakt so heisst) Giteas automatisch generiertes Quellcode-Archiv des AKTUELLEN Default-Branch-Stands, ohne jede Fehlermeldung. Ein einfaches 'sudo ./update.sh' ohne explizites TESM_RELEASE_TAG haette also lautlos einen unversionierten Zwischenstand installiert statt des tatsaechlich neuesten Releases. Fix: 'latest' wird jetzt zuerst per 'GET /releases/latest' (Gitea-API, das funktioniert bereits zuverlaessig) auf den echten Tag-Namen aufgeloest, BEVOR die Download-URL gebaut wird -- der eigentliche Download zielt damit immer auf ein konkretes, korrekt benanntes Release-Asset. Zusaetzliche Absicherung: nach dem Entpacken wird explizit geprueft, dass install.sh im Paket vorhanden ist, statt erst beim Aufruf mit einer kryptischen 'No such file'-Meldung zu scheitern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7a1f2b164e |
Master-Lizenzserver in eigenes Repo ausgelagert (tesm-license)
srv/tesm-license/, install-license.sh, update-license.sh sowie die zugehoerigen systemd-/nginx-Vorlagen leben ab sofort im eigenstaendigen Repo https://gitea.int.eertmoed.net/alientim/tesm-license (komplette bisherige Historie per git-filter-repo mit uebernommen, dort als v1.0.0 getaggt/released). Grund: eigener Release-/Versionszyklus getrennt von TESM selbst, sauberere Trennung zwischen Kundenprodukt und interner Lizenzierungs-Infrastruktur. Betrifft nur dieses Repo -- die bereits laufende Installation auf 192.168.82.51 (TESM-Lizenzserver) ist unveraendert, zukuenftige Updates dort laufen ueber das neue Repo/update.sh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
682e9b4420 |
Lizenzserver-Deployment: install-license.sh/update-license.sh + systemd/nginx-Vorlagen
Fork von install.sh/update.sh/tesm.service/nginx-Site (TESM selbst), beschraenkt auf /srv/tesm-license -- deployt ausschliesslich den in Phase 2 gebauten Master-Lizenzserver, laesst eine evtl. auf demselben Host vorhandene TESM-Installation komplett unberuehrt (eigener Pfad-/Env-Var- Namespace, siehe app.py). Gleiches Backup+Health-Check+Rueckroll- Sicherheitsnetz wie bei TESMs eigenem Installer. Teil von Phase 3 (Infrastruktur), siehe Plan toasty-twirling-hickey.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.2.0 |
||
|
|
90e07aa310 |
Master-Lizenzserver Phase 2: neue Schwester-Anwendung srv/tesm-license (v1.0.0)
Fork von TESM (srv/tesm), auf Kunden-/Lizenzverwaltung reduziert statt
Geraete-/PoE-Management. Wiederverwendet unveraendert: Login/Session/
Benutzer- und Gruppenverwaltung, LDAP/AD, NGINX- und Systemeinstellungen,
Live-Log/Verlauf/Auditlog, Im-/Export-Grundgeruest, das komplette
PERMISSIONS/NAV_ITEMS/inject_nav()-Rechtesystem sowie das in Phase 1
gebaute Lizenzsystem selbst sowohl fuer die MASTER-eigene Bootstrap-Lizenz
als auch fuer das exakt gleiche licensing.py (byte-identisch zu TESM --
Signieren/Verifizieren muss zwischen beiden Seiten kompatibel bleiben).
Entfernt: Clients/Switche/Zugangsdaten/DHCP/Fileshare/Wartung/Papierkorb/
manueller PoE-Neustart/SSH-Terminal inkl. aller zugehoerigen Tabellen,
Routen, Permissions, Nav-Eintraege und Abhaengigkeiten (paramiko/Flask-Sock/
simple-websocket/PyNaCl/pyasn1 aus requirements.txt). Benutzer-/Gruppen-
Loeschung von Soft- auf Hard-Delete umgestellt (kein Papierkorb mehr).
Eigener Pfad-/Env-Var-Namespace (TESM_LICENSE_* statt TESM_*, /var/log/
tesm-license statt /var/log/tesm usw.), damit Master und TESM testweise
sogar auf demselben Host nebeneinander laufen koennen, ohne sich Log-/
Config-Pfade streitig zu machen.
Neu -- der eigentliche Lizenzserver:
- license_customers/licenses-Tabellen, Master-Signaturschluessel
(master_signing_key.json, einmalig erzeugt, NIE automatisch rotiert --
jede Kundenlizenz traegt den zum Ausstellungszeitpunkt aktuellen
master_pubkey fest eingebettet).
- Dashboard ('/') als Kunden-/Lizenzuebersicht (Typ, Module, Ablauf,
Status, letzter Heartbeat), eigene Kunden-Verwaltungsseite.
- Lizenz-Ausstellung (Typ/Module/Laufzeit -> signierte Datei via
licensing.issue_license), Detailseite, Download, Widerruf.
- /api/activate, /api/deactivate, /api/heartbeat (unauthentifiziert per
Design -- die Signatur der Anfrage IST der Berechtigungsnachweis) sowie
eine manuelle Offline-Code-Seite, die dieselben drei Verarbeitungs-
funktionen (_process_activate/_process_deactivate/_process_heartbeat)
nutzt wie die Online-API -- ein Protokoll, zwei Transportwege.
- E-Mail-Versand ausgestellter Lizenzen per Microsoft Graph
(Client-Credentials-Flow, reine Standardbibliothek/urllib, keine neue
Abhaengigkeit) inkl. Einrichtungsanleitung und Verbindungstest.
- create_master_license.py: lokales Bootstrap-/Erneuerungs-Skript fuer
die eigene Lizenz des Masters (kein externer Super-Master noetig).
Verifiziert auf dem Testsystem (192.168.82.51): App laeuft parallel zu der
dort laufenden echten TESM-Instanz (Port 5001 vs. 80/5000, eigene
Log-/DB-Pfade, TESM unangetastet). Kompletter ECHTER End-to-End-Rundlauf
per Playwright durchgespielt -- kein simulierter Gegenpart: Kunde anlegen
-> Custom-Lizenz (dhcp+fileshare) ausstellen -> herunterladen -> auf der
echten TESM-Instanz hochladen -> ECHTE Online-Aktivierung (Master
verzeichnet Fingerprint, TESM zeigt 'Aktiviert') -> ECHTE Online-
Deaktivierung (TESM zeigt 'lizenzlos, Export bleibt moeglich', Master
zeigt Status 'Deaktiviert'). Lokale Syntax-/Templatepruefung (py_compile,
pyflakes, jinja2-Parse aller Templates) sauber.
Bewusst NICHT nach main gemergt/getaggt/ausgerollt -- folgt zusammen mit
Phase 3 (Infrastruktur-Umzug + POETEST-Enterprise-Lizenz), siehe Plan
toasty-twirling-hickey.md (Phase 2 von 3).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
c9ac0bf69b |
Lizenz-/Modulsystem Phase 1: TESM-seitiges Gating, Aktivierung, Topbar (v1.2.0)
Baut auf licensing.py (Phase 0) auf und schaltet TESM erstmals auf ein lizenziertes Modulmodell um. Standard-Funktionen (Dashboard, Clients, Switche, Zugangsdaten, Benutzer, Gruppen, Auditlog, Live-Log, Verlauf, Import) bleiben immer frei nutzbar. - license_active()/module_licensed()-Gate-Funktionen + In-Memory- Lizenzcache (_load_license, _write_license_file, _delete_license_file), Jinja-Globals fuer Templates. - Vollstaendige Module (DHCP inkl. Kea-Log, Fileshare, Wartung) werden ohne passendes Modul in der Lizenz komplett aus der Navigation ausgeblendet (_nav_key_visible) UND serverseitig auf Routenebene abgewiesen (_require_module) -- ein direkter URL-Aufruf hilft nicht. - 'Sichtbar, aber ohne Lizenz inaktiv': LDAP- und NGINX-Einstellungen, Papierkorb-Bearbeiten, Export sowie das Pruefintervall-Feld (aus dem gemeinsamen Systemeinstellungen-Gate herausgeloest, Hostname/Zeitzone/ Netzwerk/Logs bleiben frei) zeigen ohne Lizenz einen Hinweis statt der Aktion. - Export bleibt als bewusste Ausnahme auch im lizenzlosen Zustand nutzbar, wenn dieser durch eine selbst ausgeloeste Deaktivierung (Systemwechsel) entstanden ist (license_export_grace). - Automatischer PoE-Neustart bei Ausfall wird in poe.sh (nicht in Python) gegatet -- einmal pro Schleifendurchlauf gelesen, an check_device() durchgereicht; der manuelle Neustart-Button (devices.restart) bleibt komplett unangetastet. - Topbar-Lizenzanzeige (Prioritaet: fehlt > abgelaufen > laeuft in <=30 Tagen ab > Heartbeat seit >=14 Tagen ausgeblieben (rein informativ) > alles ok), eigene CSS-Blink-Varianten fuer Rot/Orange (die vorhandene pulse-dot-Animation war fest auf Gruen verdrahtet). - Neue Settings-Unterseite 'Lizenz': Status, Lizenzdatei-Upload, Online- Aktivierung/-Deaktivierung mit automatischem Fallback auf einen manuell auszutauschenden Code (ein Protokoll, zwei Transportwege, siehe licensing.py), Anbieter-Kontaktkarte. - 6-stuendiger Heartbeat-Hintergrundthread (_license_heartbeat_loop, exakt nach dem Vorbild von _audit_archive_loop). Isoliert auf dem Testsystem (192.168.82.51) verifiziert: alle fuenf Topbar-/Sichtbarkeitszustaende (keine/gueltige/bald ablaufende/abgelaufene/ heartbeat-stale Lizenz) per Playwright durchgespielt, kompletter Online- Fallback-auf-Offline-Code-Austausch fuer Aktivierung UND Deaktivierung gegen einen simulierten Master erfolgreich durchlaufen (inkl. Export- Grace-Ausnahme nach Deaktivierung). Bewusst NICHT nach main gemergt/getaggt/ausgerollt: ohne den in Phase 2 noch zu bauenden Master-Lizenzserver kann POETEST noch keine echte Lizenz beziehen -- ein Rollout jetzt wuerde dort DHCP/Fileshare/Wartung/LDAP/ NGINX/Papierkorb/Export/automatischen Neustart sofort abschalten. Bleibt auf dev, bis Phase 2 (Master) + Phase 3 (POETEST-Enterprise-Lizenz) so weit sind, dass beides zusammen ausgerollt werden kann. Teil des Lizenz-/Modulsystems, siehe Plan toasty-twirling-hickey.md (Phase 1 von 3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
b70ebefb17 |
Lizenzsystem Phase 0: Ed25519-Kryptographie, Aktivierungs-/Heartbeat-Protokoll (licensing.py)
Neues gemeinsames Modul fuer Client (TESM) und kuenftigen Master-Lizenzserver: - Zwei-Ebenen-Ed25519-Schluesselhierarchie (Master-Signaturschluessel + Pro-Lizenz-Schluesselpaar), kanonische JSON-Signierung - issue_license/verify_license_file/license_status (inkl. 30-Tage-Gnadenfrist, 30-Tage-Vorwarnung, Modul-Set) - Ein Protokoll fuer Online- (API) und Offline-Aktivierung (Code-Copy-Paste): build_client_request/verify_client_request, build_master_response/verify_master_response, encode_code/decode_code - system_fingerprint (Systembindung), heartbeat_stale_warning (14 Tage, rein informativ) Isoliert getestet (Signatur-Rundlauf, Manipulationserkennung, Ablauf-/ Gnadenfrist-Datumslogik, Online- und Offline-Aktivierung/Deaktivierung inkl. Fingerprint-Bindungspruefung und Reaktivierung nach Deaktivierung, Eingabevalidierung) -- 41/41 Checks erfolgreich. Teil von Phase 0 des Lizenz-/Modulsystems, siehe Plan toasty-twirling-hickey.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d90c27b8f8 |
LDAP-Export: Sperrstatus + individuelle Gruppenzuweisung von AD-Nutzern (weiterhin v1.1.11)
_export_users()/_import_users() schliessen AD/LDAP-Konten bewusst komplett aus (sie werden ueber Active Directory verwaltet). Das hatte aber eine Luecke: weder eine manuelle Sperrung/Entsperrung noch eine manuell (per "Gruppe zuweisen") vergebene individuelle Gruppenmitgliedschaft eines AD-Benutzers wurden bisher irgendwo gesichert -- bei einem Umzug auf ein neues System (der eigentliche Zweck dieser Seite) waere ein zuvor gesperrtes AD-Konto dort wieder entsperrt gewesen, und individuell vergebene Rechte waeren beim naechsten Login des Nutzers verloren gegangen. Neu in der "ldap"-Kategorie: ad_user_states exportiert fuer jeden bereits bekannten AD-Benutzer Sperrstatus + aktuelle Gruppenmitgliedschaft (inkl. Admin als Sentinel, analog den bestehenden AD-Gruppenzuordnungen). Beim Import wird ein bereits vorhandenes AD-Konto entsprechend aktualisiert; existiert es auf dem Zielsystem noch nicht (Nutzer hat sich dort noch nie eingeloggt), wird ein Platzhalter-Konto mit auth_source='ldap' und korrektem Sperrstatus VORAB angelegt -- der Login-Pfad prueft is_locked bereits vor der eigentlichen AD-Authentifizierung, ein vorab gesperrtes Konto bleibt also gesperrt. Gruppenzuweisungen sind rein additiv (insert or ignore), die naechste automatische AD-Gruppenzuordnung beim Login nimmt nichts davon wieder weg. Ausserdem: - Die pauschalen "Was exportieren?"/"Was importieren?"-Hinweise entfernt (waren durch die praeziseren Kategorie-Hinweise bei Benutzer/LDAP/ Zugangsdaten/NGINX/Auditlog laengst ueberholt und teils sachlich veraltet). - Kategorie-Hinweise jetzt auch in der Import-Vorschau sichtbar (vorher nur beim Export). - "Nur Admin"-Kennzeichnung aus beiden Kategorie-Listen entfernt (rein optisch, die serverseitige Rechtepruefung bleibt unveraendert). Verifiziert per isoliertem Roundtrip-Test: gesperrter AD-Nutzer bleibt nach Reset+Import gesperrt, manuell zugewiesene Gruppe eines AD-Nutzers uebersteht Loeschen+Import, und ein komplett neuer (nie eingeloggter) AD-Nutzer wird korrekt mit Sperrstatus/Gruppen/Admin-Flag vorangelegt -- inkl. Bestaetigung, dass der echte Login-Sperr-Check dieses vorab angelegte Konto tatsaechlich blockiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.11 |
||
|
|
8577cd24cd |
Export-Kategorien: kuerzere Namen, Auditlog/Benutzer-Hinweise als Icon (weiterhin v1.1.11)
Sichtbare Checkbox-Namen im Export-Formular verkuerzt (nur die Kachel- Ansicht betroffen, Funktionalitaet/Schluessel unveraendert): Custom-Gruppen -> Gruppen, LDAP/AD-Einstellungen -> LDAP/AD, DHCP-Einstellungen -> DHCP, NGINX-Einstellungen -> NGINX, Benutzer (lokal) -> Benutzer. Neue Hinweis-Icons (Klammerzusaetze wandern konsistent zu den bereits bestehenden bei Zugangsdaten/NGINX ins "i"-Icon statt in den Namen): - Auditlog: Hinweis, dass nur die aktuell in der DB stehenden Eintraege exportiert werden (bereits archivierte Tage liegen als eigene Dateien vor). - Benutzer: Hinweis, dass AD/LDAP-Benutzer NICHT gesichert werden (nur lokale Konten sind Teil des Exports -- AD-Konten legen sich beim naechsten Login ohnehin automatisch neu an). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
877903bd77 |
NGINX-Export inkl. Zertifikat/Schluessel, Kategorie-Hinweise als Icon (weiterhin v1.1.11)
- NGINX-Export nimmt jetzt zusaetzlich das aktuell hinterlegte TLS- Zertifikat und den privaten Schluessel selbst mit (Base64, falls vorhanden) -- der gesamte Export ist ohnehin passphrasenverschluesselt, genau wie andere Geheimnisse (Zugangsdaten-Passwoerter, LDAP-Bind- Passwort) bereits im Klartext darin landen. Bewusst weiterhin AUSGESCHLOSSEN bleibt jeglicher Let's-Encrypt-/certbot-Zustand (ssl_source, ACME-Konto, Renewal-Timer) -- an genau diesen Host/diese Domain-Registrierung gebunden, kann nicht mitziehen. Ein Import setzt ssl_source deshalb immer auf "upload", auch wenn urspruenglich per Let's Encrypt ausgestellt. - Die erklaerenden Klammerzusaetze bei "Zugangsdaten" (know_hosts-Hinweis) und "NGINX-Einstellungen" (Zertifikat/Schluessel-Hinweis) sind jetzt ein "i"-Hinweis-Icon statt Fliesstext im Label, passend zum bestehenden Hover-Tooltip-/Klick-Modal-Muster (siehe v1.1.10). Verifiziert per isoliertem Test mit einem echten (selbstsignierten) Test-Zertifikat: Export->Import-Roundtrip liefert bitidentische Cert-/ Key-Dateien mit korrekten Berechtigungen (0600 Schluessel, 0644 Zertifikat) auf einem eigenen Zielpfad, sowie live per Browser gegen POETEST (Hover zeigt korrekten Tooltip-Text, 2 Icons im Export-Formular). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
2b05d012ee |
Im-/Export: fehlende Felder ergaenzt, NGINX neu, known_hosts (v1.1.11)
Vollstaendiger Audit aller Export-/Import-Kategorien gegen das tatsaechliche DB-Schema (7 parallele Pruef-Agents je Kategorie) ergab mehrere Luecken -- alle behoben: - Auditlog-Export (v1.1.10-Nacharbeit): kein Deckel mehr bei 500 Zeilen, exportiert wird das komplette aktuell in der DB stehende Auditlog (bereits archivierte Tage liegen ohnehin als eigene Dateien vor). - Clients: ssh_port und Zugangsdaten-Zuordnung (als portabler credential_name statt roher ID) fehlten komplett im Export. - Switche: ssh_port fehlte komplett im Export. - Zugangsdaten: category (z.B. "Linux-Client" fuer Wartung) fehlte. - Custom-Gruppen: is_default fehlte (war beim Import zusaetzlich hart auf 0 gesetzt statt aus der Importdatei uebernommen). - LDAP-Gruppenzuordnungen: app_group_id wurde als ROHE lokale Datenbank-ID exportiert -- auf einem frisch installierten Zielsystem (der eigentliche Zweck dieser Funktion, siehe Seiten-Untertitel "Umzug auf eine neue Umgebung") haben Gruppen dort andere IDs, die Zuordnung waere kaputt oder auf die falsche Gruppe gezeigt. Jetzt wird der Gruppenname exportiert (bzw. "Admin" als Sonderfall) und beim Import auf dem Zielsystem wieder zur dortigen ID aufgeloest -- unbekannte Zielgruppen werden übersprungen statt eine kaputte Referenz anzulegen. - ldap_fileshare_mappings (AD-Gruppe -> Freigabe) fehlte im Export/Import komplett -- alle Fileshare-Zuordnungen gingen bei jedem Umzug verloren. - ldap_required_login_group (Login-Beschraenkung auf eine AD-Gruppe) fehlte in LDAP_SETTING_KEYS und wurde nie exportiert. - NGINX-Einstellungen (Domain, Ports, SSL/HSTS-Schalter) hatten bisher gar keine Export-/Import-Kategorie. Bewusst AUSGESCHLOSSEN bleiben ssl_source, das TLS-Zertifikat und der private Schluessel sowie jeglicher Let's-Encrypt-/certbot-Zustand -- das sind host-gebundene reale Dateien bzw. eine ACME-Registrierung fuer genau diesen Host, kein portabler Einstellungswert. - known_hosts: sobald Zugangsdaten exportiert werden, wird zusaetzlich der Inhalt von known_hosts mitexportiert (jeder per SSH bereits bestaetigte Host-Key gehoert inhaltlich zu den Zugangsdaten) und beim Import ergaenzend (nicht ueberschreibend) mit Zeilen-Deduplizierung in die eigene known_hosts-Datei eingespielt. avatar_filename (Benutzer) bleibt bewusst vom Export ausgeschlossen -- reiner Dateiname ohne die zugehoerige Bilddatei waere auf dem Zielsystem nur ein kaputter Verweis, analog zur bewussten Nichtaufnahme von TLS-Zertifikat/ Schluessel bei NGINX. Verifiziert per isoliertem Roundtrip-Test gegen eine Kopie der echten Datenbank (nie die Produktivdaten selbst): Export -> Loeschen -> Re-Import fuer jede geaenderte Kategorie plus known_hosts-Merge (inkl. Deduplizierungs-Probe durch zweifachen Import derselben Datei), sowie ein echter Browser-Exportlauf gegen POETEST zur Bestaetigung der UI/Label-Texte. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
0f0fa99e53 |
Feld-/Funktionserklaerungen als Hover-Tooltip + Klick-Modal (v1.1.10)
Statisch sichtbare Erklaerungstexte (.hint/.field-hint) app-weit auf ein
einheitliches "i"-Icon umgestellt: Hover zeigt den Text neben dem
Mauszeiger, Klick/Tap oeffnet ein wiederverwendetes Modal mit dem vollen
Text. Zentral in app.js (initHintIcons, ensureHintTooltip/-Modal) via
Event-Delegation, kein Aufwand pro Seite. Hover ist bewusst per
matchMedia("(hover: hover) and (pointer: fine)") gated -- auf Touch-
Geraeten faellt automatisch nur Tap-zu-Modal an, da echte
mouseenter/mousemove-Events dort ohnehin nicht zuverlaessig auftreten.
Nicht jede .hint/.field-hint wurde umgestellt -- nach folgenden Kriterien
entschieden:
- Live-Status-Anzeigen (aktuelle Log-Pfade, Netzwerkstatus, Auditlog-
Schwellenwerte, aktive Reservierungs-/Lease-Zaehler, certbot-Timer-
Status usw.) bleiben sichtbar, da sie aktuellen Zustand zeigen, keine
generische Erklaerung.
- Kurze, einzeilige Abschnitts-Untertitel (z.B. "Server, Bind-Konto und
Suchparameter fuer die Anbindung an AD/LDAP.") bleiben als Orientierung
direkt unter der Ueberschrift sichtbar.
- Bereits JS-live-aktualisierte Status-Felder (id-tragend, z.B.
"Fragt live... ab." -> "N Gruppen geladen.") bleiben unangetastet.
- Laengere, mehrsaetzige Abschnitts-Erklaerungen (z.B. AD-Gruppenzu-
ordnungen, Fileshare-Gruppenzuordnung, Subnetze, DHCP-Options, Wartung)
sowie so gut wie alle Feld-Erklaerungen wandern ins Icon.
Nebenbei: Karte "Fileshare-Gruppen" in LDAP-Einstellungen zu "Fileshare-
Gruppenzuordnung" umbenannt (passt jetzt zur bereits bestehenden
log_action-Zielbezeichnung in app.py).
Neue gemeinsame Macro-Datei templates/_hint_icon.html, importiert von
15 Templates. Verifiziert: alle 18 betroffenen Seiten laden fehlerfrei
(200, keine JS-Fehler), Icon-Anzahl je Seite geprueft, Hover-Tooltip und
Klick-Modal auf Desktop, Tap-Modal auf simuliertem Mobilgeraet (390x844,
Touch), sowie stichprobenartig bestaetigt dass die bewusst belassenen
Hinweise unveraendert sichtbar bleiben.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.1.10
|
||
|
|
9b4cf8c2e1 |
Auditlog: Statistik-Kacheln zeigen geladen/gesamt (weiterhin v1.1.9)
Alle vier Kacheln (Alle, Hinzufuegen, Aenderungen, Loeschungen) zeigen jetzt "geladen / gesamt" (z.B. "300 / 466"), solange noch nicht alles geladen ist -- Gesamtzahlen je Kategorie kommen aus einer einzigen aggregierten SQL-Abfrage (SUM/CASE ueber die bestehenden LIKE-Muster von category_of), nicht aus einem vollstaendigen Tabellen-Scan. Sobald alles geladen ist, wird wieder nur die reine Zahl angezeigt (client- wie serverseitig identische Formatierungslogik). Dieser Fix wird per Nutzervorgabe unter der GLEICHEN Releasnummer v1.1.9 ausgeliefert (Tag wird bewusst weitergezogen statt neu vergeben, da v1.1.9 zum Zeitpunkt dieses Commits zwar schon getaggt, aber die Kachel-Anzeige noch nicht ausgereift war). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.9 |
||
|
|
1c0082d3d4 |
Auditlog: Nachladen aelterer Eintraege in 300er-Schritten (v1.1.9)
Die Auditlog-Seite zeigte bisher unveraenderlich nur die neuesten 500 Zeilen an -- alles Aeltere war komplett unsichtbar (auch wenn es noch nicht archiviert war, siehe v1.1.6). Jetzt: - Seitengroesse von 500 auf 300 reduziert (Erstladung UND jeder Nachlade- Schritt). - Neuer Button "Mehr laden (300)" (orange) laedt per AJAX genau den naechsten 300er-Block aelterer Eintraege nach (Cursor-Pagination ueber die id-Spalte) und haengt ihn ans bestehende tbody an, ohne die Seite neu zu laden. - Neuer Button "Alle laden" ruft denselben Nachlade-Schritt wiederholt auf, bis wirklich alles geladen ist (mit Fortschrittsanzeige im Button-Text), statt in einer Anfrage die komplette (potenziell sehr grosse) Tabelle auf einmal zu rendern. - Suche/Sortierung/Kategorie-Filter sowie die vier Statistik-Kacheln arbeiten transparent ueber ALLE aktuell geladenen Zeilen (nicht nur die urspruenglichen 300) -- Zeilenliste fuer die Filterung wird jetzt bei jedem Aufruf frisch aus dem DOM gelesen statt einmalig zwischengespeichert. - Zeilen-Rendering (Aktions-Label/-Icon/-Farbe) in ein gemeinsames Makro (_audit_log_macros.html) ausgelagert, das sowohl die Erstladung als auch das AJAX-Fragment (_audit_log_rows.html) nutzen -- verhindert Abweichungen zwischen beiden Darstellungen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
fc4b165d23 |
Auditlog: Ziel-Spalte wieder dynamisch statt erzwungen einzeilig (v1.1.8)
Das white-space:nowrap aus v1.1.7 sollte nur den Sonderfall des neuen Auditlog-Export-Eintrags fixen, hat aber als Nebenwirkung die gesamte Ziel-Spalte in die Breite gezogen: der bereits laenger bestehenden Aktion data.export gehoert ein bis zu 135 Zeichen langes Ziel (Liste der exportierten Kategorien), und table-layout:auto bemisst eine Spaltenbreite anhand des LAENGSTEN unzeilenumbrochenen Inhalts ueber alle (auch gerade nicht gefilterte) Zeilen hinweg -- nicht nur der sichtbaren. Der eigentliche Ausloeser (zu langer Wert im Ziel-Feld) ist mit v1.1.7 bereits behoben (Ziel = kurzer Ressourcenname wie "Auditlog", Dateiliste in Details) und macht das erzwungene nowrap ueberfluessig. Zeilenumbruch bei den seltenen laengeren Zielen (wie data.export) ist unproblematisch, genau wie es die Details-Spalte schon immer handhabt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.8 |
||
|
|
e81735d5ec |
Auditlog-Export: Ziel/Details korrekt getrennt (v1.1.7)
Beim Export archivierter Auditlog-Tage (log_action("auditlog.export_delete", ...))
stand faelschlich die komplette Dateiliste im Feld "Ziel" (dadurch wurde die
Spalte in der Auditlog-Tabelle wieder unschoen breit), statt wie bei allen
anderen Aktionen ueblich einen kurzen Ressourcennamen im Ziel und die
ausfuehrliche Beschreibung in Details zu haben. Jetzt: Ziel = "Auditlog",
Details = Anzahl + exakte Dateinamen. Gleiche Korrektur fuer den
automatischen Archivierungslauf selbst (auditlog.archive). "Ziel"-Spalte
zusaetzlich mit white-space:nowrap gegen Umbrueche bei laengeren Werten
abgesichert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.1.7
|
||
|
|
2eaefc9e1f |
Auditlog-Archivierung, Verlauf-RAW mit Zeilennummern, UI-Politur (v1.1.6)
- Auditlog waechst nur bis zu einem Schwellenwert (20.000 Zeilen): danach werden die aeltesten VOLLSTAENDIGEN Kalendertage (der laufende Tag wird nie mitten am Tag angefasst) tageweise in je eine Datei audit-YYYY-MM-DD.log unter /var/log/tesm/audit-archive archiviert und aus der DB geloescht, bis wieder 15.000 Zeilen unterschritten sind. Laeuft als taeglich pruefender Hintergrund-Thread (gate: TESM_WEB_PROCESS, wie beim bestehenden Fileshare-Sweep), zusaetzlich einmalig sofort beim Dienststart. - Archivierte Auditlog-Tage sind unter Verlauf einsehbar (RAW-Ansicht) und koennen dort als ein ZIP exportiert werden, was sie danach vom Server loescht -- bewusst nur manuell ausgeloest statt automatisch, mit eigenem Recht logs_history.edit (neue "E"-Spalte bei Verlauf in der Gruppen-Rechtematrix, ergibt sich automatisch aus GROUP_ROW_TYPES). Aufbewahrung der Archivdateien selbst ist unbegrenzt; bei knappem Speicherplatz (< 1 GiB frei im Log-Verzeichnis) erscheint ein Warnhinweis auf der Verlauf-Seite. - Verlauf -> "Komplettes Log (RAW)": Inhalt jetzt mit Zeilennummern (reine CSS-Counter-Loesung, kein Extra-Markup pro Zeile) -- Live-Log-RAW und nginx-RAW-Konfiguration bleiben bewusst unveraendert (nur an dieser Stelle angefragt). - Auditlog-Tabelle: Avatar-Spalte entfernt, Spaltenbreiten (Zeitpunkt/ Benutzer/Aktion/Ziel) schrumpfen jetzt auf den Inhalt statt zu strecken, "Ziel" nimmt nur noch so viel Platz wie noetig; Aktion-Pills brechen generell nicht mehr um (white-space: nowrap). - "Änderungen" endgueltig zu "Auditlog" umbenannt (Recht-Label, Kommentare); die "Änderungen"-Filterkachel selbst (Bearbeiten-Kategorie) behaelt bewusst ihren Namen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.6 |
||
|
|
cb8b929c3c |
Auditlog: Hinzufuegen gruen, Loeschen rot, Bearbeiten orange (v1.1.5)
- Neue Pill-Farbvarianten (.action-pill--create/--delete/--edit) je nach Aktions-Art -- "create"/"upload"/"mkdir" gruen, "delete" rot, alles andere (bearbeiten/aktivieren/zuweisen/...) orange als Sammelkategorie. - Fehlende Klartext-Label fuer die neuen fileshare.*-Aktionen ergaenzt (upload/mkdir/delete/rename/download) -- vorher stand dort noch der rohe Aktionsschluessel wie "fileshare.delete" statt eines lesbaren Textes wie bei allen anderen Aktionen. Live auf POETEST verifiziert: alle drei Farben korrekt (gruen bei "Datei(en) hochgeladen"/"Gruppe angelegt", rot bei "Datei/Ordner gelöscht"/"Gruppe gelöscht", orange bei "Als ZIP heruntergeladen"/ "Gruppe bearbeitet"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.5 |
||
|
|
af5dfe30b4 |
Fileshare: Mehrfachauswahl zum Herunterladen (ZIP) und Loeschen (v1.1.4)
- Checkbox-Spalte in der Dateitabelle (inkl. "alle auswaehlen" im Tabellen- kopf) -- sobald mindestens ein Eintrag markiert ist, erscheint eine Aktionsleiste mit "Herunterladen (ZIP)" und (mit fileshare.edit) "Loeschen". - Herunterladen buendelt die Auswahl serverseitig zu EINEM ZIP statt mehrerer einzelner Downloads (vermeidet Browser-Blockaden bei vielen gleichzeitigen Downloads) -- neue Route /fileshare/download-multi, Ordner werden dabei rekursiv mit aufgenommen (relativer Pfad als Archivpfad). - Loeschen mehrerer Elemente auf einmal ueber eine neue Route /fileshare/delete-multi (ein Bestaetigungsdialog fuer die ganze Auswahl), mit Sammel-Erfolgsmeldung analog zum Mehrfach-Upload. - Die Pfadaufloesung+Validierung fuer ein einzelnes Kind-Element wurde aus dem bestehenden Einzel-Loeschen in _fileshare_resolve_child() extrahiert und von Einzel-Loeschen, Mehrfach-Loeschen UND Mehrfach-Download gemeinsam genutzt (kein duplizierter Sicherheitscode). Live auf POETEST verifiziert: Mehrfachauswahl inkl. "alle auswaehlen", ZIP-Download zweier Dateien (Inhalt geprueft), ZIP-Download eines Ordners (rekursiv, korrekter Archivpfad), Mehrfach-Loeschen mit Sammelmeldung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.4 |
||
|
|
efb0d2aa01 |
Gruppen-Bearbeiten als Modal, Mobile-Fix, Mehrfach-Upload (v1.1.3)
- Gruppen bearbeiten (Name + Rechte) laeuft jetzt ueber ein Modal statt der bisherigen Inline-Ausklapp-Zeile -- gleiche Optik wie "Neue Gruppe". Der Gruppenname war dabei bisher ein verstecktes, nie wirklich editierbares Feld; jetzt ein normales Texteingabefeld (Backend unterstuetzte das Umbenennen inkl. Systemgruppen-Schutz bereits vollstaendig, es fehlte nur die Eingabemoeglichkeit im Formular). Admin-Rechte-Ansicht und die "Freischalten"-Ausnahme fuer die Systemgruppe "Benutzer" ziehen ins jeweilige Modal mit um. - Fileshare-Baum + Tabelle nebeneinander sprengte auf Tablet-/Handybreite die Seite -- stapelt jetzt (Baum oben, Tabelle darunter) ab der bestehenden 900px-Sidebar-Umschaltgrenze. - Mehrfach-Upload: Datei-Eingabefeld erlaubt jetzt echte Mehrfachauswahl (mehrere Dateien in einem Dialog) UND mehrmaliges Hinzufuegen nacheinander (per DataTransfer angesammelt, bevor "Hochladen" gedrueckt wird) -- funktioniert nativ auch auf Mobilgeraeten, da kein Custom- Upload-Mechanismus noetig ist. Backend verarbeitet jetzt eine Liste statt einer einzelnen Datei (request.files.getlist), mit Sammel- Erfolgsmeldung und pro Datei separater Namensvalidierung. Live auf POETEST verifiziert: Umbenennen+Rechte-Speichern ueber das neue Modal, Admin-Modal (readonly), Mobile-Layout (390px, Baum stapelt korrekt), echte Mehrfachauswahl (2 Dateien in einem Dialog, beide korrekt hochgeladen und einzeln in der Sammelmeldung genannt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.3 |
||
|
|
d461f8ca42 |
Fileshare: redundanten Breadcrumb entfernt, Baum/Tabelle gleich hoch (v1.1.2)
- Der Breadcrumb-Link-Zeile ueber der Dateitabelle ("LDAP Share / Test")
war seit der Baum-Navigation redundant -- der Baum zeigt die aktuelle
Position ja schon (aufgeklappt + hervorgehoben). Entfernt.
- .fileshare-layout auf align-items:stretch umgestellt (statt flex-start)
und .fileshare-main als Flex-Spalte mit table-wrap{flex:1}, damit die
Baum- und die Tabellen-Kachel immer exakt gleich hoch sind, unabhaengig
vom jeweiligen Inhalt. Das inline margin-bottom auf .table-wrap musste
dafuer weg (sonst blieb trotzdem eine 16px-Luecke zwischen den unteren
Kanten).
Live auf POETEST verifiziert: beide Kacheln exakt gleiche Hoehe (166.5px
in beiden Fällen), Breadcrumb-Div nicht mehr im DOM.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.1.2
|
||
|
|
1b7fe81507 |
Fileshare: Baum-Navigation, Datei-Vorschau, 15MB-Uploads (v1.1.1)
- Freigaben-Auswahl von einem Dropdown auf eine Baum-Navigation (links) umgestellt: alle gemounteten Freigaben als Wurzelknoten, Ordner beliebig aufklappbar. Der Pfad von der Wurzel bis zur aktuellen Position wird serverseitig schon aufgeklappt gerendert (_fileshare_tree_ancestors), alles andere laedt der Client per Klick lazy nach (neue Route /fileshare/subfolders) -- kein Durchlaufen der kompletten Freigabe auf einmal noetig. Dateien stehen bewusst nicht im Baum, nur in der bestehenden Tabelle rechts. - Inline-Vorschau fuer PDF (iframe), Bilder, TXT/CSV/LOG/MD/JSON (per Range- Request in Haeppchen bis 512KB, mit Hinweis bei Abschneiden -- Erkennung ueber den Content-Range-Header, nicht nur den 206-Status, der bei JEDEM Range-Request auftritt) sowie Word/Excel rein clientseitig per mammoth.js bzw. SheetJS (beide lokal vendored, kein Cloud-Viewer -- interne Dokumente verlassen das Netz nicht). Excel-Zellen werden bewusst per textContent statt ueber SheetJS' eigene HTML-Ausgabe gesetzt, um unabhaengig von deren Escaping-Verhalten sicher zu sein. Neue Route /fileshare/view mit fester Endungs-Positivliste, nosniff + restriktiver CSP. - Uploads bis 15MB erlaubt (MAX_CONTENT_LENGTH in app.py) -- dafuer musste auch nginx' client_max_body_size (Standard 1MB) auf 16MB angehoben werden, sowohl im Vorlagen-Code (_NGINX_PROXY_LOCATIONS/_render_nginx_config, fuer die naechste Anwendung ueber Einstellungen->NGINX) als auch in der Fresh-Install-Vorlage. Neuer 413-Errorhandler zeigt eine Flash-Meldung statt Werkzeugs nackter Fehlerseite. - install.sh: known_hosts (liegt unter /srv/tesm) von rsync --delete ausgenommen -- fehlte bisher in der Exclude-Liste (anders als sqlite.db/ fernet.key/secret.key) und waere bei jedem Update verlorengegangen. Live auf POETEST getestet: Baum-Vorbefuellung, Lazy-Load von Geschwisterordnern, alle 5 Vorschautypen (PDF-Route direkt verifiziert, Bild/Text/Word/Excel zusaetzlich visuell), Abschneide-Erkennung sowohl bei kleiner als auch bei >512KB-Datei. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.1.1 |
||
|
|
2c9ec8a100 |
Fileshare: Hostname-Aufloesung fuer UNC-Pfade + Zuordnungen bearbeitbar
- Freigabe-Hostnamen (statt nur IP) werden jetzt unterstuetzt: mount.cifs
bekommt die tatsaechliche Ziel-IP gezielt per "ip="-Option mitgegeben,
aufgeloest ueber _resolve_unc_host_ip(). Deckt zwei reale Faelle ab, die
beim Live-Test mit einem echten Server (Kurzname "s2025") auftraten:
1. Ein bloer Kurzname ohne DNS-Suffixsuche loest auf diesem Server gar
nicht auf -- Fallback probiert zusaetzlich den aus der LDAP-Server-
Einstellung abgeleiteten AD-Domaenensuffix (a la ".ad.eertmoed.net").
2. IPv4 wird bevorzugt aufgeloest, IPv6 nur als Fallback genutzt, falls
fuer den Hostnamen keine IPv4-Adresse existiert.
Der Hostname bleibt dabei unveraendert in der UNC sichtbar/gespeichert.
- AD-Gruppenzuordnungen (App-Rechte) und Fileshare-Gruppen sind jetzt per
Bearbeiten-Button (Stift-Icon) direkt aenderbar statt nur loeschen+neu
anlegen zu koennen -- neue edit_ldap_group_mapping/edit_fileshare_mapping
POST-Routen (UPDATE per id), Bearbeiten-Modals vorbefuellt inkl.
AD-Gruppen-Dropdown (gleiches Lade-Muster wie beim Anlegen).
- UX: "Erforderliche AD-Gruppe fuer Login" ist jetzt ein Dropdown mit
"Gruppen laden" (identisches Muster wie die anderen AD-Gruppenfelder)
statt eines Freitextfelds, in das der volle DN von Hand einzutragen war.
- _normalize_share_unc()-Hilfsfunktion aus dem Add-Handler herausgezogen,
jetzt auch vom Edit-Handler genutzt (kein duplizierter Code).
Live auf POETEST verifiziert: Hostname-Mount ("s2025") erfolgreich nach
Fix, Bearbeiten-Modals korrekt vorbefuellt und persistiert, Dropdown laedt
55 AD-Gruppen und behaelt die aktuelle Auswahl beim Neuladen bei.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.1.0
|
||
|
|
09b222615a |
Fileshare-Feature: LDAP-gruppengesteuerte Netzlaufwerke im Browser (v1.1.0)
- Neue ldap_fileshare_mappings-Tabelle (AD-Gruppe -> Freigabe-UNC+Label), verwaltet in Einstellungen -> LDAP wie die bestehende App-Gruppenzuordnung (inkl. AD-Gruppen-Dropdown per "Gruppen laden", UNC-Backslash/Slash- Normalisierung). - Login-Gate: optionale "Erforderliche AD-Gruppe fuer Login" (Dropdown, gleiches Lade-Muster) -- ohne Mitgliedschaft scheitert der Login wie bei falschen Zugangsdaten, ohne Hinweis auf den eigentlichen Grund. - Sessionabhaengiges Mounten: beim LDAP-Login werden alle per AD-Gruppe zugeordneten Freigaben mit den eigenen (nie gespeicherten) Zugangsdaten per mount.cifs unter /mnt/tesm-shares/<session-token>/ gemountet, beim Logout wieder ausgehaengt. Passwort läuft ausschliesslich ueber die PASSWD-Umgebungsvariable von mount.cifs. Aufraeum-Sweep (30 Min-Takt, 12h-Schwelle) und Cleanup-on-Startup als Sicherheitsnetz. - Zwei unabhaengige Gates: TESM-Rechtesystem (fileshare.view/.create/.edit, neue PERMISSIONS-Gruppe fileshare_group) UND AD-Gruppenmitgliedschaft muessen beide zutreffen, sonst wird nicht gemountet und der Menuepunkt "Dateifreigaben" bleibt verborgen. - Neuer Dateibrowser (/fileshare, .../download, .../upload, .../mkdir, .../delete, .../rename) mit Breadcrumbs, Upload/Download, Anlegen/ Umbenennen/Loeschen -- Pfadaufloesung ausschliesslich ueber os.path.realpath-Vergleich gegen die eigene Mount-Root (Path-Traversal- Schutz), zusaetzlich secure_filename() fuer Datei-/Ordnernamen. - KRITISCHER Bugfix waehrend Live-Test gefunden: generate_ips.py importiert app.py als Hilfsmodul (u.a. aus poe.sh/tesm-check.service heraus, alle paar Sekunden) -- das fuehrte Modul-Level-Code erneut aus und hat aktive Fileshare-Mounts anderer Sitzungen sofort wieder ausgehaengt. Neues TESM_WEB_PROCESS-Env-Flag (nur in tesm.service gesetzt) schuetzt Cleanup-on-Startup und den Sweep-Thread davor, bei einem reinen Modul-Import erneut zu laufen. - install.sh: cifs-utils ergaenzt (liefert mount.cifs). - SCHEMA_VERSION 1 -> 2, VERSION 1.0.10 -> 1.1.0. Live auf Testbox (192.168.82.51) und POETEST (192.168.80.202) getestet: Login-Gate (erlauben/verbieten/reset), Rechte-Gate (Mount+Menuepunkt nur mit fileshare.view), vollstaendiger Datei-Workflow (Upload/Download/ Umbenennen/Anlegen/Loeschen) gegen eine echte SMB-Freigabe, Path-Traversal gegen alle 5 Routen (serverseitig verifiziert, kein Escape), sauberes Unmount bei /logout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
0074e48e09 |
Responsive-Fixes per echtem Playwright/DevTools-Test verifiziert, zwei Bugs behoben
Auf Nachfrage tatsaechlich mit Browser-DevTools (Playwright/Chromium,
lokal installiert) statt nur per CSS-Analyse geprueft -- echte
Screenshots + automatisierte Overflow-Pruefung auf iPhone-16-Pro- (393x852),
iPad- (820x1180 Hoch-/1180x820 Querformat) und Desktop-Viewport (1440x900)
gegen die echte Live-Instanz auf Testsystem-Update-Restart.
Dabei zwei echte Bugs gefunden und behoben:
1. Topbar-Ueberlappung (ALLE Seiten, jeder eingeloggte Nutzer, jedes
iPhone/kleine Handy): .topbar-left (Seitentitel) und .topbar-right
(Pruef-Countdown- + DHCP-Pille) sind beide flex:1 ohne Zeilenumbruch --
auf 393px Breite quetscht das den Titel auf eine Breite, die kleiner
ist als sein eigener Inhalt braucht, wodurch der Titeltext sichtbar
ueber seine Spalte hinauslief und mit der Pille kollidierte (per
getBoundingClientRect nachgewiesen: 24.5px Ueberlauf). Fix: ab 560px
Breite bricht die Topbar um, Titel und Pillen bekommen je eine eigene
Zeile.
2. HTTP-/HTTPS-Port-Felder auf der NGINX-Seite stapelten trotz
entsprechender Media-Query-Regel nicht auf Handy-Breite -- Ursache:
inline style="flex:1" auf den .field-Divs hat Vorrang vor jeder externen
@media-Regel, ganz gleich wie spezifisch. Fix: inline style entfernt,
neue Klasse .field--half stattdessen -- die Mobile-Stapel-Regel greift
jetzt wie vorgesehen.
Zusaetzlich EINE eigene Aenderung aus v1.0.9 wieder zurueckgenommen, weil
sie sich beim Testen als unnoetig und riskant herausstellte:
- .table-wrap: overflow-x:auto (aus v1.0.9) wieder auf overflow:hidden
zurueckgesetzt. Grund: JEDES Template mit .table-wrap hat bereits einen
eigenen <div style="overflow-x:auto;"> exakt um die <table> -- die
zusaetzliche Scroll-Faehigkeit der Karte selbst war komplett redundant
und erzeugte einen verschachtelten doppelten Scroll-Container um
denselben Inhalt. Beim Testen der aufklappbaren Rechte-Tabelle
(Gruppen-Seite) fuehrte das zu Inhalten, die bei negativem x dauerhaft
unsichtbar wurden. table.data-table { min-width: 560px; } bleibt
bestehen (harmlos, der jeweils vorhandene innere Wrapper scrollt
weiterhin korrekt).
Eine dritte vermutete Ursache (aufgeklappte Rechte-Tabelle "verschwindet"
beim Anklicken des Chevrons) stellte sich NICHT als echter App-Bug heraus,
sondern als Artefakt von Playwrights eigenem automatischem
"scroll-into-view-vor-Klick"-Verhalten (der Chevron sitzt in der letzten
Tabellenspalte) -- per direktem JS-Aufruf von toggleDetail() ohne
Playwright-Klick liess sich das nicht reproduzieren, die Seite verhaelt
sich für echte Nutzer korrekt.
Alle 8 getesteten Seiten x 4 Viewports (32 Kombinationen) zeigen nach den
Fixes keinerlei horizontalen Overflow mehr (dokumentseitenweite
scrollWidth == clientWidth in jedem Fall).
VERSION 1.0.9 -> 1.0.10.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
latest
v1.0.10
|
||
|
|
a3f5f028f2 |
Responsives CSS ausgebaut: Handy (iPhone) und Tablet (iPad) sauber nutzbar
Bestehendes eigenes Design-System beibehalten (kein Bootstrap-Framework eingebunden), stattdessen die vorhandenen Media Queries/Breakpoints (Sidebar kollabiert bereits ab 900px) um die fehlenden Faelle ergaenzt: - .table-wrap: overflow:hidden -> overflow-x:auto. Tabellen mit vielen Spalten (Geraete, Switche, Papierkorb, Benutzer, ...) haben auf schmalen Screens bisher entweder Spalten lautlos abgeschnitten oder die ganze Seite waagerecht aufgerissen -- jetzt scrollt NUR die Tabelle selbst (gleiches Prinzip wie Bootstraps .table-responsive). table.data-table bekommt zusaetzlich eine min-width als sinnvolle Scroll-Schwelle. - .settings-grid: minmax(340px, 1fr) -> minmax(min(340px, 100%), 1fr) -- ein nackter Pixel-Mindestwert erzwingt auf einem 375px-iPhone sonst waagerechten Overflow der ganzen Seite, sobald die Spalte breiter als der verfuegbare Platz ist. Neue Modifier-Klasse .settings-grid--wide (420px-Variante) ersetzt zwei betroffene inline style="grid-template-columns:minmax(420px,...)"-Overrides (LDAP-, NGINX-Seite), die denselben 100%-Fallback nicht hatten. - .permission-group-col: eigenes overflow-x:auto zusaetzlich zum bestehenden flex-wrap der Elternzeile -- falls eine einzelne Rechte-Spalte (z.B. Geraete mit R/W/E/D) auch nach dem Umbruch auf eine eigene Zeile noch zu breit ist. - Ab 900px (bestehender Sidebar-Breakpoint) zusaetzlich: .flex (generische Button-/Toolbar-Zeilen), .section-head, .term-toolbar/.log-toolbar und .detail-row brechen um statt seitlich ueberzulaufen; .modal-footer ebenso; .search-input in Tabellen-Toolbars schrumpft flexibel. - Ab 640px: nebeneinander angeordnete Formularfelder (z.B. HTTP-/HTTPS- Port bei NGINX) stapeln sich auf volle Breite statt zwei sehr schmale Eingaben zu erzwingen; Modal-Innenabstand etwas schmaler. - Ab 560px: Buttons in Kopfzeilen (section-head) nehmen die volle Breite ein statt als schmaler Block rechts zu kleben. Live auf Testsystem-Update-Restart verifiziert: alle Kernseiten (Dashboard, Clients, Switche, Gruppen, LDAP, NGINX, Live-Log, Wartung) laden weiterhin fehlerfrei (200) nach dem CSS-/Template-Austausch. WICHTIGER HINWEIS: In dieser Umgebung steht kein Browser-Screenshot- Werkzeug zur Verfuegung, die Aenderungen wurden also nicht pixelgenau auf echten iPhone-16-Pro-/iPad-Bildschirmgroessen gegengeprueft, sondern rein ueber CSS-Analyse und Seiten-Ladetests. Ein Blick auf einem echten Geraet (oder per Browser-Devtools-Emulation) wird empfohlen. VERSION 1.0.8 -> 1.0.9. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>v1.0.9 |