Workspace: Dateiablage mit Freigabe-Ablauf (Phase 1 vollstaendig)
/workspace/dateien.html -- Ablegen per Ziehen oder Auswaehlen, Entwurf -> Review -> Freigabe, Filter je Zustand. OHNE NEUE ABHAENGIGKEIT. Uploads laufen ueblicherweise ueber multer. Hier schickt der Browser die Datei roh im Rumpf, der Name steht im Kopf (URI-kodiert, damit Umlaute heil ankommen). express.raw() bringt den passenden Empfaenger schon mit -- kein Zerlegen von multipart/form-data von Hand, was gerade hier heikel waere. Die drei Punkte, an denen Dateiablagen typischerweise scheitern: 1. Die Dateien liegen AUSSERHALB des Repos (workspace-daten/dateien/). Lagen sie darin, wuerde express.static sie ungeprueft ans Netz geben. 2. Auf der Platte traegt jede Datei einen erzeugten Zufallsnamen, der Originalname steht nur in der Datenbank. Geprueft mit dem Dateinamen "../../etc/passwd": gespeichert wurde "passwd", auf der Platte ein Zufallsname IM Ordner -- nichts ist ausgebrochen. 3. Ausgeliefert wird immer als Download mit neutralem Typ, dazu nosniff und CSP sandbox. Geprueft mit einer hochgeladenen boese.html: kommt als application/octet-stream zurueck, kann also keinen Code im Namen der Domain ausfuehren. Freigabe nach Konzept: Der Zustand "freigegeben" ist eine Abnahme und bleibt dem Management vorbehalten. Geprueft -- Luna kann Entwurf -> Review, aber nicht freigeben (403); eine freigegebene Datei kann sie weder aendern noch loeschen. Sichtbarkeit wie ueberall: Chef 2 Dateien, Luna 1, Mika 0. Zugriff auf eine fremde Datei: 404, nicht 403. Ein Fehler beim Testen gefunden: Bei zu grosser Datei brach express.raw ab, BEVOR der eigene Code lief -- der Fehler landete im allgemeinen Behandler als HTTP 500. Fuer die Nutzerin sah das aus wie ein kaputter Server statt wie "Datei zu gross". Jetzt faengt ein eigener Fehlerbehandler am Ende des Routers das ab und antwortet mit 413 und einer verstaendlichen Meldung. Damit ist Phase 1 aus dem Konzept vollstaendig.
This commit is contained in:
@@ -43,6 +43,11 @@ const require = createRequire(pathToFileURL(__dirname + "/"));
|
||||
const DB_PFAD = process.env.WORKSPACE_DB
|
||||
|| join(__dirname, "..", "..", "workspace-daten", "workspace.db");
|
||||
|
||||
/* Ordner fuer hochgeladene Dateien -- neben der Datenbank, also ebenfalls
|
||||
ausserhalb des Repos. Waeren sie im Repo, wuerde express.static sie
|
||||
ungeschuetzt ausliefern. */
|
||||
export const DATEN_ORDNER = dirname(DB_PFAD);
|
||||
|
||||
const COOKIE = "dfw_sitzung";
|
||||
const SITZUNG_STUNDEN = 12;
|
||||
const VERSUCHE_MAX = 8; // pro IP
|
||||
@@ -171,6 +176,27 @@ export function db() {
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_termine_beginn ON termine (beginn);
|
||||
|
||||
/* Dateiablage. Auf der Platte traegt jede Datei einen erzeugten
|
||||
Zufallsnamen (name_datei), der Originalname steht nur hier.
|
||||
Dadurch kann ein Dateiname weder Pfade verlassen noch etwas
|
||||
ueberschreiben. Der Freigabe-Ablauf aus dem Konzept steckt in
|
||||
status: entwurf -> review -> freigegeben. */
|
||||
CREATE TABLE IF NOT EXISTS dateien (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name_original TEXT NOT NULL,
|
||||
name_datei TEXT NOT NULL UNIQUE,
|
||||
groesse INTEGER NOT NULL,
|
||||
typ TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'entwurf'
|
||||
CHECK (status IN ('entwurf','review','freigegeben')),
|
||||
notiz TEXT,
|
||||
creator_id INTEGER REFERENCES personen(id) ON DELETE SET NULL,
|
||||
hochgeladen_von INTEGER REFERENCES personen(id) ON DELETE SET NULL,
|
||||
erstellt TEXT NOT NULL,
|
||||
geaendert TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_dateien_creator ON dateien (creator_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS versuche (
|
||||
ip TEXT NOT NULL,
|
||||
zeitpunkt TEXT NOT NULL
|
||||
@@ -305,6 +331,7 @@ const GESCHUETZT = {
|
||||
"/workspace/personen.html": ["admin"],
|
||||
"/workspace/profil.html": ["admin", "creator"],
|
||||
"/workspace/kalender.html": null,
|
||||
"/workspace/dateien.html": null,
|
||||
};
|
||||
|
||||
workspaceRouter.use((req, res, next) => {
|
||||
|
||||
Reference in New Issue
Block a user