-- CreateEnum CREATE TYPE "IncidentCategory" AS ENUM ('malware', 'phishing', 'unauthorized_access', 'data_loss', 'outage', 'physical', 'misconfiguration', 'supplier', 'prototype_customer_data', 'other'); -- CreateEnum CREATE TYPE "IncidentStatus" AS ENUM ('neu', 'triage', 'in_bearbeitung', 'eingedaemmt', 'behoben', 'abgeschlossen', 'wiedereroeffnet'); -- AlterTable ALTER TABLE "tenant_settings" ADD COLUMN "nis2_category" TEXT NOT NULL DEFAULT 'keine'; -- CreateTable CREATE TABLE "incidents" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "ref_no" TEXT NOT NULL, "title" TEXT NOT NULL, "description" TEXT, "source" TEXT NOT NULL DEFAULT 'manual', "reporter_name" TEXT, "reporter_contact" TEXT, "occurred_at" TIMESTAMP(3), "detected_at" TIMESTAMP(3), "reported_at" TIMESTAMP(3), "category" "IncidentCategory" NOT NULL DEFAULT 'other', "impact_c" INTEGER NOT NULL DEFAULT 0, "impact_i" INTEGER NOT NULL DEFAULT 0, "impact_a" INTEGER NOT NULL DEFAULT 0, "urgency" INTEGER NOT NULL DEFAULT 2, "affected_data_categories" TEXT[] DEFAULT ARRAY[]::TEXT[], "personal_data" BOOLEAN NOT NULL DEFAULT false, "prototype_data" BOOLEAN NOT NULL DEFAULT false, "severity" TEXT NOT NULL DEFAULT 'mittel', "priority" TEXT NOT NULL DEFAULT 'mittel', "owner_id" TEXT, "assignee_id" TEXT, "status" "IncidentStatus" NOT NULL DEFAULT 'neu', "restricted" BOOLEAN NOT NULL DEFAULT false, "nis2_relevant" BOOLEAN NOT NULL DEFAULT false, "dsgvo_relevant" BOOLEAN NOT NULL DEFAULT false, "report_status" TEXT NOT NULL DEFAULT 'none', "erstmeldung_due_at" TIMESTAMP(3), "folgemeldung_due_at" TIMESTAMP(3), "abschluss_due_at" TIMESTAMP(3), "dsgvo_due_at" TIMESTAMP(3), "root_cause" TEXT, "resolution" TEXT, "closing_note" TEXT, "lessons_learned" TEXT, "created_by" TEXT, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "incidents_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_comments" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "author_id" TEXT, "body" TEXT NOT NULL, "internal" BOOLEAN NOT NULL DEFAULT false, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL, CONSTRAINT "incident_comments_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_assets" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "asset_id" TEXT NOT NULL, CONSTRAINT "incident_assets_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_processes" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "process_id" TEXT NOT NULL, CONSTRAINT "incident_processes_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_risks" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "risk_id" TEXT NOT NULL, CONSTRAINT "incident_risks_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_controls" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "control_ref" TEXT NOT NULL, CONSTRAINT "incident_controls_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_measures" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "measure_id" TEXT NOT NULL, CONSTRAINT "incident_measures_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "incident_attachments" ( "id" TEXT NOT NULL, "tenant_id" TEXT NOT NULL, "incident_id" TEXT NOT NULL, "name" TEXT NOT NULL, "storage_key" TEXT, "size" INTEGER, "mime" TEXT, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "incident_attachments_pkey" PRIMARY KEY ("id") ); -- CreateIndex CREATE INDEX "incidents_tenant_id_idx" ON "incidents"("tenant_id"); -- CreateIndex CREATE INDEX "incidents_tenant_id_status_idx" ON "incidents"("tenant_id", "status"); -- CreateIndex CREATE INDEX "incidents_tenant_id_severity_idx" ON "incidents"("tenant_id", "severity"); -- CreateIndex CREATE UNIQUE INDEX "incidents_tenant_id_ref_no_key" ON "incidents"("tenant_id", "ref_no"); -- CreateIndex CREATE INDEX "incident_comments_tenant_id_incident_id_idx" ON "incident_comments"("tenant_id", "incident_id"); -- CreateIndex CREATE INDEX "incident_assets_tenant_id_idx" ON "incident_assets"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "incident_assets_incident_id_asset_id_key" ON "incident_assets"("incident_id", "asset_id"); -- CreateIndex CREATE INDEX "incident_processes_tenant_id_idx" ON "incident_processes"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "incident_processes_incident_id_process_id_key" ON "incident_processes"("incident_id", "process_id"); -- CreateIndex CREATE INDEX "incident_risks_tenant_id_idx" ON "incident_risks"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "incident_risks_incident_id_risk_id_key" ON "incident_risks"("incident_id", "risk_id"); -- CreateIndex CREATE INDEX "incident_controls_tenant_id_idx" ON "incident_controls"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "incident_controls_incident_id_control_ref_key" ON "incident_controls"("incident_id", "control_ref"); -- CreateIndex CREATE INDEX "incident_measures_tenant_id_idx" ON "incident_measures"("tenant_id"); -- CreateIndex CREATE UNIQUE INDEX "incident_measures_incident_id_measure_id_key" ON "incident_measures"("incident_id", "measure_id"); -- CreateIndex CREATE INDEX "incident_attachments_tenant_id_incident_id_idx" ON "incident_attachments"("tenant_id", "incident_id"); -- AddForeignKey ALTER TABLE "incidents" ADD CONSTRAINT "incidents_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incidents" ADD CONSTRAINT "incidents_assignee_id_fkey" FOREIGN KEY ("assignee_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_comments" ADD CONSTRAINT "incident_comments_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_assets" ADD CONSTRAINT "incident_assets_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_assets" ADD CONSTRAINT "incident_assets_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_processes" ADD CONSTRAINT "incident_processes_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_processes" ADD CONSTRAINT "incident_processes_process_id_fkey" FOREIGN KEY ("process_id") REFERENCES "processes"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_risks" ADD CONSTRAINT "incident_risks_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_risks" ADD CONSTRAINT "incident_risks_risk_id_fkey" FOREIGN KEY ("risk_id") REFERENCES "risks"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_controls" ADD CONSTRAINT "incident_controls_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_measures" ADD CONSTRAINT "incident_measures_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_measures" ADD CONSTRAINT "incident_measures_measure_id_fkey" FOREIGN KEY ("measure_id") REFERENCES "measures"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "incident_attachments" ADD CONSTRAINT "incident_attachments_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- Row Level Security (zweite Verteidigungslinie, analog übrige Mandanten-Tabellen; -- FORCE + WITH CHECK für den scharfen isms_app-Pfad, F-04). Lokal (Owner-Rolle ohne -- FORCE) bleibt der Betrieb unverändert. DO $$ DECLARE t text; BEGIN FOREACH t IN ARRAY ARRAY[ 'incidents', 'incident_comments', 'incident_assets', 'incident_processes', 'incident_risks', 'incident_controls', 'incident_measures', 'incident_attachments' ] LOOP EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); EXECUTE format('DROP POLICY IF EXISTS tenant_isolation ON %I', t); EXECUTE format( 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true)) WITH CHECK (tenant_id = current_setting(''app.tenant_id'', true))', t ); EXECUTE format('ALTER TABLE %I FORCE ROW LEVEL SECURITY', t); END LOOP; END $$;