Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
247 lines
9.0 KiB
SQL
247 lines
9.0 KiB
SQL
-- 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 $$;
|