Fundament: ISMS-Module entfernt; Craftvia-Rollen, Module, Navigation, i18n-Split
- ISMS-Routen, Actions, Server-/Lib-Code, Komponenten, Prisma-Modelle, Seeds, Importer, Skripte und ISMS-Tests entfernt (Fundament bleibt: Auth, Identity, MFA/WebAuthn, RBAC, Audit, Mail, Storage, Backup/DSGVO, Plattform-Admin) - Schema auf Fundament-Modelle reduziert; TenantSettings generisch (+phone/email) - TENANT_MODELS (db.ts, backup/topology.ts) und PII-Felder ausgedünnt - RBAC: Rollen tenant-admin/backoffice/team-lead/technician + Craftvia-Permissions - Modul-Katalog (customers, sites, teams, work_orders, imports, field, reports, emergency, documents, notifications, lotse) + Navigation aus src/lib/nav.ts - Modul-Routen mit requireModule-Layout und Platzhalterseite - Message-Katalog je Namespace (messages/<locale>/<namespace>.json), fs-Loader - check-module-guards: Modul-Key aus src/server/actions/<moduleKey>/ - Provisionierung, Admin-Konsole, Einstellungen, Files-Route, Mail entkoppelt - Seed minimal (demo/demo2, Nutzer je Rolle); Fundament-Tests auf Role/ NotificationPreference-Fixtures umgestellt Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,24 @@
|
||||
import { requireSession } from "@/server/auth";
|
||||
import { dbForTenant } from "@/server/db";
|
||||
import { storage } from "@/server/storage/adapter";
|
||||
|
||||
/**
|
||||
* Download-Route für hochgeladene Nachweise/Richtlinien (Epic S1). Liefert die im
|
||||
* Objektspeicher (MinIO/S3) abgelegten Bytes anhand ihres Storage-Keys aus.
|
||||
* Download-Route für Dateien im Objektspeicher (Garage/S3) anhand ihres Storage-Keys.
|
||||
*
|
||||
* Mandanten-Isolation (mehrschichtig):
|
||||
* 1. Der Key ist mandantenpräfixiert (`<tenantId>/uploads/…`). Er MUSS mit dem
|
||||
* Tenant der aktuellen Session beginnen — ein Fremd-Tenant-Key wird mit 404
|
||||
* abgewiesen (keine Existenz-Preisgabe).
|
||||
* 2. Zusätzlich (Defense in Depth) muss der Key in einer mandantengebundenen
|
||||
* Referenz vorkommen (`Evidence.fileRef` oder `PolicyRequirement.nachweisLink`);
|
||||
* so werden nur tatsächlich referenzierte Objekte ausgeliefert, keine geratenen.
|
||||
* Mandanten-Isolation:
|
||||
* Der Key ist mandantenpräfixiert (`<tenantId>/…`). Er MUSS mit dem Tenant der
|
||||
* aktuellen Session beginnen — ein Fremd-Tenant-Key wird mit 404 abgewiesen
|
||||
* (keine Existenz-Preisgabe).
|
||||
*
|
||||
* TODO(documents): Defense in Depth wiederherstellen — sobald das Craftvia-Document-Modell
|
||||
* existiert, zusätzlich prüfen, dass der Key in einer mandantengebundenen Referenz
|
||||
* (Document.storageKey) vorkommt und der Nutzer das Dokument sehen darf
|
||||
* (document:read bzw. document:read_internal für interne Dokumente).
|
||||
*
|
||||
* Auslieferung mit `Content-Disposition: attachment` und `X-Content-Type-Options:
|
||||
* nosniff` (F-07) — kein Inline-Rendering, kein MIME-Sniffing.
|
||||
*
|
||||
* Route-Handler laufen NICHT durch das Layout-Gate; die Auth wird hier eigenständig
|
||||
* über `requireSession` erzwungen (Autorisierung = Mandantenbindung des Keys).
|
||||
* über `requireSession` erzwungen.
|
||||
*/
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
@@ -33,6 +33,7 @@ export async function GET(
|
||||
|
||||
// Pfad-Traversal ausschließen und Mandantenpräfix erzwingen.
|
||||
if (
|
||||
!tenantId ||
|
||||
!key ||
|
||||
key.includes("..") ||
|
||||
key.includes("\0") ||
|
||||
@@ -41,16 +42,6 @@ export async function GET(
|
||||
return new Response("Nicht gefunden.", { status: 404 });
|
||||
}
|
||||
|
||||
// Defense in Depth: Key muss in einer mandantengebundenen Referenz vorkommen.
|
||||
const db = dbForTenant(tenantId);
|
||||
const [evidence, requirement] = await Promise.all([
|
||||
db.evidence.findFirst({ where: { fileRef: key }, select: { id: true } }),
|
||||
db.policyRequirement.findFirst({ where: { nachweisLink: key }, select: { reqId: true } }),
|
||||
]);
|
||||
if (!evidence && !requirement) {
|
||||
return new Response("Nicht gefunden.", { status: 404 });
|
||||
}
|
||||
|
||||
const content = await storage.get(key);
|
||||
if (!content) {
|
||||
// Kein Byte-Backend (Stub) oder Objekt fehlt → 404.
|
||||
|
||||
Reference in New Issue
Block a user