5 Commits
Author SHA1 Message Date
florian.wildeandClaude Opus 5 85b9576b38 test(auth): Gruppenisolation absichern, Doku zum Rechtemodell
AccessControlTest prueft die Trennung ueber die echte Kette
Auth -> AccessPolicy -> Repository, also genau so, wie die
Controller sie benutzen - nicht gegen eine nachgebaute Logik.
Abgedeckt sind unter anderem: ein Mitglied sieht nur die eigene
Gruppe, der direkte Aufruf einer fremden Kennung findet nichts,
ein Benutzer ohne Gruppe sieht gar nichts (die leere Liste darf
nicht wie "keine Einschraenkung" wirken), Viewer duerfen lesen
aber nicht schreiben, das Umhaengen einer Domain nimmt ihre
Kurzlinks mit, fremde Logos werden nicht ausgeliefert, ein
gesperrtes Konto verliert seine laufende Sitzung und ein
Passwortwechsel macht bestehende Sitzungen ungueltig.

MigrationUpgradeTest deckt zusaetzlich den Umstieg ab: das
Bestandskonto wird mit unveraendertem Hash uebernommen, die alten
settings-Schluessel verschwinden, Bestandsdomains landen in der
Standardgruppe - und eine frische Installation bekommt kein
erfundenes Konto.

README: neuer Abschnitt "Benutzer, Gruppen und Rechte" mit dem
Grundprinzip, den beiden Rollenebenen, der Einrichtung in drei
Schritten und dem Umstieg von der Einbenutzer-Version.
ADMIN_USER und ADMIN_PASSWORD_HASH sind als Notfallzugang neu
beschrieben, in README und .env.example.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 12:03:41 +02:00
florian.wilde 583d9f0ae2 feat(links): Mehrteilige Slugs und erhaltene Gross-/Kleinschreibung
Wer bereits gedruckte QR-Codes aus einem anderen System uebernimmt,
kann deren Adressen nicht mehr aendern. Sehen die aus wie
"/g/1AEJ0X90ZmF", waren sie hier bisher nicht abbildbar: der
Normalisierer machte daraus "g-1aej0x90zmf" - Schraegstrich zu
Bindestrich, alles klein. Der Kurzlink zeigte damit ins Leere.

Ein Slug darf jetzt mehrere durch "/" getrennte Abschnitte haben
(hoechstens acht, je 64 Zeichen) und behaelt die eingegebene
Schreibweise. Erlaubt sind Buchstaben, Ziffern, Bindestrich,
Unterstrich und Punkt; jeder Abschnitt muss mit einem Buchstaben oder
einer Ziffer beginnen und enden. Umlaute und Leerzeichen werden
weiterhin umgeschrieben. Wird versehentlich eine komplette URL
eingefuegt, bleibt davon nur der Pfad uebrig.

Damit die Verwaltung nicht verdeckt werden kann, greift die Liste
reservierter Namen jetzt auf den ERSTEN Abschnitt: "admin/geheim" wird
ebenso abgelehnt wie "admin". Die Route der Weiterleitung steht
weiterhin ganz am Ende, alle Verwaltungsrouten gewinnen davor.

Bei der Eindeutigkeit gilt bewusst das Gegenteil: sie wird OHNE
Ruecksicht auf Gross- und Kleinschreibung geprueft. "Messe" und "messe"
koennen auf derselben Domain nicht nebeneinander bestehen - an einer
gedruckten Adresse waere sonst nicht mehr erkennbar, welcher Link
gemeint ist. Ausserdem wuerde MySQL sie je nach Kollation ohnehin als
gleich ansehen, SQLite nicht; so verhalten sich beide Treiber wieder
gleich.

Beim Aufruf wird zuerst exakt gesucht und nur bei einem Fehlschlag ohne
Ruecksicht auf die Schreibweise - abgetippte Codes stimmen nicht immer.
Weil die Eindeutigkeit oben schon ohne Ruecksicht geprueft wurde, kann
dieser zweite Versuch nie mehrdeutig sein.

Geprueft in beiden Betriebsarten: im Container und in der
Shared-Hosting-Variante, bei der die Root-.htaccess nach public/
umleitet - dort sind mehrteilige Pfade der wahrscheinlichste
Stolperstein.
2026-08-12 18:32:41 +02:00
florian.wilde 649f6ba247 fix: Lauffaehigkeit unter PHP 8.4 und 8.5 herstellen
Auf einem Hetzner-Webhosting mit PHP 8.5 brach die Anwendung beim ersten
Aufruf ab. Ausloeser war session.sid_length: seit PHP 8.4 als veraltet
gemarkt. Die Meldung wurde ausgegeben, damit waren Header raus - und
danach liess sich weder eine ini-Einstellung aendern noch eine Session
starten. Aus einer Randnotiz wurde so ein Totalausfall.

Behoben auf drei Ebenen:

1. Die veralteten Aufrufe sind weg. session.sid_length und
   session.sid_bits_per_character werden nicht mehr gesetzt; die
   Voreinstellungen liefern mit 128 Bit ohnehin genug Entropie.
   Ebenso entfallen curl_close(), imagedestroy() und finfo_close() -
   seit PHP 8.0 bzw. 8.5 wirkungslos und als veraltet gemeldet.

2. public/index.php puffert die Ausgabe von der ersten Zeile an. Viele
   Hoster erzwingen display_errors und lassen sich per ini_set() nicht
   davon abbringen. Gepuffert laesst sich Fremdausgabe vor dem Senden
   verwerfen und ins Fehlerprotokoll umleiten, statt die Antwort zu
   zerstoeren. Das schuetzt auch Binaerdownloads: eine einzelne
   Meldung haette bisher jedes PNG beschaedigt. Session::start()
   meldet ausserdem klar, wenn doch schon Ausgaben raus sind, statt
   eine Kaskade aus Warnungen zu erzeugen.

3. endroid/qr-code von 5.1 auf 6.0 angehoben. Version 5 markiert
   Parameter implizit als nullable, was PHP 8.4 bemaengelt - bei jedem
   erzeugten QR-Code. Version 6.1 waere ebenfalls verfuegbar, verlangt
   aber PHP 8.4+ und wuerde Hoster mit 8.2 oder 8.3 ausschliessen;
   6.0 laeuft ab 8.1.

Getestet wird jetzt gegen PHP 8.3, 8.4 und 8.5, jeweils mit
display_errors=On und error_reporting=E_ALL, damit Deprecations
sichtbar werden statt still im Log zu landen. Alle 232 Tests laufen auf
allen drei Versionen ohne eine einzige Meldung durch. Zusaetzlich ist
das Release-Paket in einer PHP-8.5-Umgebung mit fremdem Webroot
geprueft: Ersteinrichtung, Kurzlinks, Weiterleitung, QR-Codes und
Statistik, ohne Streuausgabe in Seiten oder Downloads.
2026-08-12 18:01:30 +02:00
florian.wilde c65c02cda5 feat(setup): Systemcheck und Upload-Paket fuer klassisches Webhosting
Auf Shared Hosting gibt es keine Kommandozeile - ohne Anzeige laesst
sich dort nicht feststellen, ob etwa gd fehlt, die PHP-Version zu alt
ist oder data/ nicht beschreibbar ist. Der Setup-Assistent prueft das
jetzt und listet es auf; im Dashboard meldet sich die Pruefung spaeter
nur noch, wenn etwas nicht stimmt. Fehlende Pflichtbausteine sind
Fehler, fehlende Kuer (intl, imagick, cURL) nur Hinweise mit Angabe,
was dadurch entfaellt.

build-release.sh erzeugt das Upload-Paket: Abhaengigkeiten ohne
Entwicklungspakete, ohne Tests und Docker-Dateien. Dabei faellt eine
16 MB grosse Schriftart aus endroid/qr-code weg, die ausschliesslich
fuer Beschriftungen unter dem QR-Code gebraucht wird - die erzeugt
diese Anwendung nicht. Das Paket schrumpft damit von 18 MB auf 1 MB.
Sollte spaeter doch eine Beschriftung dazukommen, erkennt das Skript
das und behaelt die Datei.

Die Variante ohne eigenen Webroot (alles liegt im Domainverzeichnis,
die .htaccess im Stammverzeichnis leitet nach public/ um) war bisher
gebaut, aber nie erprobt. Sie ist jetzt gegen eine Hoster-Simulation
getestet: Ersteinrichtung, Kurzlinks, Weiterleitung, QR-Codes,
Statistik und die Sperren fuer src/, vendor/, data/ und .env.
2026-08-10 15:32:13 +02:00
florian.wilde 7d5a4f3f29 docs: README mit Deployment-, Bedienungs- und Sicherheitsteil
Zwei Deployment-Wege (Shared Hosting per FTP in zwei Varianten sowie
Docker), vollstaendige Konfigurationsreferenz, Bedienungsanleitung und
eine Anleitung, was fuer eine weitere Domain beim Provider zu tun ist -
inklusive Fehlersuche.
2026-08-03 18:11:40 +02:00