Files
DogFatherGitandClaude Opus 5 74091590b8 Verwaltung: nur noch EINMAL den Code eingeben
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]>
2026-08-22 21:36:11 +02:00

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();
}