Vier Befunde aus einer externen Durchsicht der 2.4-Integration.
1. Die .htpasswd war eine Klartext-Kundenliste
Das htpasswd-Format hasht nur die Passwortspalte. Benutzername UND Passwort
waren der Lizenzschluessel - der Schluessel stand also im Klartext direkt
neben seinem eigenen bcrypt-Hash, und der Hash war Dekoration. Geschuetzt
hat das Ganze nur die FilesMatch-Regel in derselben Datei.
Der Benutzername wird jetzt abgeleitet: lic_<sha256(schluessel), 16 Hex>.
Die Datei enthaelt damit nur noch eine Einwegableitung und einen Hash ueber
einen hochentropen Schluessel.
Server und SDK muessen dabei zeichengenau uebereinstimmen; ein Test prueft
die C#-Ableitung gegen die PHP-Formel.
2. Ein Formatwechsel blieb unbemerkt liegen
Beim Umbau auf 1. faellt auf: reconcile() sah keinen Anlass zur
Neuerzeugung, die Dateien behielten das alte Format, waehrend die Clients
bereits das neue schickten. Die erzeugten Dateien tragen deshalb jetzt eine
Formatkennung; weicht sie ab, wird neu erzeugt.
3. Doku beschrieb Nginx, der Schutz ist Apache-only
.htaccess wird von Nginx ignoriert - dort waeren die Verzeichnisse offen und
die .htpasswd oeffentlich abrufbar. Die Statusanzeige pruefte nur, ob die
Dateien existieren, und haette in dem Fall "GESCHUETZT" gemeldet.
Neu: ein echter Selbsttest ruft die eigene Paket-Adresse OHNE Zugangsdaten
ab und erwartet 401. Er laeuft beim manuellen Erzeugen und nach jeder
automatischen Neuerzeugung; das Ergebnis steht in der Oberflaeche, ein
Fehlschlag im Log. Er findet nebenbei auch abgeschaltetes AllowOverride und
Tippfehler in der erzeugten Datei. Doku korrigiert, Nginx-Vorlage ergaenzt.
4. Erstinstallation schrieb an einen Ort, an dem Linux-Anwendungen nicht lesen
setup.json-Ziele waren immer installationsrelativ. Eine Anwendung, die sich
unter Linux richtig verhaelt, liest aus $XDG_CONFIG_HOME - /opt/<app> ist
fuer den Dienstbenutzer meist nicht schreibbar. Der Installer legte die
Datei also dorthin, wo nie jemand nachsieht.
Ziele haben jetzt ein "location": install (Vorgabe), config, data, home,
plus ${VAR}- und %VAR%-Ersetzung in "file". Unbekannte Variablen bleiben
stehen statt leer zu werden - ein Platzhalter faellt auf, ein falscher Pfad
nicht. Der Installer gibt den aufgeloesten Pfad aus, weil bei config das
Konto entscheidet, unter dem er laeuft.
Ausserdem
- Doku zeigte "status": "ok" fuer update/delete; Http::ok() erzeugt
"status": "success".
- UPGRADE §16.1 deckte Neuprodukte nicht ab: Fuer ein Produkt ohne Release
existiert /releases/<slug>/ nicht und wird uebersprungen. Das Verzeichnis
entsteht erst mit dem ersten Upload, der naechste Tick schuetzt es. Der erste
ausgelieferte Build muss die Zugangsdaten also schon mitbringen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74 lines
2.9 KiB
JSON
74 lines
2.9 KiB
JSON
{
|
|
"_comment": "Vorlage fuer setup.json. Gehoert ins Wurzelverzeichnis des Publish-Ordners, damit sie im Paket neben der manifest.json landet. Der Installer liest sie nach dem Entpacken und fragt genau die Werte ab, die er nicht selbst ermitteln kann.",
|
|
|
|
"schema": 1,
|
|
"displayName": "Beispielanwendung",
|
|
"description": "Fuer den Betrieb werden eine Datenbank und ein Zugang zum Kursdatenanbieter benoetigt.",
|
|
|
|
"_targets_comment": "Wohin geschrieben wird. 'location' bestimmt den Bezugspunkt: install (Vorgabe, Installationsverzeichnis), config (%APPDATA% bzw. $XDG_CONFIG_HOME), data, home. In 'file' sind ${VAR} und %VAR% erlaubt, ein absoluter Pfad wird unveraendert benutzt. format ist 'json' oder 'env'. Ueber 'id' lassen sich einzelne Felder gezielt einer Datei zuordnen; ohne Angabe schreibt ein Feld in das erste Ziel.",
|
|
"targets": [
|
|
{ "id": "app", "file": "appsettings.json", "location": "install", "format": "json" },
|
|
{ "id": "user", "file": "beispielanwendung/Settings.json", "location": "config", "format": "json" },
|
|
{ "id": "env", "file": ".env", "location": "install", "format": "env" }
|
|
],
|
|
|
|
"_fields_comment": "source steuert, ob gefragt wird: 'ask' (Vorgabe) fragt nach, 'detect:...' leitet ab, 'provision' laesst ein Token ausstellen. Ist der Wert in der Zieldatei bereits gesetzt und kein Platzhalter, wird er uebernommen und nicht erneut erfragt.",
|
|
"fields": [
|
|
{
|
|
"key": "ConnectionStrings:Main",
|
|
"label": "Datenbankverbindung",
|
|
"help": "Beispiel: Server=localhost;Database=beispiel;User Id=app;Password=...;",
|
|
"type": "secret",
|
|
"required": true
|
|
},
|
|
{
|
|
"key": "Marktdaten:ApiKey",
|
|
"label": "API-Schluessel des Kursdatenanbieters",
|
|
"type": "secret",
|
|
"required": true,
|
|
"validate": "^[A-Za-z0-9_-]{20,}$",
|
|
"validationMessage": "Der Schluessel besteht aus mindestens 20 Zeichen ohne Leerzeichen."
|
|
},
|
|
{
|
|
"key": "Deploymentcenter:BaseUrl",
|
|
"label": "Deploymentcenter",
|
|
"type": "url",
|
|
"source": "detect:baseurl"
|
|
},
|
|
{
|
|
"key": "Deploymentcenter:Token",
|
|
"label": "Token dieser Installation",
|
|
"source": "provision",
|
|
"scopes": ["watchdog:ping", "bugtracker:report"]
|
|
},
|
|
{
|
|
"key": "Watchdog:Source",
|
|
"label": "Name im Monitoring",
|
|
"source": "detect:hostname"
|
|
},
|
|
{
|
|
"key": "Watchdog:IntervalSeconds",
|
|
"label": "Heartbeat-Intervall in Sekunden",
|
|
"type": "int",
|
|
"default": "60",
|
|
"required": false
|
|
},
|
|
{
|
|
"key": "Features:BetaAktiv",
|
|
"label": "Beta-Funktionen einschalten",
|
|
"type": "bool",
|
|
"default": "false",
|
|
"required": false
|
|
},
|
|
{
|
|
"key": "LOGLEVEL",
|
|
"label": "Protokollierungsstufe",
|
|
"target": "env",
|
|
"type": "enum",
|
|
"options": ["Debug", "Information", "Warning", "Error"],
|
|
"default": "Information",
|
|
"required": false
|
|
}
|
|
]
|
|
}
|