Spicy Media legt Manager UND Scouts an -- eine Liste statt vier

Filipe, mit Bildschirmfoto der Zugaenge-Seite: "die spicy rolle soll
auch manager und scouts hinzufuegen koennen."

DER MANAGER-KNOPF FEHLTE NICHT AUS RECHTEGRUENDEN. Serverseitig war die
Tuer /workspace/api/manager-anlegen fuer Spicy Media die ganze Zeit
offen. Es gab nur nichts zum Draufdruecken -- wegen ZWEIER Listen in
derselben Funktion, drei Zeilen auseinander (personen.js):

  const darf = ... spicy ? ['manager', 'creator'] : ['creator'];
  ...
  if ((r.wert === 'admin' || r.wert === 'manager') && ich.rolle !== 'admin') continue;

Die erste erlaubt den Manager, die zweite nimmt ihn wieder weg. Uebrig
blieb ein einziger Knopf: Creator. Nichts war kaputt, nichts wurde rot,
es fehlte einfach -- die Sorte Fehler, die nur jemandem auffaellt, der
davorsitzt.

FUER SCOUTS GAB ES UEBERHAUPT KEINE TUER. Nur DogFather konnte welche
anlegen. Und die Wegwahl in der Oberflaeche war eine Kette mit
Auffangbecken (`rolle === 'manager' ? ... : creator-anlegen`): Ein Scout
waere im else gelandet, und creator-anlegen legt IMMER einen Creator an.
Der Knopf haette Erfolg gemeldet und das Falsche getan.

DIE ANTWORT STEHT JETZT AN EINER STELLE. `darfAnlegen` in workspace.js
sagt, wer wen anlegen darf. Daraus lesen:

  - die beiden Team-Tueren (Manager, Scout)
  - die allgemeine Verwaltungs-Tuer von DogFather
  - die Oberflaeche, ueber `darf_anlegen` in /workspace/api/ich

Die Oberflaeche hat damit gar keine eigene Liste mehr und kann deshalb
auch nicht mehr abweichen -- weder zu streng noch zu grosszuegig.

ZWEI TUEREN, NICHT EINE MIT EINEM ROLLENFELD. Der Absatz an der
Manager-Tuer raet davon ab, und der Rat gilt: Eine Tuer, die NICHTS
anderes kann, als eine bestimmte Rolle anzulegen, ist sicherer als eine,
die vorher nachfragt. `teamTuer(rolle)` baut beide aus demselben Text --
die Rolle wird beim Einhaengen festgelegt und kommt nie aus dem Aufruf.
Geprueft: ein mitgeschicktes "rolle: admin" bleibt wirkungslos.

GEPRUEFT (pruef-creator-anlegen, 33 -> 49 Pruefungen)
  - Spicy Media legt Manager an       -> 201, Rolle stimmt
  - Spicy Media legt Scout an         -> 201, Rolle stimmt
  - "rolle: admin" mitgeschickt       -> wirkungslos, es wird ein Scout
  - ein Manager durch die Scout-Tuer  -> 404
  - ein Scout durch die Scout-Tuer    -> 404
  - Personenliste lesen               -> 200 (die eine gewollte Ausnahme)
  - darueber anlegen                  -> 404, und es entsteht niemand
  - /api/ich nennt Spicy: manager, scout, creator -- und keinen DogFather
  - ein Manager bekommt genau eine Rolle genannt, ein Scout keine
  - die Knoepfe auf der Seite stimmen mit alldem ueberein
  - DogFather sieht unveraendert alle -- gemessen, nicht geglaubt

Der erste Anlauf der Pruefung behauptete, Spicy Media komme gar nicht an
/workspace/api/verwaltung. Falsch, und sie wurde zu Recht rot: nurAdmin
laesst genau einen Fall durch, das LESEN der Personenliste. Diese
Trennung ist jetzt festgenagelt.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-10 21:56:45 +02:00
co-authored by Claude Opus 5
parent 5f270c96c5
commit bcc1c70f4a
28 changed files with 577 additions and 293 deletions
+69 -23
View File
@@ -15,7 +15,7 @@
import express from "express";
import {
db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN,
db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, darfAnlegen,
} from "./workspace.js";
import { sicherungJetzt } from "./workspace-sicherung.js";
@@ -520,27 +520,61 @@ personenRouter.post("/workspace/api/creator-anlegen", gleicheHerkunft, nurLeitun
EINEN MANAGER LEGT MAN NICHT NEBENBEI AN: Er sieht danach jeden
Creator, den er betreut, und legt selbst welche an. Deshalb bleibt
diese Tuer Spicy Media und DogFather vorbehalten -- ein Manager kann
keinen zweiten Manager schaffen. */
personenRouter.post("/workspace/api/manager-anlegen", gleicheHerkunft, (req, res) => {
try {
const person = sitzungLesen(req);
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
if (!siehtAlles(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
keinen zweiten Manager schaffen.
const name = String(req.body?.name ?? "").trim();
if (name.length < 2) return res.status(400).json({ fehler: "Name fehlt." });
if (name.length > NAME_MAX) return res.status(400).json({ fehler: "Name ist zu lang." });
const vorhanden = db().prepare(
"SELECT 1 FROM personen WHERE lower(name) = lower(?) AND aktiv = 1").get(name);
if (vorhanden) return res.status(409).json({ fehler: "Diesen Namen gibt es schon." });
NACHTRAG 10.09.2026 — JETZT AUCH SCOUTS, UND TROTZDEM ZWEI TUEREN.
const neu = personAnlegen(name, "manager", { ...person, ip: echteIp(req) });
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code });
} catch (fehler) {
console.error("[workspace] Manager anlegen:", fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
Filipe: "die spicy rolle soll auch manager und scouts hinzufuegen
koennen." Fuer Scouts gab es bis heute ueberhaupt keinen Weg ausser
ueber DogFather.
Der naheliegende Griff waere gewesen, hier ein Feld `rolle`
anzunehmen. Genau davon raet der Absatz oben ab, und der Rat gilt
weiter: Eine Tuer, die NICHTS anderes kann, als eine bestimmte Rolle
anzulegen, ist sicherer als eine, die vorher nachfragt. Eine
vergessene Abfrage waere ein zweiter Zugang mit allen Rechten; eine
vergessene Abfrage an einer Tuer, die nur "scout" kennt, ist ein
Aergernis, aber keine Luecke.
Also weiterhin zwei Tueren mit je einer festen Rolle — nur nicht mehr
zweimal derselbe Text. `teamTuer(rolle)` baut den Griff, die Rolle
wird beim EINHAENGEN festgelegt und kommt nie aus dem Aufruf.
Und die Frage "darf diese Person das ueberhaupt" beantwortet nicht
mehr diese Datei, sondern `darfAnlegen` in workspace.js — dieselbe
Auskunft, aus der auch die Oberflaeche ihre Knoepfe baut. Zwei Listen
fuer dieselbe Aussage waren der Grund, warum Spicy Media den
Manager-Knopf nie zu sehen bekam. */
function teamTuer(rolle) {
return (req, res) => {
try {
const person = sitzungLesen(req);
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
/* ZWEI SCHLOESSER, ABSICHTLICH. `siehtAlles` sagt, wer diese Tuer
ueberhaupt sieht; `darfAnlegen` sagt, ob sie fuer genau diese
Rolle offen ist. Faellt eines der beiden weg, haelt das andere. */
if (!siehtAlles(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
if (!darfAnlegen(person).includes(rolle)) {
return res.status(403).json({ fehler: "Diese Rolle legst du nicht an." });
}
const name = String(req.body?.name ?? "").trim();
if (name.length < 2) return res.status(400).json({ fehler: "Name fehlt." });
if (name.length > NAME_MAX) return res.status(400).json({ fehler: "Name ist zu lang." });
const vorhanden = db().prepare(
"SELECT 1 FROM personen WHERE lower(name) = lower(?) AND aktiv = 1").get(name);
if (vorhanden) return res.status(409).json({ fehler: "Diesen Namen gibt es schon." });
const neu = personAnlegen(name, rolle, { ...person, ip: echteIp(req) });
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code });
} catch (fehler) {
console.error(`[workspace] ${rolle} anlegen:`, fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
};
}
personenRouter.post("/workspace/api/manager-anlegen", gleicheHerkunft, teamTuer("manager"));
personenRouter.post("/workspace/api/scout-anlegen", gleicheHerkunft, teamTuer("scout"));
/* Welche Scouts kann ich einem neuen Creator gleich mitgeben?
Dieselbe Regel wie oben, nur lesend -- damit die Oberflaeche genau
@@ -594,10 +628,22 @@ personenRouter.post("/workspace/api/verwaltung/personen", gleicheHerkunft, (req,
/* ERSTER VORBEHALT: Eine Leitung anlegen darf nur DogFather.
Duerfte ein Manager das, koennte er sich einen zweiten Zugang mit
vollen Rechten schaffen -- und waere damit nicht mehr begrenzbar.
"Nur DogFather hat alle endgueltigen Rechte" faengt hier an. */
if (!istDogFather(req.person) && (rolle === "admin" || rolle === "manager")) {
"Nur DogFather hat alle endgueltigen Rechte" faengt hier an.
SEIT DEM 10.09.2026 STEHT DIE ANTWORT NICHT MEHR HIER, sondern in
`darfAnlegen` (workspace.js) — derselben Auskunft, aus der auch
die beiden Team-Tueren und die Oberflaeche ihre Knoepfe bauen.
Vorher stand die Regel an vier Stellen in drei Dateien, und genau
daran ist sie zerbrochen: Zwei davon widersprachen einander, und
Spicy Media bekam den Manager-Knopf nie zu sehen.
Am Ergebnis aendert sich nichts — die Liste sagt fuer DogFather
"alles" und fuer einen Manager "nur Creator", also genau das, was
hier vorher ausgeschrieben stand. Es steht nur noch an einer
Stelle. */
if (!darfAnlegen(req.person).includes(rolle)) {
return res.status(403).json({
fehler: "DogFather und Manager legt nur DogFather selbst an.",
fehler: "Diese Rolle legst du nicht an.",
});
}