Rueckmeldung 22.08.2026: "ich hab auf der verwaltungs seite 2 mal dass ich
den zugangscode eingeben muss und vanvan auch, ich will es nur einmal
eingeben muessen." Berechtigt.
Die Zugangswand hat serverseitig laengst geprueft, WER da ist -- Dogfather
oder VanVan, laut Masterplan S.4 beide gleichberechtigte
Volladministratoren. Ein zweites Passwort danach bringt keinen
zusaetzlichen Schutz, es kostet nur jedes Mal Zeit.
Warum es nicht einfach "Cookie mitschicken" ist: Die Verwaltungsdaten
liegen hinter postfach.dogfather-universe.com, einem ANDEREN Rechnernamen.
Das Sitzungs-Cookie gilt dort nicht -- so sind Cookies gebaut, und das ist
gut so.
Loesung: Die Zugangswand stellt unter /webdesign/api-ausweis einen
kurzlebigen, signierten Ausweis aus, den die interne API anerkennt.
- 15 Minuten gueltig, die Seite holt bei Bedarf still einen neuen
- traegt bereich="wd-admin", damit ein Sitzungs-Token der Zugangswand
hier NICHT durchgeht und umgekehrt
- nur mit gueltiger Zugangssitzung zu bekommen
- gilt AUSSCHLIESSLICH fuer die /webdesign-Endpunkte. Postfach,
Bewerbungen, Supporter und Teamverwaltung bleiben unberuehrt
Fehlt WEBDESIGN_API_SECRET auf einer der beiden Seiten, gilt kein Ausweis
und die Verwaltung fragt wie bisher nach dem Team-Code. Ein fehlender
Konfigurationswert darf niemals eine Tuer oeffnen -- nur eine schliessen.
Genau das prueft der letzte Testfall.
Bei einem 401 wird EINMAL still ein neuer Ausweis geholt und die Anfrage
wiederholt, statt jemanden mitten im Arbeiten rauszuwerfen.
10/10 Tests, darunter: gefaelschte Signatur, fremdes Geheimnis,
abgelaufen, falscher Bereich, erfundene Rolle.
Co-Authored-By: Claude Opus 5 <[email protected]>
84 lines
3.5 KiB
JavaScript
84 lines
3.5 KiB
JavaScript
/* =====================================================================
|
|
webdesign-ausweis.js — Ausweis der Zugangswand anerkennen
|
|
|
|
Hintergrund (Rückmeldung 22.08.2026: "ich will es nur einmal eingeben
|
|
müssen"): Die Zugangswand vor /webdesign hat serverseitig bereits
|
|
geprüft, wer da ist. Dogfather und VanVan sind laut Masterplan S.4
|
|
gleichberechtigte Volladministratoren. Ein zweites Passwort für die
|
|
Verwaltung bringt danach keinen zusätzlichen Schutz — es kostet nur
|
|
jedes Mal Zeit.
|
|
|
|
Der Ausweis wird von server/webdesign-gate.js ausgestellt und hier
|
|
geprüft. Beide Seiten teilen sich dafür WEBDESIGN_API_SECRET.
|
|
|
|
⚠️ WAS DIESER AUSWEIS NICHT IST
|
|
Er ist KEIN Team-Login. Er berechtigt ausschließlich zu den
|
|
Webdesign-Endpunkten und verschafft keinerlei Zugriff auf Postfach,
|
|
Bewerbungen, Supporter oder Teamverwaltung. Wer den Webdesign-Bereich
|
|
betreten darf, darf damit nicht automatisch alles andere auch.
|
|
|
|
⚠️ FEHLT DAS GEHEIMNIS, GILT KEIN AUSWEIS
|
|
Dann bleibt es bei der Team-Anmeldung wie bisher. Ein fehlender
|
|
Konfigurationswert darf niemals eine Tür öffnen — nur eine schließen.
|
|
===================================================================== */
|
|
|
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
|
function b64urlZurueck(str) {
|
|
return Buffer.from(String(str).replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
}
|
|
|
|
/**
|
|
* Prüft den Ausweis aus dem Authorization-Kopf.
|
|
* Liefert { rolle } bei Gültigkeit, sonst null.
|
|
*/
|
|
export function ausweisPruefen(req) {
|
|
const geheim = process.env.WEBDESIGN_API_SECRET;
|
|
if (!geheim) return null; // nicht eingerichtet -> kein Ausweis
|
|
|
|
const kopf = req.headers["authorization"] || "";
|
|
const token = kopf.startsWith("Bearer ") ? kopf.slice(7) : null;
|
|
if (!token || !token.includes(".")) return null;
|
|
|
|
const [nutzlast, sigB64] = token.split(".");
|
|
try {
|
|
const erwartet = createHmac("sha256", geheim).update(nutzlast).digest();
|
|
const geliefert = b64urlZurueck(sigB64);
|
|
/* Länge zuerst vergleichen: timingSafeEqual wirft bei
|
|
unterschiedlich langen Puffern, statt false zu liefern. */
|
|
if (geliefert.length !== erwartet.length) return null;
|
|
if (!timingSafeEqual(geliefert, erwartet)) return null;
|
|
|
|
const daten = JSON.parse(b64urlZurueck(nutzlast).toString("utf8"));
|
|
/* bereich prüfen: Ohne diesen Vergleich würde ein Sitzungs-Token der
|
|
öffentlichen Zugangswand hier ebenfalls durchgehen, obwohl es für
|
|
einen ganz anderen Zweck ausgestellt wurde. */
|
|
if (daten.bereich !== "wd-admin") return null;
|
|
if (!daten.exp || daten.exp < Date.now()) return null;
|
|
if (daten.rolle !== "dogfather" && daten.rolle !== "vanvan") return null;
|
|
|
|
return { rolle: daten.rolle };
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Einheitliche Rechteprüfung für die Webdesign-Endpunkte.
|
|
*
|
|
* Zwei Wege führen hinein:
|
|
* 1. Der Ausweis der Zugangswand (Dogfather oder VanVan) — beide sind
|
|
* Volladministratoren und dürfen im Webdesign-Bereich alles.
|
|
* 2. Eine normale Team-Anmeldung mit dem passenden Einzelrecht — für
|
|
* den Fall, dass später jemand aus dem Team nur Anfragen ansehen,
|
|
* aber keine Kundendaten bearbeiten soll.
|
|
*
|
|
* `pruefeTeam` wird als Funktion hereingereicht, damit diese Datei nichts
|
|
* von auth.js und permissions.js wissen muss.
|
|
*/
|
|
export async function darfWebdesign(req, recht, pruefeTeam) {
|
|
const ausweis = ausweisPruefen(req);
|
|
if (ausweis) return { erlaubt: true, actor: ausweis.rolle, ueberAusweis: true };
|
|
return pruefeTeam();
|
|
}
|