API: Zugriff strikt auf den Besitzer des API-Keys, Key generieren/anzeigen

Der X-API-Key identifiziert den Benutzer; alle Endpunkte waren bereits auf
dessen user_id gescoped (27 Operationen geprueft). Zwei Luecken blieben:

- Die Aufloesung Token -> Benutzer nahm bei mehrdeutigem Token per LIMIT 1
  einfach den ersten Treffer. Haetten zwei Benutzer denselben Token, saehe
  der eine die Daten des anderen. Jetzt: fail closed (401 + Logeintrag),
  und /einstellungen weist einen bereits vergebenen Token mit 409 ab.
- Ein leerer Token galt als Wert: wer seine Einstellungen einmal gespeichert
  hatte, besass eine API_TOKEN-Zeile mit ''. Leere Werte matchen jetzt nie.

Neu in den Einstellungen (Abschnitt REST-API):
- "Neu generieren" erzeugt einen zufaelligen Token (32 Byte, crypto.get-
  RandomValues); er wird nur ins Feld gefuellt und erst beim Speichern
  aktiv, ein Fehlklick laesst sich also verwerfen.
- "Kopieren" legt den Token in die Zwischenablage; das Auge blendet ihn ein
  (bestand bereits fuer Secret-Felder).

API aktualisiert:
- PUT /settings kannte nur name/adresse/kundennummer, GET lieferte aber alle
  acht Felder. Jetzt schreibt PUT alle (email, telefon, ort, webseite,
  geburtsdatum) und aendert nur die im Body uebergebenen Felder; die Antwort
  enthaelt den neuen Stand.

Swagger:
- Beschreibung sagte "konfiguriert via Umgebungsvariable API_TOKEN" - das
  gilt seit der Multi-User-Umstellung nicht mehr. Jetzt dokumentiert:
  Token pro Benutzer aus den Einstellungen, Zugriff nur auf eigene Daten,
  fremde id -> 404, unbekannter/leerer/mehrdeutiger Token -> 401.
- Settings-Schema um die fehlenden fuenf Felder ergaenzt.

Verifiziert mit zwei Benutzern und je eigenem Key: Lesen, Aendern, Loeschen,
Timeline, Anhaenge, E-Mails, Generierung, Jobangebote und Blacklist des
jeweils anderen liefern durchgaengig 404; Listen, Export und Statistik
zeigen nur eigene Daten; kollidierender Token -> 409; mehrdeutiger Token in
der DB -> 401 fuer beide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 22:56:21 +02:00
co-authored by Claude Opus 4.8
parent b6cb94fdb5
commit 1e14738258
5 changed files with 179 additions and 25 deletions
+16
View File
@@ -2888,6 +2888,22 @@ initializeDatabase().then(async () => {
werte[f.key] = req.body[f.key] != null ? String(req.body[f.key]) : '';
}
}
// The API token identifies the user to /api/v1, so it must not collide with
// another user's: the auth lookup would otherwise be ambiguous (it now fails
// closed, which would lock *both* users out). Reject the save instead.
const token = (werte.API_TOKEN || '').trim();
if (token) {
const fremd = await dbGet(
`SELECT u.username FROM app_state a JOIN users u ON u.id = a.user_id
WHERE a.key = ? AND a.value = ? AND a.user_id != ?`,
[config.PREFIX + 'API_TOKEN', token, uid()]
);
if (fremd) {
return res.status(409).send('Dieser API-Token ist bereits von einem anderen Benutzer belegt. Bitte „Neu generieren“ verwenden.');
}
}
await config.saveAll(werte);
res.redirect('/einstellungen');
} catch (error) {