From 6f3f5a5947ccf9a492f6de239f3a56e6a3c675d4 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 14 Sep 2026 11:40:34 +0200 Subject: [PATCH] Fundament: Baseline-Migration mit RLS-Funktion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 67 Certvia-Migrationen zu prisma/migrations/0001_baseline gesquasht (prisma migrate diff --from-empty --to-schema) - RLS-Block: Rolle craftvia_app (NOLOGIN NOBYPASSRLS, idempotent), GRANTs + ALTER DEFAULT PRIVILEGES, wiederverwendbare Funktion enable_tenant_rls(tbl) (ENABLE + Policy tenant_isolation USING/WITH CHECK + FORCE + GRANT), angewendet auf alle Tenant-Tabellen (inkl. nullable tenant_id wie bisher) - docs/craftvia/MIGRATIONS.md: Pflichten für neue Tenant-Tabellen (enable_tenant_rls + beide TENANT_MODELS-Listen + PII-Felder) Co-Authored-By: Claude Opus 5 --- docs/craftvia/MIGRATIONS.md | 71 +++ prisma/migrations/0001_baseline/migration.sql | 502 ++++++++++++++++++ .../20260702095800_init/migration.sql | 131 ----- .../migration.sql | 36 -- .../20260702101647_assets_bia/migration.sql | 132 ----- .../migration.sql | 18 - .../migration.sql | 74 --- .../20260702133003_rls_risks/migration.sql | 9 - .../20260702135246_measures/migration.sql | 59 -- .../20260702135301_rls_measures/migration.sql | 9 - .../migration.sql | 32 -- .../20260703081059_suppliers/migration.sql | 260 --------- .../migration.sql | 16 - .../migration.sql | 239 --------- .../20260707132008_policies/migration.sql | 136 ----- .../migration.sql | 156 ------ .../migration.sql | 5 - .../migration.sql | 3 - .../migration.sql | 78 --- .../migration.sql | 46 -- .../migration.sql | 6 - .../migration.sql | 22 - .../20260722092625_tasks/migration.sql | 56 -- .../migration.sql | 9 - .../migration.sql | 55 -- .../migration.sql | 91 ---- .../migration.sql | 6 - .../migration.sql | 32 -- .../migration.sql | 12 - .../20260728122213_wizard_facts/migration.sql | 29 - .../20260728153225_wizard_scope/migration.sql | 30 -- .../migration.sql | 25 - .../migration.sql | 27 - .../20260729101027_risk_catalog/migration.sql | 28 - .../migration.sql | 5 - .../migration.sql | 35 -- .../migration.sql | 3 - .../migration.sql | 36 -- .../migration.sql | 9 - .../20260730160000_rls_enforce/migration.sql | 391 -------------- .../migration.sql | 14 - .../migration.sql | 84 --- .../migration.sql | 57 -- .../migration.sql | 38 -- .../migration.sql | 15 - .../migration.sql | 3 - .../migration.sql | 136 ----- .../migration.sql | 18 - .../migration.sql | 18 - .../migration.sql | 3 - .../migration.sql | 94 ---- .../migration.sql | 3 - .../migration.sql | 158 ------ .../migration.sql | 54 -- .../migration.sql | 35 -- .../migration.sql | 20 - .../migration.sql | 12 - .../migration.sql | 40 -- .../20260812100000_backup_jobs/migration.sql | 28 - .../migration.sql | 13 - .../20260817130000_incidents/migration.sql | 246 --------- .../migration.sql | 43 -- .../migration.sql | 67 --- .../migration.sql | 67 --- .../20260821100000_soa_entries/migration.sql | 37 -- .../20260821110000_mgmt_clauses/migration.sql | 175 ------ .../20260821120000_doc_control/migration.sql | 68 --- .../migration.sql | 35 -- 68 files changed, 573 insertions(+), 3927 deletions(-) create mode 100644 docs/craftvia/MIGRATIONS.md create mode 100644 prisma/migrations/0001_baseline/migration.sql delete mode 100644 prisma/migrations/20260702095800_init/migration.sql delete mode 100644 prisma/migrations/20260702095816_row_level_security/migration.sql delete mode 100644 prisma/migrations/20260702101647_assets_bia/migration.sql delete mode 100644 prisma/migrations/20260702101701_rls_assets_bia/migration.sql delete mode 100644 prisma/migrations/20260702132947_risks_and_process_category/migration.sql delete mode 100644 prisma/migrations/20260702133003_rls_risks/migration.sql delete mode 100644 prisma/migrations/20260702135246_measures/migration.sql delete mode 100644 prisma/migrations/20260702135301_rls_measures/migration.sql delete mode 100644 prisma/migrations/20260702144829_decimal_reductions_and_catalogs/migration.sql delete mode 100644 prisma/migrations/20260703081059_suppliers/migration.sql delete mode 100644 prisma/migrations/20260703081120_rls_suppliers/migration.sql delete mode 100644 prisma/migrations/20260703113715_suppliers_as_assets/migration.sql delete mode 100644 prisma/migrations/20260707132008_policies/migration.sql delete mode 100644 prisma/migrations/20260707153909_managed_tables/migration.sql delete mode 100644 prisma/migrations/20260707163523_policy_approval/migration.sql delete mode 100644 prisma/migrations/20260708111123_policy_protection_override/migration.sql delete mode 100644 prisma/migrations/20260719203024_admin_console/migration.sql delete mode 100644 prisma/migrations/20260720183635_platform_admins/migration.sql delete mode 100644 prisma/migrations/20260721113710_policy_lifecycle_archived/migration.sql delete mode 100644 prisma/migrations/20260722071432_user_mgmt_and_platform_settings/migration.sql delete mode 100644 prisma/migrations/20260722092625_tasks/migration.sql delete mode 100644 prisma/migrations/20260723071617_strip_tool_variable_articles/migration.sql delete mode 100644 prisma/migrations/20260723090004_managed_registers/migration.sql delete mode 100644 prisma/migrations/20260724095408_software_and_project_assets/migration.sql delete mode 100644 prisma/migrations/20260728104412_tasks_wizard_fields/migration.sql delete mode 100644 prisma/migrations/20260728105241_onboarding_progress/migration.sql delete mode 100644 prisma/migrations/20260728115109_object_review_status/migration.sql delete mode 100644 prisma/migrations/20260728122213_wizard_facts/migration.sql delete mode 100644 prisma/migrations/20260728153225_wizard_scope/migration.sql delete mode 100644 prisma/migrations/20260728154046_implementation_hints/migration.sql delete mode 100644 prisma/migrations/20260728161306_policy_package_state/migration.sql delete mode 100644 prisma/migrations/20260729101027_risk_catalog/migration.sql delete mode 100644 prisma/migrations/20260729103725_risk_acceptance/migration.sql delete mode 100644 prisma/migrations/20260729120000_control_assessments/migration.sql delete mode 100644 prisma/migrations/20260729140000_task_description/migration.sql delete mode 100644 prisma/migrations/20260729150000_control_implementations/migration.sql delete mode 100644 prisma/migrations/20260730111105_login_lockout/migration.sql delete mode 100644 prisma/migrations/20260730160000_rls_enforce/migration.sql delete mode 100644 prisma/migrations/20260730170000_mfa_totp_replay/migration.sql delete mode 100644 prisma/migrations/20260730180000_mail_fundament/migration.sql delete mode 100644 prisma/migrations/20260730190000_auth_tokens_sessions/migration.sql delete mode 100644 prisma/migrations/20260731120000_webauthn_credentials/migration.sql delete mode 100644 prisma/migrations/20260731123409_tisax_m0_foundation/migration.sql delete mode 100644 prisma/migrations/20260731130000_platform_admin_role/migration.sql delete mode 100644 prisma/migrations/20260731152213_tisax_m1_m4_consolidated/migration.sql delete mode 100644 prisma/migrations/20260731154215_tisax_rekey_onboarding_steps/migration.sql delete mode 100644 prisma/migrations/20260731165818_tisax_v3_process_fields/migration.sql delete mode 100644 prisma/migrations/20260801134128_tisax_v4_catalog_parent/migration.sql delete mode 100644 prisma/migrations/20260804111342_tisax_v5_audit/migration.sql delete mode 100644 prisma/migrations/20260805093503_tisax_v7_policy_domain/migration.sql delete mode 100644 prisma/migrations/20260810120000_policy_templates/migration.sql delete mode 100644 prisma/migrations/20260811120000_identity_foundation/migration.sql delete mode 100644 prisma/migrations/20260811140000_webauthn_identity/migration.sql delete mode 100644 prisma/migrations/20260811150000_contract_user_auth_columns/migration.sql delete mode 100644 prisma/migrations/20260811160000_identity_ui_locale/migration.sql delete mode 100644 prisma/migrations/20260812090000_backup_dsgvo_foundation/migration.sql delete mode 100644 prisma/migrations/20260812100000_backup_jobs/migration.sql delete mode 100644 prisma/migrations/20260817120000_backup_target_config/migration.sql delete mode 100644 prisma/migrations/20260817130000_incidents/migration.sql delete mode 100644 prisma/migrations/20260818090000_incident_links_c/migration.sql delete mode 100644 prisma/migrations/20260818100000_incident_inbound_d/migration.sql delete mode 100644 prisma/migrations/20260821090000_framework_core/migration.sql delete mode 100644 prisma/migrations/20260821100000_soa_entries/migration.sql delete mode 100644 prisma/migrations/20260821110000_mgmt_clauses/migration.sql delete mode 100644 prisma/migrations/20260821120000_doc_control/migration.sql delete mode 100644 prisma/migrations/20260907084110_add_process_dependencies/migration.sql diff --git a/docs/craftvia/MIGRATIONS.md b/docs/craftvia/MIGRATIONS.md new file mode 100644 index 0000000..baac95b --- /dev/null +++ b/docs/craftvia/MIGRATIONS.md @@ -0,0 +1,71 @@ +# Craftvia — Migrationen & Row Level Security + +## Ausgangslage + +Die Certvia-Historie (67 Migrationen) wurde beim Rückbau zu **einer** Baseline gesquasht: + +- `prisma/migrations/0001_baseline/migration.sql` + - DDL aus `npx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script` + - angehängter RLS-Block (Rolle `craftvia_app`, GRANTs, Funktion `enable_tenant_rls`) + +Bestehende Datenbanken aus der Certvia-Zeit sind **nicht** migrierbar — lokal neu aufsetzen: + +```bash +docker compose exec postgres psql -U craftvia -d postgres -c 'DROP DATABASE IF EXISTS craftvia WITH (FORCE);' -c 'CREATE DATABASE craftvia;' +npx prisma migrate deploy +npx prisma db seed +``` + +## Mandantentrennung — drei Stellen, die immer zusammen gepflegt werden + +Jede neue Tabelle mit `tenant_id` (z. B. `customers`, `work_orders`) braucht: + +1. **Migration**: nach dem `CREATE TABLE` im selben Migrationsfile + + ```sql + SELECT enable_tenant_rls('customers'); + ``` + + Die Funktion ist idempotent und setzt `ENABLE ROW LEVEL SECURITY`, die Policy + `tenant_isolation` (`USING` **und** `WITH CHECK` auf + `tenant_id = current_setting('app.tenant_id', true)`), `FORCE ROW LEVEL SECURITY` + und die GRANTs für `craftvia_app`. + +2. **`TENANT_MODELS` in `src/server/db.ts`** — aktiviert den Tenant-Guard + (`dbForTenant` filtert/injiziert `tenantId`, prüft Ownership bei Mutationen). + +3. **`TENANT_MODELS` in `src/server/backup/topology.ts`** — Backup/Restore/DSGVO-Export. + Die Liste ist bewusst dupliziert; `scripts/test-backup-isolation.ts` bzw. + `scripts/test-rls-enforcement.ts` prüfen Synchronität und RLS-Abdeckung gegen die DB. + +Zusätzlich: Felder, die eine `User.id` referenzieren (Zuweisung, Freigabe, Ersteller), +in `src/server/dsgvo/pii-fields.ts` eintragen. + +Globale Tabellen (Kataloge ohne `tenant_id`, z. B. künftige Materialstammdaten des +Plattformbetreibers) bekommen **keine** Policy und stehen in keiner TENANT_MODELS-Liste. + +## Nullable `tenant_id` + +`audit_logs`, `mail_logs` und `auth_tokens` tragen eine nullable `tenant_id` +(Plattform-Ereignisse). Die Policy ist identisch; Zeilen mit `NULL` sind für +`craftvia_app` unsichtbar und werden ausschließlich über den Owner-Client geschrieben. + +## Neue Migration anlegen + +```bash +# Schema ändern, dann: +npx prisma migrate dev --name --create-only +# migration.sql prüfen und ggf. `SELECT enable_tenant_rls('');` anhängen +npx prisma migrate dev +``` + +## RLS scharf testen (lokal) + +```bash +docker compose exec postgres psql -U craftvia -d craftvia \ + -c "ALTER ROLE craftvia_app WITH LOGIN PASSWORD 'craftvia_app_local';" +npx tsx scripts/test-rls-enforcement.ts +``` + +Für den Betrieb mit scharfer RLS: `RLS_ENFORCED=true` und +`RLS_DATABASE_URL=postgresql://craftvia_app:@:5432/craftvia?schema=public`. diff --git a/prisma/migrations/0001_baseline/migration.sql b/prisma/migrations/0001_baseline/migration.sql new file mode 100644 index 0000000..d864e4b --- /dev/null +++ b/prisma/migrations/0001_baseline/migration.sql @@ -0,0 +1,502 @@ +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "public"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "vector"; + +-- CreateEnum +CREATE TYPE "TenantStatus" AS ENUM ('ACTIVE', 'SUSPENDED', 'ARCHIVED'); + +-- CreateEnum +CREATE TYPE "UserStatus" AS ENUM ('ACTIVE', 'INVITED', 'LOCKED', 'DEACTIVATED'); + +-- CreateTable +CREATE TABLE "tenants" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "short" TEXT, + "sector" TEXT, + "status" "TenantStatus" NOT NULL DEFAULT 'ACTIVE', + "config" JSONB NOT NULL DEFAULT '{}', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "tenants_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tenant_settings" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "org_name" TEXT NOT NULL, + "org_short" TEXT, + "address" TEXT, + "phone" TEXT, + "email" TEXT, + "sector" TEXT, + "logo_key" TEXT, + "accent" TEXT, + "locale" TEXT NOT NULL DEFAULT 'de', + "timezone" TEXT NOT NULL DEFAULT 'Europe/Berlin', + "security_policy" JSONB NOT NULL DEFAULT '{}', + "smtp" JSONB NOT NULL DEFAULT '{}', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "tenant_settings_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tenant_modules" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "module_key" TEXT NOT NULL, + "enabled" BOOLEAN NOT NULL DEFAULT true, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "tenant_modules_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "identities" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password_hash" TEXT NOT NULL, + "must_change_password" BOOLEAN NOT NULL DEFAULT false, + "mfa_secret" TEXT, + "mfa_enrolled_at" TIMESTAMP(3), + "recovery_codes" JSONB NOT NULL DEFAULT '[]', + "last_totp_step" BIGINT, + "failed_logins" INTEGER NOT NULL DEFAULT 0, + "locked_until" TIMESTAMP(3), + "sessions_valid_after" TIMESTAMP(3), + "status" TEXT NOT NULL DEFAULT 'ACTIVE', + "ui_locale" TEXT NOT NULL DEFAULT 'de', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "identities_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "users" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "identity_id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT NOT NULL, + "status" "UserStatus" NOT NULL DEFAULT 'ACTIVE', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "webauthn_credentials" ( + "id" TEXT NOT NULL, + "identity_id" TEXT NOT NULL, + "credential_id" TEXT NOT NULL, + "public_key" TEXT NOT NULL, + "counter" BIGINT NOT NULL DEFAULT 0, + "transports" TEXT[] DEFAULT ARRAY[]::TEXT[], + "device_name" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "last_used_at" TIMESTAMP(3), + + CONSTRAINT "webauthn_credentials_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "roles" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "key" TEXT NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "roles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "permissions" ( + "id" TEXT NOT NULL, + "key" TEXT NOT NULL, + + CONSTRAINT "permissions_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "user_roles" ( + "user_id" TEXT NOT NULL, + "role_id" TEXT NOT NULL, + + CONSTRAINT "user_roles_pkey" PRIMARY KEY ("user_id","role_id") +); + +-- CreateTable +CREATE TABLE "role_permissions" ( + "role_id" TEXT NOT NULL, + "permission_id" TEXT NOT NULL, + + CONSTRAINT "role_permissions_pkey" PRIMARY KEY ("role_id","permission_id") +); + +-- CreateTable +CREATE TABLE "platform_admins" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password_hash" TEXT NOT NULL, + "name" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'ACTIVE', + "role" TEXT NOT NULL DEFAULT 'full', + "mfa_secret" TEXT, + "mfa_enrolled_at" TIMESTAMP(3), + "recovery_codes" JSONB NOT NULL DEFAULT '[]', + "failed_logins" INTEGER NOT NULL DEFAULT 0, + "locked_until" TIMESTAMP(3), + "last_totp_step" BIGINT, + "last_login_at" TIMESTAMP(3), + "sessions_valid_after" TIMESTAMP(3), + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "platform_admins_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "platform_settings" ( + "id" TEXT NOT NULL DEFAULT 'singleton', + "mfa_required" BOOLEAN NOT NULL DEFAULT false, + "backup_target" TEXT NOT NULL DEFAULT 'local', + "backup_local_dir" TEXT, + "backup_s3_endpoint" TEXT, + "backup_s3_bucket" TEXT, + "backup_s3_region" TEXT, + "backup_s3_access_key" TEXT, + "backup_s3_secret_key_enc" TEXT, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "platform_settings_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "audit_logs" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT, + "scope" TEXT NOT NULL DEFAULT 'tenant', + "actor_id" TEXT, + "action" TEXT NOT NULL, + "entity" TEXT NOT NULL, + "entity_id" TEXT, + "before" JSONB, + "after" JSONB, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "mail_logs" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT, + "scope" TEXT NOT NULL DEFAULT 'tenant', + "to" TEXT NOT NULL, + "template" TEXT NOT NULL, + "locale" TEXT NOT NULL DEFAULT 'de', + "status" TEXT NOT NULL DEFAULT 'pending', + "provider_message_id" TEXT, + "error" TEXT, + "attempts" INTEGER NOT NULL DEFAULT 0, + "dedupe_key" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "sent_at" TIMESTAMP(3), + + CONSTRAINT "mail_logs_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "notification_preferences" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "event_type" TEXT NOT NULL, + "email" BOOLEAN NOT NULL DEFAULT true, + "locale" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "notification_preferences_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth_tokens" ( + "id" TEXT NOT NULL, + "principal_type" TEXT NOT NULL, + "principal_id" TEXT NOT NULL, + "tenant_id" TEXT, + "type" TEXT NOT NULL, + "token_hash" TEXT NOT NULL, + "new_email" TEXT, + "expires_at" TIMESTAMP(3) NOT NULL, + "used_at" TIMESTAMP(3), + "request_ip" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "auth_tokens_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "deletion_certificates" ( + "id" TEXT NOT NULL, + "scope" TEXT NOT NULL, + "tenant_id" TEXT, + "tenant_slug" TEXT, + "subject_identity_id" TEXT, + "subject_email" TEXT, + "actor_id" TEXT, + "deleted_counts" JSONB NOT NULL DEFAULT '{}', + "anonymized_counts" JSONB NOT NULL DEFAULT '{}', + "snapshot_id" TEXT, + "reason" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "deletion_certificates_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tombstone_entries" ( + "id" TEXT NOT NULL, + "tenant_id" TEXT NOT NULL, + "model" TEXT NOT NULL, + "target_field" TEXT NOT NULL, + "target_value" TEXT NOT NULL, + "action" TEXT NOT NULL, + "anonymized_fields" JSONB NOT NULL DEFAULT '{}', + "reason" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "tombstone_entries_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "backup_jobs" ( + "id" TEXT NOT NULL, + "kind" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'queued', + "tenant_id" TEXT NOT NULL, + "tenant_slug" TEXT, + "subject_identity_id" TEXT, + "actor_id" TEXT, + "snapshot_id" TEXT, + "params" JSONB NOT NULL DEFAULT '{}', + "result" JSONB, + "error" TEXT, + "download_token" TEXT, + "download_expires_at" TIMESTAMP(3), + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "backup_jobs_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "tenants_slug_key" ON "tenants"("slug"); + +-- CreateIndex +CREATE UNIQUE INDEX "tenant_settings_tenant_id_key" ON "tenant_settings"("tenant_id"); + +-- CreateIndex +CREATE INDEX "tenant_settings_tenant_id_idx" ON "tenant_settings"("tenant_id"); + +-- CreateIndex +CREATE INDEX "tenant_modules_tenant_id_idx" ON "tenant_modules"("tenant_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "tenant_modules_tenant_id_module_key_key" ON "tenant_modules"("tenant_id", "module_key"); + +-- CreateIndex +CREATE UNIQUE INDEX "identities_email_key" ON "identities"("email"); + +-- CreateIndex +CREATE INDEX "users_tenant_id_idx" ON "users"("tenant_id"); + +-- CreateIndex +CREATE INDEX "users_identity_id_idx" ON "users"("identity_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_tenant_id_email_key" ON "users"("tenant_id", "email"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_tenant_id_identity_id_key" ON "users"("tenant_id", "identity_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "webauthn_credentials_credential_id_key" ON "webauthn_credentials"("credential_id"); + +-- CreateIndex +CREATE INDEX "webauthn_credentials_identity_id_idx" ON "webauthn_credentials"("identity_id"); + +-- CreateIndex +CREATE INDEX "roles_tenant_id_idx" ON "roles"("tenant_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "roles_tenant_id_key_key" ON "roles"("tenant_id", "key"); + +-- CreateIndex +CREATE UNIQUE INDEX "permissions_key_key" ON "permissions"("key"); + +-- CreateIndex +CREATE UNIQUE INDEX "platform_admins_email_key" ON "platform_admins"("email"); + +-- CreateIndex +CREATE INDEX "audit_logs_tenant_id_entity_entity_id_idx" ON "audit_logs"("tenant_id", "entity", "entity_id"); + +-- CreateIndex +CREATE INDEX "audit_logs_tenant_id_created_at_idx" ON "audit_logs"("tenant_id", "created_at"); + +-- CreateIndex +CREATE UNIQUE INDEX "mail_logs_dedupe_key_key" ON "mail_logs"("dedupe_key"); + +-- CreateIndex +CREATE INDEX "mail_logs_tenant_id_created_at_idx" ON "mail_logs"("tenant_id", "created_at"); + +-- CreateIndex +CREATE INDEX "mail_logs_status_idx" ON "mail_logs"("status"); + +-- CreateIndex +CREATE INDEX "notification_preferences_tenant_id_idx" ON "notification_preferences"("tenant_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "notification_preferences_user_id_event_type_key" ON "notification_preferences"("user_id", "event_type"); + +-- CreateIndex +CREATE UNIQUE INDEX "auth_tokens_token_hash_key" ON "auth_tokens"("token_hash"); + +-- CreateIndex +CREATE INDEX "auth_tokens_principal_type_principal_id_type_idx" ON "auth_tokens"("principal_type", "principal_id", "type"); + +-- CreateIndex +CREATE INDEX "auth_tokens_expires_at_idx" ON "auth_tokens"("expires_at"); + +-- CreateIndex +CREATE INDEX "deletion_certificates_tenant_id_idx" ON "deletion_certificates"("tenant_id"); + +-- CreateIndex +CREATE INDEX "deletion_certificates_subject_identity_id_idx" ON "deletion_certificates"("subject_identity_id"); + +-- CreateIndex +CREATE INDEX "tombstone_entries_tenant_id_idx" ON "tombstone_entries"("tenant_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "tombstone_entries_tenant_id_model_target_value_key" ON "tombstone_entries"("tenant_id", "model", "target_value"); + +-- CreateIndex +CREATE UNIQUE INDEX "backup_jobs_download_token_key" ON "backup_jobs"("download_token"); + +-- CreateIndex +CREATE INDEX "backup_jobs_tenant_id_idx" ON "backup_jobs"("tenant_id"); + +-- CreateIndex +CREATE INDEX "backup_jobs_status_idx" ON "backup_jobs"("status"); + +-- AddForeignKey +ALTER TABLE "tenant_settings" ADD CONSTRAINT "tenant_settings_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "tenant_modules" ADD CONSTRAINT "tenant_modules_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_identity_id_fkey" FOREIGN KEY ("identity_id") REFERENCES "identities"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "webauthn_credentials" ADD CONSTRAINT "webauthn_credentials_identity_id_fkey" FOREIGN KEY ("identity_id") REFERENCES "identities"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "roles" ADD CONSTRAINT "roles_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_permission_id_fkey" FOREIGN KEY ("permission_id") REFERENCES "permissions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- ============================================================================ +-- Row Level Security (Craftvia-Baseline) +-- ============================================================================ +-- Zweite Verteidigungslinie hinter dem Tenant-Guard (src/server/db.ts). +-- +-- App-Rolle `craftvia_app` (NOLOGIN, NOBYPASSRLS): Mit RLS_ENFORCED=true verbindet sich +-- die App als diese Rolle (RLS_DATABASE_URL) und setzt `app.tenant_id` transaktionslokal. +-- LOGIN + Passwort sind ein SECRET und werden NICHT hier gesetzt, sondern operativ: +-- ALTER ROLE craftvia_app WITH LOGIN PASSWORD ''; +-- +-- Owner-/Migrationsrolle: Superuser bzw. BYPASSRLS — RLS greift für sie auch unter FORCE +-- nie. Migrationen, Seed und der mandantenübergreifende Login-Lookup bleiben funktionsfähig. +-- +-- Nullable tenant_id (audit_logs, mail_logs, auth_tokens): identische Policy. Zeilen mit +-- tenant_id = NULL (Plattform-Ereignisse) erfüllen die Bedingung nie und sind für +-- craftvia_app weder les- noch schreibbar — sie werden ausschließlich über den Owner-Client +-- geschrieben (beabsichtigt, wie im Certvia-Fundament). +-- +-- Neue Tenant-Tabellen in künftigen Migrationen: `SELECT enable_tenant_rls('');` +-- (siehe docs/craftvia/MIGRATIONS.md). +-- ============================================================================ + +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'craftvia_app') THEN + CREATE ROLE craftvia_app NOLOGIN NOBYPASSRLS; + END IF; +END +$$; + +GRANT USAGE ON SCHEMA public TO craftvia_app; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO craftvia_app; +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO craftvia_app; +ALTER DEFAULT PRIVILEGES IN SCHEMA public + GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO craftvia_app; +ALTER DEFAULT PRIVILEGES IN SCHEMA public + GRANT USAGE, SELECT ON SEQUENCES TO craftvia_app; + +-- Wiederverwendbar und idempotent: ENABLE + Policy (USING + WITH CHECK) + FORCE. +-- WITH CHECK ist zwingend: ohne sie lehnt Postgres unter FORCE jeden INSERT/UPDATE der +-- App-Rolle ab bzw. erlaubt das Umschreiben einer Zeile auf einen fremden Mandanten. +-- Ungesetzter Kontext → current_setting(..., true) = NULL → 0 Zeilen (fail-closed). +CREATE OR REPLACE FUNCTION enable_tenant_rls(tbl text) RETURNS void +LANGUAGE plpgsql AS $fn$ +BEGIN + EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', tbl); + EXECUTE format('DROP POLICY IF EXISTS tenant_isolation ON %I', tbl); + 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))', + tbl + ); + EXECUTE format('ALTER TABLE %I FORCE ROW LEVEL SECURITY', tbl); + EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON %I TO craftvia_app', tbl); +END +$fn$; + +-- Tenant-Tabellen (Spiegel von TENANT_MODELS in src/server/db.ts / backup/topology.ts) +SELECT enable_tenant_rls('users'); +SELECT enable_tenant_rls('roles'); +SELECT enable_tenant_rls('audit_logs'); +SELECT enable_tenant_rls('mail_logs'); +SELECT enable_tenant_rls('notification_preferences'); +SELECT enable_tenant_rls('auth_tokens'); +SELECT enable_tenant_rls('tenant_settings'); +SELECT enable_tenant_rls('tenant_modules'); diff --git a/prisma/migrations/20260702095800_init/migration.sql b/prisma/migrations/20260702095800_init/migration.sql deleted file mode 100644 index 2e56fd5..0000000 --- a/prisma/migrations/20260702095800_init/migration.sql +++ /dev/null @@ -1,131 +0,0 @@ --- CreateExtension -CREATE EXTENSION IF NOT EXISTS "vector"; - --- CreateEnum -CREATE TYPE "TenantStatus" AS ENUM ('ACTIVE', 'SUSPENDED'); - --- CreateEnum -CREATE TYPE "UserStatus" AS ENUM ('ACTIVE', 'INVITED', 'LOCKED', 'DEACTIVATED'); - --- CreateTable -CREATE TABLE "tenants" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "slug" TEXT NOT NULL, - "status" "TenantStatus" NOT NULL DEFAULT 'ACTIVE', - "config" JSONB NOT NULL DEFAULT '{}', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "tenants_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "users" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "password_hash" TEXT NOT NULL, - "name" TEXT NOT NULL, - "status" "UserStatus" NOT NULL DEFAULT 'ACTIVE', - "mfa_secret" TEXT, - "is_platform_admin" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "roles" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "key" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "roles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "permissions" ( - "id" TEXT NOT NULL, - "key" TEXT NOT NULL, - - CONSTRAINT "permissions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "user_roles" ( - "user_id" TEXT NOT NULL, - "role_id" TEXT NOT NULL, - - CONSTRAINT "user_roles_pkey" PRIMARY KEY ("user_id","role_id") -); - --- CreateTable -CREATE TABLE "role_permissions" ( - "role_id" TEXT NOT NULL, - "permission_id" TEXT NOT NULL, - - CONSTRAINT "role_permissions_pkey" PRIMARY KEY ("role_id","permission_id") -); - --- CreateTable -CREATE TABLE "audit_logs" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "actor_id" TEXT, - "action" TEXT NOT NULL, - "entity" TEXT NOT NULL, - "entity_id" TEXT, - "before" JSONB, - "after" JSONB, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "tenants_slug_key" ON "tenants"("slug"); - --- CreateIndex -CREATE INDEX "users_tenant_id_idx" ON "users"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "users_tenant_id_email_key" ON "users"("tenant_id", "email"); - --- CreateIndex -CREATE INDEX "roles_tenant_id_idx" ON "roles"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "roles_tenant_id_key_key" ON "roles"("tenant_id", "key"); - --- CreateIndex -CREATE UNIQUE INDEX "permissions_key_key" ON "permissions"("key"); - --- CreateIndex -CREATE INDEX "audit_logs_tenant_id_entity_entity_id_idx" ON "audit_logs"("tenant_id", "entity", "entity_id"); - --- CreateIndex -CREATE INDEX "audit_logs_tenant_id_created_at_idx" ON "audit_logs"("tenant_id", "created_at"); - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "roles" ADD CONSTRAINT "roles_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_permission_id_fkey" FOREIGN KEY ("permission_id") REFERENCES "permissions"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20260702095816_row_level_security/migration.sql b/prisma/migrations/20260702095816_row_level_security/migration.sql deleted file mode 100644 index 4027740..0000000 --- a/prisma/migrations/20260702095816_row_level_security/migration.sql +++ /dev/null @@ -1,36 +0,0 @@ --- Row Level Security als zweite Verteidigungslinie (docs/SPEC.md §2). --- Primäre Isolation ist der Tenant-Guard in src/server/db.ts. --- --- Aktivierungsstand: Policies sind definiert; die App verbindet sich aktuell --- noch als Tabellen-Owner (Policies greifen für Owner ohne FORCE nicht). --- Härtungs-Iteration (SPEC §11 Punkt 8): App-Verbindung auf die Rolle --- "isms_app" umstellen und app.tenant_id pro Transaktion setzen — dann sind --- die Policies scharf. FORCE wird bewusst nicht gesetzt, damit Migrationen, --- Seeds und der mandantenübergreifende Login-Lookup als Owner funktionieren. - --- Eingeschränkte Anwendungsrolle (für die Härtungs-Iteration) -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'isms_app') THEN - CREATE ROLE isms_app NOLOGIN NOBYPASSRLS; - END IF; -END -$$; - -GRANT USAGE ON SCHEMA public TO isms_app; -GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO isms_app; -ALTER DEFAULT PRIVILEGES IN SCHEMA public - GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO isms_app; - --- RLS auf allen mandanten-gebundenen Tabellen -ALTER TABLE "users" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "roles" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "audit_logs" ENABLE ROW LEVEL SECURITY; - --- Isolation: Zugriff nur auf Zeilen des in app.tenant_id gesetzten Mandanten -CREATE POLICY tenant_isolation ON "users" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "roles" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "audit_logs" - USING ("tenant_id" = current_setting('app.tenant_id', true)); diff --git a/prisma/migrations/20260702101647_assets_bia/migration.sql b/prisma/migrations/20260702101647_assets_bia/migration.sql deleted file mode 100644 index 62846c8..0000000 --- a/prisma/migrations/20260702101647_assets_bia/migration.sql +++ /dev/null @@ -1,132 +0,0 @@ --- CreateEnum -CREATE TYPE "AssetType" AS ENUM ('INFORMATION', 'SYSTEM', 'APPLICATION', 'LOCATION', 'SUPPLIER', 'PERSON', 'DATA'); - --- CreateEnum -CREATE TYPE "AssetStatus" AS ENUM ('ACTIVE', 'PLANNED', 'RETIRED'); - --- CreateEnum -CREATE TYPE "ProcessAssetRole" AS ENUM ('PRIMARY', 'SECONDARY'); - --- CreateTable -CREATE TABLE "assets" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "type" "AssetType" NOT NULL, - "status" "AssetStatus" NOT NULL DEFAULT 'ACTIVE', - "owner_id" TEXT, - "location" TEXT, - "tags" TEXT[] DEFAULT ARRAY[]::TEXT[], - "confidentiality" INTEGER NOT NULL DEFAULT 1, - "integrity" INTEGER NOT NULL DEFAULT 1, - "availability" INTEGER NOT NULL DEFAULT 1, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "assets_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "asset_relations" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "related_asset_id" TEXT NOT NULL, - "type" TEXT NOT NULL DEFAULT 'depends_on', - - CONSTRAINT "asset_relations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "processes" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "owner_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "processes_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "process_assets" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "process_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "role" "ProcessAssetRole" NOT NULL DEFAULT 'SECONDARY', - - CONSTRAINT "process_assets_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "bia_entries" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "process_id" TEXT NOT NULL, - "rto_hours" INTEGER, - "rpo_hours" INTEGER, - "mtd_hours" INTEGER, - "impact_c" INTEGER NOT NULL DEFAULT 1, - "impact_i" INTEGER NOT NULL DEFAULT 1, - "impact_a" INTEGER NOT NULL DEFAULT 1, - "criticality" INTEGER NOT NULL DEFAULT 1, - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "bia_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "assets_tenant_id_idx" ON "assets"("tenant_id"); - --- CreateIndex -CREATE INDEX "assets_tenant_id_type_idx" ON "assets"("tenant_id", "type"); - --- CreateIndex -CREATE INDEX "asset_relations_tenant_id_idx" ON "asset_relations"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "asset_relations_asset_id_related_asset_id_type_key" ON "asset_relations"("asset_id", "related_asset_id", "type"); - --- CreateIndex -CREATE INDEX "processes_tenant_id_idx" ON "processes"("tenant_id"); - --- CreateIndex -CREATE INDEX "process_assets_tenant_id_idx" ON "process_assets"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "process_assets_process_id_asset_id_key" ON "process_assets"("process_id", "asset_id"); - --- CreateIndex -CREATE UNIQUE INDEX "bia_entries_process_id_key" ON "bia_entries"("process_id"); - --- CreateIndex -CREATE INDEX "bia_entries_tenant_id_idx" ON "bia_entries"("tenant_id"); - --- AddForeignKey -ALTER TABLE "assets" ADD CONSTRAINT "assets_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "asset_relations" ADD CONSTRAINT "asset_relations_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "asset_relations" ADD CONSTRAINT "asset_relations_related_asset_id_fkey" FOREIGN KEY ("related_asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "processes" ADD CONSTRAINT "processes_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "process_assets" ADD CONSTRAINT "process_assets_process_id_fkey" FOREIGN KEY ("process_id") REFERENCES "processes"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "process_assets" ADD CONSTRAINT "process_assets_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "bia_entries" ADD CONSTRAINT "bia_entries_process_id_fkey" FOREIGN KEY ("process_id") REFERENCES "processes"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260702101701_rls_assets_bia/migration.sql b/prisma/migrations/20260702101701_rls_assets_bia/migration.sql deleted file mode 100644 index 303f54e..0000000 --- a/prisma/migrations/20260702101701_rls_assets_bia/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- RLS-Policies für die Assets-&-BIA-Tabellen (analog zur Migration row_level_security) - -ALTER TABLE "assets" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "asset_relations" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "processes" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "process_assets" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "bia_entries" ENABLE ROW LEVEL SECURITY; - -CREATE POLICY tenant_isolation ON "assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "asset_relations" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "processes" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "process_assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "bia_entries" - USING ("tenant_id" = current_setting('app.tenant_id', true)); diff --git a/prisma/migrations/20260702132947_risks_and_process_category/migration.sql b/prisma/migrations/20260702132947_risks_and_process_category/migration.sql deleted file mode 100644 index 9ed3855..0000000 --- a/prisma/migrations/20260702132947_risks_and_process_category/migration.sql +++ /dev/null @@ -1,74 +0,0 @@ --- CreateEnum -CREATE TYPE "ProcessCategory" AS ENUM ('CORE', 'MANAGEMENT', 'SUPPORT'); - --- CreateEnum -CREATE TYPE "RiskTreatment" AS ENUM ('AVOID', 'MITIGATE', 'TRANSFER', 'ACCEPT'); - --- CreateEnum -CREATE TYPE "RiskStatus" AS ENUM ('OPEN', 'IN_TREATMENT', 'ACCEPTED', 'CLOSED'); - --- AlterTable -ALTER TABLE "processes" ADD COLUMN "category" "ProcessCategory" NOT NULL DEFAULT 'CORE'; - --- CreateTable -CREATE TABLE "risks" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT, - "threat" TEXT, - "vulnerability" TEXT, - "likelihood" INTEGER NOT NULL DEFAULT 1, - "impact" INTEGER NOT NULL DEFAULT 1, - "score" INTEGER NOT NULL DEFAULT 1, - "residual_likelihood" INTEGER, - "residual_impact" INTEGER, - "residual_score" INTEGER, - "treatment" "RiskTreatment" NOT NULL DEFAULT 'MITIGATE', - "status" "RiskStatus" NOT NULL DEFAULT 'OPEN', - "owner_id" TEXT, - "process_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "risks_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "risk_assets" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "risk_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - - CONSTRAINT "risk_assets_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "risks_tenant_id_idx" ON "risks"("tenant_id"); - --- CreateIndex -CREATE INDEX "risks_tenant_id_status_idx" ON "risks"("tenant_id", "status"); - --- CreateIndex -CREATE UNIQUE INDEX "risks_tenant_id_ref_no_key" ON "risks"("tenant_id", "ref_no"); - --- CreateIndex -CREATE INDEX "risk_assets_tenant_id_idx" ON "risk_assets"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "risk_assets_risk_id_asset_id_key" ON "risk_assets"("risk_id", "asset_id"); - --- AddForeignKey -ALTER TABLE "risks" ADD CONSTRAINT "risks_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "risks" ADD CONSTRAINT "risks_process_id_fkey" FOREIGN KEY ("process_id") REFERENCES "processes"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "risk_assets" ADD CONSTRAINT "risk_assets_risk_id_fkey" FOREIGN KEY ("risk_id") REFERENCES "risks"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "risk_assets" ADD CONSTRAINT "risk_assets_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260702133003_rls_risks/migration.sql b/prisma/migrations/20260702133003_rls_risks/migration.sql deleted file mode 100644 index f542644..0000000 --- a/prisma/migrations/20260702133003_rls_risks/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ --- RLS-Policies für die Risiko-Tabellen (analog zu den vorherigen RLS-Migrationen) - -ALTER TABLE "risks" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "risk_assets" ENABLE ROW LEVEL SECURITY; - -CREATE POLICY tenant_isolation ON "risks" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "risk_assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)); diff --git a/prisma/migrations/20260702135246_measures/migration.sql b/prisma/migrations/20260702135246_measures/migration.sql deleted file mode 100644 index d55f998..0000000 --- a/prisma/migrations/20260702135246_measures/migration.sql +++ /dev/null @@ -1,59 +0,0 @@ --- CreateEnum -CREATE TYPE "MeasureStatus" AS ENUM ('OPEN', 'IN_PROGRESS', 'DONE'); - --- CreateEnum -CREATE TYPE "MeasurePriority" AS ENUM ('LOW', 'MEDIUM', 'HIGH'); - --- CreateTable -CREATE TABLE "measures" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT, - "status" "MeasureStatus" NOT NULL DEFAULT 'OPEN', - "priority" "MeasurePriority" NOT NULL DEFAULT 'MEDIUM', - "owner_id" TEXT, - "due_date" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "measures_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "risk_measures" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "risk_id" TEXT NOT NULL, - "measure_id" TEXT NOT NULL, - "reduction_likelihood" INTEGER NOT NULL DEFAULT 0, - "reduction_impact" INTEGER NOT NULL DEFAULT 0, - - CONSTRAINT "risk_measures_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "measures_tenant_id_idx" ON "measures"("tenant_id"); - --- CreateIndex -CREATE INDEX "measures_tenant_id_status_idx" ON "measures"("tenant_id", "status"); - --- CreateIndex -CREATE UNIQUE INDEX "measures_tenant_id_ref_no_key" ON "measures"("tenant_id", "ref_no"); - --- CreateIndex -CREATE INDEX "risk_measures_tenant_id_idx" ON "risk_measures"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "risk_measures_risk_id_measure_id_key" ON "risk_measures"("risk_id", "measure_id"); - --- AddForeignKey -ALTER TABLE "measures" ADD CONSTRAINT "measures_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "risk_measures" ADD CONSTRAINT "risk_measures_risk_id_fkey" FOREIGN KEY ("risk_id") REFERENCES "risks"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "risk_measures" ADD CONSTRAINT "risk_measures_measure_id_fkey" FOREIGN KEY ("measure_id") REFERENCES "measures"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260702135301_rls_measures/migration.sql b/prisma/migrations/20260702135301_rls_measures/migration.sql deleted file mode 100644 index c6708f4..0000000 --- a/prisma/migrations/20260702135301_rls_measures/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ --- RLS-Policies für die Maßnahmen-Tabellen - -ALTER TABLE "measures" ENABLE ROW LEVEL SECURITY; -ALTER TABLE "risk_measures" ENABLE ROW LEVEL SECURITY; - -CREATE POLICY tenant_isolation ON "measures" - USING ("tenant_id" = current_setting('app.tenant_id', true)); -CREATE POLICY tenant_isolation ON "risk_measures" - USING ("tenant_id" = current_setting('app.tenant_id', true)); diff --git a/prisma/migrations/20260702144829_decimal_reductions_and_catalogs/migration.sql b/prisma/migrations/20260702144829_decimal_reductions_and_catalogs/migration.sql deleted file mode 100644 index 0480c08..0000000 --- a/prisma/migrations/20260702144829_decimal_reductions_and_catalogs/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ --- AlterTable -ALTER TABLE "risk_measures" ALTER COLUMN "reduction_likelihood" SET DEFAULT 0, -ALTER COLUMN "reduction_likelihood" SET DATA TYPE DOUBLE PRECISION, -ALTER COLUMN "reduction_impact" SET DEFAULT 0, -ALTER COLUMN "reduction_impact" SET DATA TYPE DOUBLE PRECISION; - --- AlterTable -ALTER TABLE "risks" ALTER COLUMN "residual_likelihood" SET DATA TYPE DOUBLE PRECISION, -ALTER COLUMN "residual_impact" SET DATA TYPE DOUBLE PRECISION, -ALTER COLUMN "residual_score" SET DATA TYPE DOUBLE PRECISION; - --- CreateTable -CREATE TABLE "threats" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "threats_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "vulnerabilities" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "vulnerabilities_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "threats_name_key" ON "threats"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "vulnerabilities_name_key" ON "vulnerabilities"("name"); diff --git a/prisma/migrations/20260703081059_suppliers/migration.sql b/prisma/migrations/20260703081059_suppliers/migration.sql deleted file mode 100644 index 1cc6df3..0000000 --- a/prisma/migrations/20260703081059_suppliers/migration.sql +++ /dev/null @@ -1,260 +0,0 @@ --- CreateEnum -CREATE TYPE "SupplierStatus" AS ENUM ('ACTIVE', 'ONBOARDING', 'UNDER_REVIEW', 'OFFBOARDED'); - --- CreateEnum -CREATE TYPE "AssessmentType" AS ENUM ('QUESTIONNAIRE', 'SELF_ASSESSMENT', 'AUDIT'); - --- CreateEnum -CREATE TYPE "AssessmentStatus" AS ENUM ('SENT', 'RECEIVED', 'EVALUATED', 'OVERDUE'); - --- CreateEnum -CREATE TYPE "EvidenceKind" AS ENUM ('CERTIFICATE', 'TISAX_LABEL', 'ATTESTATION', 'AUDIT_REPORT', 'SELF_ASSESSMENT'); - --- CreateEnum -CREATE TYPE "ResponsibleParty" AS ENUM ('CLIENT', 'SUPPLIER', 'SHARED'); - --- CreateTable -CREATE TABLE "suppliers" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "name" TEXT NOT NULL, - "sector" TEXT, - "services" TEXT, - "criticality" INTEGER NOT NULL DEFAULT 1, - "data_categories" TEXT[] DEFAULT ARRAY[]::TEXT[], - "confidentiality" INTEGER NOT NULL DEFAULT 1, - "integrity" INTEGER NOT NULL DEFAULT 1, - "availability" INTEGER NOT NULL DEFAULT 1, - "nis2_relevant" BOOLEAN NOT NULL DEFAULT false, - "status" "SupplierStatus" NOT NULL DEFAULT 'ACTIVE', - "contact" TEXT, - "next_review" TIMESTAMP(3), - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "suppliers_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "supplier_assets" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - - CONSTRAINT "supplier_assets_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "supplier_assessments" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "type" "AssessmentType" NOT NULL, - "status" "AssessmentStatus" NOT NULL DEFAULT 'SENT', - "score" INTEGER, - "date" TIMESTAMP(3), - "next_review" TIMESTAMP(3), - "result" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "supplier_assessments_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "contracts" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "type" TEXT NOT NULL DEFAULT 'service', - "av_dpa" BOOLEAN NOT NULL DEFAULT false, - "security_clauses" BOOLEAN NOT NULL DEFAULT false, - "flowdown" BOOLEAN NOT NULL DEFAULT false, - "customer_transparency" BOOLEAN NOT NULL DEFAULT false, - "valid_from" TIMESTAMP(3), - "valid_to" TIMESTAMP(3), - "reference" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "contracts_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "ndas" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "parties" TEXT, - "info_scope" TEXT, - "subject" TEXT, - "valid_from" TIMESTAMP(3), - "valid_to" TIMESTAMP(3), - "obligations" TEXT, - "extension_status" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "ndas_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "supplier_evidence" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "kind" "EvidenceKind" NOT NULL, - "name" TEXT, - "protects_cia" TEXT, - "valid_to" TIMESTAMP(3), - "adequacy_checked" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "supplier_evidence_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "service_responsibilities" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "it_service" TEXT NOT NULL, - "requirement" TEXT NOT NULL, - "responsible_party" "ResponsibleParty" NOT NULL DEFAULT 'SHARED', - "isa_applicability" TEXT, - "evidence" TEXT, - "integrated_local_controls" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "service_responsibilities_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "subcontractors" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "flowdown_obligation" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "subcontractors_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "management_decisions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "reason_no_audit" TEXT NOT NULL, - "decision" TEXT NOT NULL, - "decided_by" TEXT, - "date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "record_ref" TEXT, - - CONSTRAINT "management_decisions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "supplier_controls" ( - "id" TEXT NOT NULL, - "ref" TEXT NOT NULL, - "title" TEXT NOT NULL, - "objective" TEXT NOT NULL, - "must_req" TEXT, - "should_req" TEXT, - "high_req" TEXT, - "very_high_req" TEXT, - "simplified_group_assessment" BOOLEAN NOT NULL DEFAULT false, - "target_maturity" INTEGER NOT NULL DEFAULT 3, - "references" TEXT, - - CONSTRAINT "supplier_controls_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "supplier_control_maturity" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "supplier_id" TEXT NOT NULL, - "control_id" TEXT NOT NULL, - "maturity" INTEGER NOT NULL DEFAULT 0, - "notes" TEXT, - - CONSTRAINT "supplier_control_maturity_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "suppliers_tenant_id_idx" ON "suppliers"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "suppliers_tenant_id_ref_no_key" ON "suppliers"("tenant_id", "ref_no"); - --- CreateIndex -CREATE INDEX "supplier_assets_tenant_id_idx" ON "supplier_assets"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "supplier_assets_supplier_id_asset_id_key" ON "supplier_assets"("supplier_id", "asset_id"); - --- CreateIndex -CREATE INDEX "supplier_assessments_tenant_id_idx" ON "supplier_assessments"("tenant_id"); - --- CreateIndex -CREATE INDEX "contracts_tenant_id_idx" ON "contracts"("tenant_id"); - --- CreateIndex -CREATE INDEX "ndas_tenant_id_idx" ON "ndas"("tenant_id"); - --- CreateIndex -CREATE INDEX "supplier_evidence_tenant_id_idx" ON "supplier_evidence"("tenant_id"); - --- CreateIndex -CREATE INDEX "service_responsibilities_tenant_id_idx" ON "service_responsibilities"("tenant_id"); - --- CreateIndex -CREATE INDEX "subcontractors_tenant_id_idx" ON "subcontractors"("tenant_id"); - --- CreateIndex -CREATE INDEX "management_decisions_tenant_id_idx" ON "management_decisions"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "supplier_controls_ref_key" ON "supplier_controls"("ref"); - --- CreateIndex -CREATE INDEX "supplier_control_maturity_tenant_id_idx" ON "supplier_control_maturity"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "supplier_control_maturity_supplier_id_control_id_key" ON "supplier_control_maturity"("supplier_id", "control_id"); - --- AddForeignKey -ALTER TABLE "supplier_assets" ADD CONSTRAINT "supplier_assets_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_assets" ADD CONSTRAINT "supplier_assets_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_assessments" ADD CONSTRAINT "supplier_assessments_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "contracts" ADD CONSTRAINT "contracts_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "ndas" ADD CONSTRAINT "ndas_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_evidence" ADD CONSTRAINT "supplier_evidence_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "service_responsibilities" ADD CONSTRAINT "service_responsibilities_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "subcontractors" ADD CONSTRAINT "subcontractors_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "management_decisions" ADD CONSTRAINT "management_decisions_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_control_maturity" ADD CONSTRAINT "supplier_control_maturity_supplier_id_fkey" FOREIGN KEY ("supplier_id") REFERENCES "suppliers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_control_maturity" ADD CONSTRAINT "supplier_control_maturity_control_id_fkey" FOREIGN KEY ("control_id") REFERENCES "supplier_controls"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260703081120_rls_suppliers/migration.sql b/prisma/migrations/20260703081120_rls_suppliers/migration.sql deleted file mode 100644 index 8bef651..0000000 --- a/prisma/migrations/20260703081120_rls_suppliers/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ --- RLS-Policies für die Lieferanten-Tabellen (analog zu row_level_security) -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY[ - 'suppliers','supplier_assets','supplier_assessments','contracts','ndas', - 'supplier_evidence','service_responsibilities','subcontractors', - 'management_decisions','supplier_control_maturity' - ] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format( - 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', - t - ); - END LOOP; -END $$; diff --git a/prisma/migrations/20260703113715_suppliers_as_assets/migration.sql b/prisma/migrations/20260703113715_suppliers_as_assets/migration.sql deleted file mode 100644 index 106a816..0000000 --- a/prisma/migrations/20260703113715_suppliers_as_assets/migration.sql +++ /dev/null @@ -1,239 +0,0 @@ --- CreateEnum -CREATE TYPE "SupplierLifecycle" AS ENUM ('ACTIVE', 'ONBOARDING', 'UNDER_REVIEW', 'OFFBOARDED'); - --- CreateEnum -CREATE TYPE "RaciResponsibility" AS ENUM ('PROVIDER', 'US', 'SHARED'); - --- AlterEnum -ALTER TYPE "AssetType" ADD VALUE 'IT_SERVICE'; - --- DropForeignKey -ALTER TABLE "contracts" DROP CONSTRAINT "contracts_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "management_decisions" DROP CONSTRAINT "management_decisions_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "ndas" DROP CONSTRAINT "ndas_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "service_responsibilities" DROP CONSTRAINT "service_responsibilities_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "subcontractors" DROP CONSTRAINT "subcontractors_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_assessments" DROP CONSTRAINT "supplier_assessments_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_assets" DROP CONSTRAINT "supplier_assets_asset_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_assets" DROP CONSTRAINT "supplier_assets_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_control_maturity" DROP CONSTRAINT "supplier_control_maturity_control_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_control_maturity" DROP CONSTRAINT "supplier_control_maturity_supplier_id_fkey"; - --- DropForeignKey -ALTER TABLE "supplier_evidence" DROP CONSTRAINT "supplier_evidence_supplier_id_fkey"; - --- AlterTable -ALTER TABLE "contracts" DROP COLUMN "customer_transparency", -DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL, -ADD COLUMN "customer_requirements_passed" BOOLEAN NOT NULL DEFAULT false; - --- AlterTable -ALTER TABLE "management_decisions" DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL; - --- AlterTable -ALTER TABLE "ndas" DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL, -ADD COLUMN "beyond_term" BOOLEAN NOT NULL DEFAULT false; - --- AlterTable -ALTER TABLE "subcontractors" DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL; - --- AlterTable -ALTER TABLE "supplier_assessments" DROP COLUMN "status", -DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL, -ADD COLUMN "self_score" INTEGER, -ADD COLUMN "verified_score" INTEGER; - --- AlterTable -ALTER TABLE "supplier_evidence" DROP COLUMN "supplier_id", -ADD COLUMN "asset_id" TEXT NOT NULL; - --- DropTable -DROP TABLE "service_responsibilities"; - --- DropTable -DROP TABLE "supplier_assets"; - --- DropTable -DROP TABLE "supplier_control_maturity"; - --- DropTable -DROP TABLE "supplier_controls"; - --- DropTable -DROP TABLE "suppliers"; - --- DropEnum -DROP TYPE "AssessmentStatus"; - --- DropEnum -DROP TYPE "ResponsibleParty"; - --- DropEnum -DROP TYPE "SupplierStatus"; - --- CreateTable -CREATE TABLE "supplier_profiles" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "sector" TEXT, - "service_desc" TEXT, - "criticality" INTEGER NOT NULL DEFAULT 1, - "data_categories" TEXT[] DEFAULT ARRAY[]::TEXT[], - "nis2_relevant" BOOLEAN NOT NULL DEFAULT false, - "lifecycle" "SupplierLifecycle" NOT NULL DEFAULT 'ACTIVE', - "contact" TEXT, - "tisax_label" TEXT, - "tisax_valid_to" TIMESTAMP(3), - "next_review" TIMESTAMP(3), - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "supplier_profiles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "it_service_profiles" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "provider_asset_id" TEXT, - "criticality" INTEGER NOT NULL DEFAULT 1, - "internal" BOOLEAN NOT NULL DEFAULT false, - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "it_service_profiles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "service_control_responsibilities" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "control_ref" TEXT NOT NULL, - "title" TEXT, - "applicable" BOOLEAN NOT NULL DEFAULT true, - "responsibility" "RaciResponsibility" NOT NULL DEFAULT 'SHARED', - "evidence_ref" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "service_control_responsibilities_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "maturity_assessments" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "self_value" DOUBLE PRECISION, - "verified_value" DOUBLE PRECISION, - "computed_value" DOUBLE PRECISION, - "isb_value" DOUBLE PRECISION, - "isb_justification" TEXT, - "approved_by" TEXT, - "approved_at" TIMESTAMP(3), - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "maturity_assessments_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "supplier_profiles_asset_id_key" ON "supplier_profiles"("asset_id"); - --- CreateIndex -CREATE INDEX "supplier_profiles_tenant_id_idx" ON "supplier_profiles"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "supplier_profiles_tenant_id_ref_no_key" ON "supplier_profiles"("tenant_id", "ref_no"); - --- CreateIndex -CREATE UNIQUE INDEX "it_service_profiles_asset_id_key" ON "it_service_profiles"("asset_id"); - --- CreateIndex -CREATE INDEX "it_service_profiles_tenant_id_idx" ON "it_service_profiles"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "it_service_profiles_tenant_id_ref_no_key" ON "it_service_profiles"("tenant_id", "ref_no"); - --- CreateIndex -CREATE INDEX "service_control_responsibilities_tenant_id_idx" ON "service_control_responsibilities"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "maturity_assessments_asset_id_key" ON "maturity_assessments"("asset_id"); - --- CreateIndex -CREATE INDEX "maturity_assessments_tenant_id_idx" ON "maturity_assessments"("tenant_id"); - --- AddForeignKey -ALTER TABLE "supplier_profiles" ADD CONSTRAINT "supplier_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "it_service_profiles" ADD CONSTRAINT "it_service_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "it_service_profiles" ADD CONSTRAINT "it_service_profiles_provider_asset_id_fkey" FOREIGN KEY ("provider_asset_id") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_assessments" ADD CONSTRAINT "supplier_assessments_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "supplier_evidence" ADD CONSTRAINT "supplier_evidence_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "management_decisions" ADD CONSTRAINT "management_decisions_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "contracts" ADD CONSTRAINT "contracts_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "ndas" ADD CONSTRAINT "ndas_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "subcontractors" ADD CONSTRAINT "subcontractors_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "service_control_responsibilities" ADD CONSTRAINT "service_control_responsibilities_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "maturity_assessments" ADD CONSTRAINT "maturity_assessments_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS für neue/umgestellte Lieferanten-Tabellen -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['supplier_profiles','it_service_profiles','maturity_assessments','service_control_responsibilities'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260707132008_policies/migration.sql b/prisma/migrations/20260707132008_policies/migration.sql deleted file mode 100644 index 0175d0a..0000000 --- a/prisma/migrations/20260707132008_policies/migration.sql +++ /dev/null @@ -1,136 +0,0 @@ --- CreateEnum -CREATE TYPE "PolicyDocType" AS ENUM ('LEITLINIE', 'RICHTLINIE', 'VERFAHREN', 'REGISTER', 'HANDBUCH', 'EIGENES'); - --- CreateEnum -CREATE TYPE "PolicyStatus" AS ENUM ('ENTWURF', 'IN_FREIGABE', 'FREIGEGEBEN', 'ARCHIVIERT'); - --- CreateTable -CREATE TABLE "policy_documents" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "type" "PolicyDocType" NOT NULL, - "title" TEXT NOT NULL, - "version" TEXT NOT NULL DEFAULT '1.0', - "status" "PolicyStatus" NOT NULL DEFAULT 'FREIGEGEBEN', - "owner" TEXT, - "policy_code" TEXT, - "fulfills" TEXT[] DEFAULT ARRAY[]::TEXT[], - "raw_markdown" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "policy_documents_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_requirements" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "req_id" TEXT NOT NULL, - "policy_code" TEXT NOT NULL, - "control" TEXT NOT NULL, - "obligation" TEXT NOT NULL, - "condition" TEXT, - "requirement" TEXT NOT NULL, - "implementation" TEXT NOT NULL, - "va_codes" TEXT[] DEFAULT ARRAY[]::TEXT[], - "nachweis_link" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_requirements_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_variables" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "key" TEXT NOT NULL, - "title" TEXT NOT NULL, - "kind" TEXT NOT NULL, - "group_name" TEXT, - "value" TEXT NOT NULL, - "required" BOOLEAN NOT NULL DEFAULT false, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "policy_variables_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_baseline_params" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "bl_id" TEXT NOT NULL, - "section" TEXT NOT NULL, - "name" TEXT NOT NULL, - "vorgabe" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_baseline_params_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_evidence" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "nr" INTEGER NOT NULL, - "policy_code" TEXT NOT NULL, - "nachweis" TEXT NOT NULL, - "quelle" TEXT NOT NULL, - "verantwortlich" TEXT NOT NULL, - "turnus" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_evidence_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "policy_documents_tenant_id_idx" ON "policy_documents"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_documents_tenant_id_code_key" ON "policy_documents"("tenant_id", "code"); - --- CreateIndex -CREATE INDEX "policy_requirements_tenant_id_idx" ON "policy_requirements"("tenant_id"); - --- CreateIndex -CREATE INDEX "policy_requirements_tenant_id_control_idx" ON "policy_requirements"("tenant_id", "control"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_requirements_tenant_id_req_id_key" ON "policy_requirements"("tenant_id", "req_id"); - --- CreateIndex -CREATE INDEX "policy_variables_tenant_id_idx" ON "policy_variables"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_variables_tenant_id_key_key" ON "policy_variables"("tenant_id", "key"); - --- CreateIndex -CREATE INDEX "policy_baseline_params_tenant_id_idx" ON "policy_baseline_params"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_baseline_params_tenant_id_bl_id_key" ON "policy_baseline_params"("tenant_id", "bl_id"); - --- CreateIndex -CREATE INDEX "policy_evidence_tenant_id_idx" ON "policy_evidence"("tenant_id"); - - --- RLS-Policies für die Richtlinien-Tabellen -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY[ - 'policy_documents','policy_requirements','policy_variables', - 'policy_baseline_params','policy_evidence' - ] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format( - 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', - t - ); - END LOOP; -END $$; diff --git a/prisma/migrations/20260707153909_managed_tables/migration.sql b/prisma/migrations/20260707153909_managed_tables/migration.sql deleted file mode 100644 index ec3afb1..0000000 --- a/prisma/migrations/20260707153909_managed_tables/migration.sql +++ /dev/null @@ -1,156 +0,0 @@ --- CreateTable -CREATE TABLE "crypto_entries" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "dienst" TEXT NOT NULL, - "schluessel" TEXT NOT NULL, - "algorithmus" TEXT, - "ablauf_datum" TIMESTAMP(3), - "verantwortlich" TEXT NOT NULL, - "speicherort" TEXT, - "baseline_ref" TEXT, - "notes" TEXT, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "crypto_entries_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "classification_classes" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "classification_classes_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "handling_aspects" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "category" TEXT, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "handling_aspects_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "handling_rules" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "class_id" TEXT NOT NULL, - "aspect_id" TEXT NOT NULL, - "text" TEXT NOT NULL, - - CONSTRAINT "handling_rules_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "risk_matrix_classes" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "max_score" INTEGER NOT NULL, - "acceptance" TEXT NOT NULL, - "tone" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "risk_matrix_classes_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "risk_ew_levels" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "level" INTEGER NOT NULL, - "label" TEXT NOT NULL, - "definition" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "risk_ew_levels_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "risk_damage_dimensions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "levels" JSONB NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "risk_damage_dimensions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "handbook_topics" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "category" TEXT NOT NULL, - "title" TEXT NOT NULL, - "body_md" TEXT NOT NULL, - "source_refs" TEXT[] DEFAULT ARRAY[]::TEXT[], - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "handbook_topics_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "crypto_entries_tenant_id_idx" ON "crypto_entries"("tenant_id"); - --- CreateIndex -CREATE INDEX "classification_classes_tenant_id_idx" ON "classification_classes"("tenant_id"); - --- CreateIndex -CREATE INDEX "handling_aspects_tenant_id_idx" ON "handling_aspects"("tenant_id"); - --- CreateIndex -CREATE INDEX "handling_rules_tenant_id_idx" ON "handling_rules"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "handling_rules_class_id_aspect_id_key" ON "handling_rules"("class_id", "aspect_id"); - --- CreateIndex -CREATE INDEX "risk_matrix_classes_tenant_id_idx" ON "risk_matrix_classes"("tenant_id"); - --- CreateIndex -CREATE INDEX "risk_ew_levels_tenant_id_idx" ON "risk_ew_levels"("tenant_id"); - --- CreateIndex -CREATE INDEX "risk_damage_dimensions_tenant_id_idx" ON "risk_damage_dimensions"("tenant_id"); - --- CreateIndex -CREATE INDEX "handbook_topics_tenant_id_idx" ON "handbook_topics"("tenant_id"); - --- AddForeignKey -ALTER TABLE "handling_rules" ADD CONSTRAINT "handling_rules_class_id_fkey" FOREIGN KEY ("class_id") REFERENCES "classification_classes"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "handling_rules" ADD CONSTRAINT "handling_rules_aspect_id_fkey" FOREIGN KEY ("aspect_id") REFERENCES "handling_aspects"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS-Policies für die verwalteten Register-Tabellen -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY[ - 'crypto_entries','classification_classes','handling_aspects','handling_rules', - 'risk_matrix_classes','risk_ew_levels','risk_damage_dimensions','handbook_topics' - ] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format( - 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', - t - ); - END LOOP; -END $$; diff --git a/prisma/migrations/20260707163523_policy_approval/migration.sql b/prisma/migrations/20260707163523_policy_approval/migration.sql deleted file mode 100644 index 8e8145e..0000000 --- a/prisma/migrations/20260707163523_policy_approval/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "policy_documents" ADD COLUMN "approved_at" TIMESTAMP(3), -ADD COLUMN "approved_by" TEXT, -ADD COLUMN "submitted_by" TEXT; - diff --git a/prisma/migrations/20260708111123_policy_protection_override/migration.sql b/prisma/migrations/20260708111123_policy_protection_override/migration.sql deleted file mode 100644 index 6f3d910..0000000 --- a/prisma/migrations/20260708111123_policy_protection_override/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "policy_documents" ADD COLUMN "protection_override" TEXT; - diff --git a/prisma/migrations/20260719203024_admin_console/migration.sql b/prisma/migrations/20260719203024_admin_console/migration.sql deleted file mode 100644 index 03f0049..0000000 --- a/prisma/migrations/20260719203024_admin_console/migration.sql +++ /dev/null @@ -1,78 +0,0 @@ --- AlterEnum -ALTER TYPE "TenantStatus" ADD VALUE 'ARCHIVED'; - --- AlterTable -ALTER TABLE "audit_logs" ADD COLUMN "scope" TEXT NOT NULL DEFAULT 'tenant'; - --- AlterTable -ALTER TABLE "tenants" ADD COLUMN "sector" TEXT, -ADD COLUMN "short" TEXT; - --- CreateTable -CREATE TABLE "tenant_settings" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "org_name" TEXT NOT NULL, - "org_short" TEXT, - "address" TEXT, - "sector" TEXT, - "duns" TEXT, - "isms_scope" TEXT, - "isms_scope_description" TEXT, - "role_management" TEXT, - "role_isb" TEXT, - "role_it_lead" TEXT, - "role_dpo" TEXT, - "logo_key" TEXT, - "accent" TEXT, - "locale" TEXT NOT NULL DEFAULT 'de', - "timezone" TEXT NOT NULL DEFAULT 'Europe/Berlin', - "tisax_level" TEXT NOT NULL DEFAULT 'AL2', - "security_policy" JSONB NOT NULL DEFAULT '{}', - "smtp" JSONB NOT NULL DEFAULT '{}', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "tenant_settings_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tenant_modules" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "module_key" TEXT NOT NULL, - "enabled" BOOLEAN NOT NULL DEFAULT true, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "tenant_modules_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "tenant_settings_tenant_id_key" ON "tenant_settings"("tenant_id"); - --- CreateIndex -CREATE INDEX "tenant_settings_tenant_id_idx" ON "tenant_settings"("tenant_id"); - --- CreateIndex -CREATE INDEX "tenant_modules_tenant_id_idx" ON "tenant_modules"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "tenant_modules_tenant_id_module_key_key" ON "tenant_modules"("tenant_id", "module_key"); - --- AddForeignKey -ALTER TABLE "tenant_settings" ADD CONSTRAINT "tenant_settings_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tenant_modules" ADD CONSTRAINT "tenant_modules_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS für die neuen mandantenbezogenen Tabellen -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['tenant_settings','tenant_modules'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260720183635_platform_admins/migration.sql b/prisma/migrations/20260720183635_platform_admins/migration.sql deleted file mode 100644 index e92c157..0000000 --- a/prisma/migrations/20260720183635_platform_admins/migration.sql +++ /dev/null @@ -1,46 +0,0 @@ --- DropForeignKey -ALTER TABLE "audit_logs" DROP CONSTRAINT "audit_logs_tenant_id_fkey"; - --- AlterTable -ALTER TABLE "audit_logs" ALTER COLUMN "tenant_id" DROP NOT NULL; - --- CreateTable -CREATE TABLE "platform_admins" ( - "id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "password_hash" TEXT NOT NULL, - "name" TEXT NOT NULL, - "status" TEXT NOT NULL DEFAULT 'ACTIVE', - "mfa_secret" TEXT, - "mfa_enrolled_at" TIMESTAMP(3), - "recovery_codes" JSONB NOT NULL DEFAULT '[]', - "failed_logins" INTEGER NOT NULL DEFAULT 0, - "locked_until" TIMESTAMP(3), - "last_login_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "platform_admins_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "platform_admins_email_key" ON "platform_admins"("email"); - --- AddForeignKey -ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE; - - --- Datenmigration (Phase-1-Härtung Paket 2): bestehende isPlatformAdmin-Nutzer in den --- getrennten PlatformAdmin-Store übernehmen (gleicher Argon2-Hash → Login sofort möglich) --- und das Mandanten-Flag zurücksetzen. MFA wird beim ersten Plattform-Login eingerichtet. -INSERT INTO "platform_admins" ("id", "email", "password_hash", "name", "status", "recovery_codes", "failed_logins", "created_at", "updated_at") -SELECT gen_random_uuid()::text, lower("email"), "password_hash", "name", 'ACTIVE', '[]'::jsonb, 0, now(), now() -FROM "users" -WHERE "is_platform_admin" = true -ON CONFLICT ("email") DO NOTHING; - -UPDATE "users" SET "is_platform_admin" = false WHERE "is_platform_admin" = true; - --- platform_admins ist plattformweit (kein tenant_id) → RLS bewusst NICHT aktiviert. --- Zugriff für die eingeschränkte App-Rolle (Härtungs-Iteration) explizit gewähren. -GRANT SELECT, INSERT, UPDATE, DELETE ON "platform_admins" TO isms_app; diff --git a/prisma/migrations/20260721113710_policy_lifecycle_archived/migration.sql b/prisma/migrations/20260721113710_policy_lifecycle_archived/migration.sql deleted file mode 100644 index 0e691a8..0000000 --- a/prisma/migrations/20260721113710_policy_lifecycle_archived/migration.sql +++ /dev/null @@ -1,6 +0,0 @@ --- AlterTable -ALTER TABLE "policy_documents" ADD COLUMN "archived_at" TIMESTAMP(3); - --- AlterTable -ALTER TABLE "policy_requirements" ADD COLUMN "archived_at" TIMESTAMP(3); - diff --git a/prisma/migrations/20260722071432_user_mgmt_and_platform_settings/migration.sql b/prisma/migrations/20260722071432_user_mgmt_and_platform_settings/migration.sql deleted file mode 100644 index cc20255..0000000 --- a/prisma/migrations/20260722071432_user_mgmt_and_platform_settings/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ --- AlterTable -ALTER TABLE "users" ADD COLUMN "mfa_enrolled_at" TIMESTAMP(3), -ADD COLUMN "must_change_password" BOOLEAN NOT NULL DEFAULT false, -ADD COLUMN "recovery_codes" JSONB NOT NULL DEFAULT '[]'; - --- CreateTable -CREATE TABLE "platform_settings" ( - "id" TEXT NOT NULL DEFAULT 'singleton', - "mfa_required" BOOLEAN NOT NULL DEFAULT false, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "platform_settings_pkey" PRIMARY KEY ("id") -); - - --- platform_settings ist plattformweit (kein tenant_id) → RLS nicht aktiviert. -GRANT SELECT, INSERT, UPDATE, DELETE ON "platform_settings" TO isms_app; - --- Singleton-Zeile anlegen (MFA-Pflicht standardmäßig aus). -INSERT INTO "platform_settings" ("id", "mfa_required", "updated_at") -VALUES ('singleton', false, now()) -ON CONFLICT ("id") DO NOTHING; diff --git a/prisma/migrations/20260722092625_tasks/migration.sql b/prisma/migrations/20260722092625_tasks/migration.sql deleted file mode 100644 index 0fca78d..0000000 --- a/prisma/migrations/20260722092625_tasks/migration.sql +++ /dev/null @@ -1,56 +0,0 @@ --- CreateTable -CREATE TABLE "tasks" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "type" TEXT NOT NULL, - "title" TEXT NOT NULL, - "status" TEXT NOT NULL DEFAULT 'OPEN', - "entity_type" TEXT, - "entity_id" TEXT, - "entity_ref" TEXT, - "assignee_id" TEXT, - "created_by_id" TEXT, - "due_date" TIMESTAMP(3), - "resolved_by_id" TEXT, - "resolved_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "tasks_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "task_comments" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "task_id" TEXT NOT NULL, - "author_id" TEXT, - "kind" TEXT NOT NULL DEFAULT 'comment', - "body" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "task_comments_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "tasks_tenant_id_status_idx" ON "tasks"("tenant_id", "status"); - --- CreateIndex -CREATE INDEX "tasks_tenant_id_assignee_id_status_idx" ON "tasks"("tenant_id", "assignee_id", "status"); - --- CreateIndex -CREATE INDEX "task_comments_tenant_id_task_id_idx" ON "task_comments"("tenant_id", "task_id"); - --- AddForeignKey -ALTER TABLE "task_comments" ADD CONSTRAINT "task_comments_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS für die neuen mandantenbezogenen Tabellen (zweite Verteidigungslinie). -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['tasks','task_comments'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260723071617_strip_tool_variable_articles/migration.sql b/prisma/migrations/20260723071617_strip_tool_variable_articles/migration.sql deleted file mode 100644 index 7ec14a6..0000000 --- a/prisma/migrations/20260723071617_strip_tool_variable_articles/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ --- WP2.2 (Rendering Variante B): führenden Artikel aus den betroffenen Variablen-Werten --- entfernen, damit "im {{VAR}}" nicht mehr zu "im das …" rendert. Nur nicht angepasste --- Werte (= alter Default) werden migriert; kundenindividuelle Werte bleiben unberührt. -UPDATE "policy_variables" SET "value" = 'Ticketsystem' - WHERE "key" = 'TOOL_TICKET' AND "value" = 'das Ticketsystem'; -UPDATE "policy_variables" SET "value" = 'ISMS-Tool' - WHERE "key" = 'TOOL_NAME' AND "value" = 'das ISMS-Tool'; -UPDATE "policy_variables" SET "value" = 'Entra ID / Active Directory' - WHERE "key" = 'TOOL_IAM' AND "value" = 'das zentrale Verzeichnis (Entra ID / Active Directory)'; diff --git a/prisma/migrations/20260723090004_managed_registers/migration.sql b/prisma/migrations/20260723090004_managed_registers/migration.sql deleted file mode 100644 index 83e6078..0000000 --- a/prisma/migrations/20260723090004_managed_registers/migration.sql +++ /dev/null @@ -1,55 +0,0 @@ --- CreateTable -CREATE TABLE "managed_registers" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT, - "columns" JSONB NOT NULL DEFAULT '[]', - "review_cycle" TEXT, - "responsible" TEXT, - "supplier_link" BOOLEAN NOT NULL DEFAULT false, - "asset_link" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "managed_registers_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "register_rows" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "register_id" TEXT NOT NULL, - "values" JSONB NOT NULL DEFAULT '{}', - "supplier_ref" TEXT, - "asset_ref" TEXT, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "register_rows_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "managed_registers_tenant_id_idx" ON "managed_registers"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "managed_registers_tenant_id_code_key" ON "managed_registers"("tenant_id", "code"); - --- CreateIndex -CREATE INDEX "register_rows_tenant_id_register_id_idx" ON "register_rows"("tenant_id", "register_id"); - --- AddForeignKey -ALTER TABLE "register_rows" ADD CONSTRAINT "register_rows_register_id_fkey" FOREIGN KEY ("register_id") REFERENCES "managed_registers"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS für die neuen mandantenbezogenen Tabellen. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['managed_registers','register_rows'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260724095408_software_and_project_assets/migration.sql b/prisma/migrations/20260724095408_software_and_project_assets/migration.sql deleted file mode 100644 index 93b2885..0000000 --- a/prisma/migrations/20260724095408_software_and_project_assets/migration.sql +++ /dev/null @@ -1,91 +0,0 @@ --- CreateEnum -CREATE TYPE "SoftwareApprovalStatus" AS ENUM ('BEANTRAGT', 'FREIGEGEBEN', 'GESPERRT'); - --- CreateEnum -CREATE TYPE "ProjectStatus" AS ENUM ('GEPLANT', 'LAUFEND', 'ABGESCHLOSSEN', 'ABGEBROCHEN'); - --- AlterEnum --- This migration adds more than one value to an enum. --- With PostgreSQL versions 11 and earlier, this is not possible --- in a single migration. This can be worked around by creating --- multiple migrations, each migration adding only one value to --- the enum. - - -ALTER TYPE "AssetType" ADD VALUE 'SOFTWARE'; -ALTER TYPE "AssetType" ADD VALUE 'PROJECT'; - --- CreateTable -CREATE TABLE "software_profiles" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "provider_asset_id" TEXT, - "version" TEXT, - "approval_status" "SoftwareApprovalStatus" NOT NULL DEFAULT 'BEANTRAGT', - "approved_by" TEXT, - "criticality" INTEGER NOT NULL DEFAULT 1, - "next_review" TIMESTAMP(3), - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "software_profiles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "project_profiles" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "asset_id" TEXT NOT NULL, - "ref_no" INTEGER NOT NULL, - "classification" TEXT, - "isb_involved" BOOLEAN NOT NULL DEFAULT false, - "status" "ProjectStatus" NOT NULL DEFAULT 'GEPLANT', - "notes" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_by" TEXT, - - CONSTRAINT "project_profiles_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "software_profiles_asset_id_key" ON "software_profiles"("asset_id"); - --- CreateIndex -CREATE INDEX "software_profiles_tenant_id_idx" ON "software_profiles"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "software_profiles_tenant_id_ref_no_key" ON "software_profiles"("tenant_id", "ref_no"); - --- CreateIndex -CREATE UNIQUE INDEX "project_profiles_asset_id_key" ON "project_profiles"("asset_id"); - --- CreateIndex -CREATE INDEX "project_profiles_tenant_id_idx" ON "project_profiles"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "project_profiles_tenant_id_ref_no_key" ON "project_profiles"("tenant_id", "ref_no"); - --- AddForeignKey -ALTER TABLE "software_profiles" ADD CONSTRAINT "software_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "software_profiles" ADD CONSTRAINT "software_profiles_provider_asset_id_fkey" FOREIGN KEY ("provider_asset_id") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "project_profiles" ADD CONSTRAINT "project_profiles_asset_id_fkey" FOREIGN KEY ("asset_id") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- RLS für die neuen mandantenbezogenen Tabellen. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['software_profiles','project_profiles'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260728104412_tasks_wizard_fields/migration.sql b/prisma/migrations/20260728104412_tasks_wizard_fields/migration.sql deleted file mode 100644 index a2e5c9a..0000000 --- a/prisma/migrations/20260728104412_tasks_wizard_fields/migration.sql +++ /dev/null @@ -1,6 +0,0 @@ --- AlterTable -ALTER TABLE "tasks" ADD COLUMN "links" JSONB, -ADD COLUMN "origin" TEXT, -ADD COLUMN "priority" TEXT NOT NULL DEFAULT 'mittel', -ADD COLUMN "resources" JSONB; - diff --git a/prisma/migrations/20260728105241_onboarding_progress/migration.sql b/prisma/migrations/20260728105241_onboarding_progress/migration.sql deleted file mode 100644 index e6f8706..0000000 --- a/prisma/migrations/20260728105241_onboarding_progress/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ --- CreateEnum -CREATE TYPE "OnboardingStepStatus" AS ENUM ('offen', 'in_bearbeitung', 'zur_validierung', 'validiert'); - --- CreateTable -CREATE TABLE "onboarding_progress" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "step_key" TEXT NOT NULL, - "status" "OnboardingStepStatus" NOT NULL DEFAULT 'offen', - "review_comment" TEXT, - "reviewer_id" TEXT, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "onboarding_progress_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "onboarding_progress_tenant_id_idx" ON "onboarding_progress"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "onboarding_progress_tenant_id_step_key_key" ON "onboarding_progress"("tenant_id", "step_key"); - --- RLS für die neue mandantenbezogene Tabelle. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['onboarding_progress'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260728115109_object_review_status/migration.sql b/prisma/migrations/20260728115109_object_review_status/migration.sql deleted file mode 100644 index a02d7e6..0000000 --- a/prisma/migrations/20260728115109_object_review_status/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Generalisiertes Review-Status-Enum (Story F2, Contracts §2). Ersetzt das --- schrittbezogene OnboardingStepStatus und ergänzt den Zustand `zurueckgewiesen`. --- CreateEnum -CREATE TYPE "ObjectReviewStatus" AS ENUM ('offen', 'in_bearbeitung', 'zur_validierung', 'validiert', 'zurueckgewiesen'); - --- AlterTable: onboarding_progress.status auf das neue Enum umziehen (werterhaltend) -ALTER TABLE "onboarding_progress" ALTER COLUMN "status" DROP DEFAULT; -ALTER TABLE "onboarding_progress" ALTER COLUMN "status" TYPE "ObjectReviewStatus" USING ("status"::text::"ObjectReviewStatus"); -ALTER TABLE "onboarding_progress" ALTER COLUMN "status" SET DEFAULT 'offen'; - --- DropEnum: altes, schrittbezogenes Enum entfernen -DROP TYPE "OnboardingStepStatus"; diff --git a/prisma/migrations/20260728122213_wizard_facts/migration.sql b/prisma/migrations/20260728122213_wizard_facts/migration.sql deleted file mode 100644 index 76d2205..0000000 --- a/prisma/migrations/20260728122213_wizard_facts/migration.sql +++ /dev/null @@ -1,29 +0,0 @@ --- CreateTable -CREATE TABLE "wizard_facts" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "key" TEXT NOT NULL, - "section" TEXT NOT NULL, - "value" JSONB NOT NULL, - "updated_at" TIMESTAMP(3) NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "wizard_facts_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "wizard_facts_tenant_id_idx" ON "wizard_facts"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "wizard_facts_tenant_id_key_key" ON "wizard_facts"("tenant_id", "key"); - - --- RLS für die neue mandantenbezogene Tabelle. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['wizard_facts'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260728153225_wizard_scope/migration.sql b/prisma/migrations/20260728153225_wizard_scope/migration.sql deleted file mode 100644 index be30d29..0000000 --- a/prisma/migrations/20260728153225_wizard_scope/migration.sql +++ /dev/null @@ -1,30 +0,0 @@ --- CreateTable -CREATE TABLE "wizard_scopes" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "pruefziele" TEXT[] DEFAULT ARRAY['informationssicherheit']::TEXT[], - "geltungsbereich" TEXT, - "standorte" TEXT[] DEFAULT ARRAY[]::TEXT[], - "ausschluesse" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "wizard_scopes_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "wizard_scopes_tenant_id_key" ON "wizard_scopes"("tenant_id"); - --- CreateIndex -CREATE INDEX "wizard_scopes_tenant_id_idx" ON "wizard_scopes"("tenant_id"); - - --- RLS für die neue mandantenbezogene Tabelle. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['wizard_scopes'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260728154046_implementation_hints/migration.sql b/prisma/migrations/20260728154046_implementation_hints/migration.sql deleted file mode 100644 index 531fd4a..0000000 --- a/prisma/migrations/20260728154046_implementation_hints/migration.sql +++ /dev/null @@ -1,25 +0,0 @@ --- CreateTable -CREATE TABLE "implementation_hints" ( - "id" TEXT NOT NULL, - "req_id" TEXT NOT NULL, - "control" TEXT NOT NULL, - "stufe" TEXT NOT NULL, - "requirement" TEXT NOT NULL, - "organisational" TEXT NOT NULL, - "technical" TEXT NOT NULL, - "evidence" TEXT NOT NULL, - "template" TEXT, - "resources" TEXT NOT NULL, - "al_filter" TEXT NOT NULL, - "procurement" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "implementation_hints_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "implementation_hints_req_id_key" ON "implementation_hints"("req_id"); - --- CreateIndex -CREATE INDEX "implementation_hints_control_idx" ON "implementation_hints"("control"); - diff --git a/prisma/migrations/20260728161306_policy_package_state/migration.sql b/prisma/migrations/20260728161306_policy_package_state/migration.sql deleted file mode 100644 index 8d5d65a..0000000 --- a/prisma/migrations/20260728161306_policy_package_state/migration.sql +++ /dev/null @@ -1,27 +0,0 @@ --- CreateTable -CREATE TABLE "policy_package_states" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "imported_version" TEXT, - "imported_at" TIMESTAMP(3), - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "policy_package_states_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "policy_package_states_tenant_id_key" ON "policy_package_states"("tenant_id"); - --- CreateIndex -CREATE INDEX "policy_package_states_tenant_id_idx" ON "policy_package_states"("tenant_id"); - - --- RLS für die neue mandantenbezogene Tabelle. -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['policy_package_states'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260729101027_risk_catalog/migration.sql b/prisma/migrations/20260729101027_risk_catalog/migration.sql deleted file mode 100644 index 84f22d4..0000000 --- a/prisma/migrations/20260729101027_risk_catalog/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ --- AlterTable -ALTER TABLE "risks" ADD COLUMN "catalog_code" TEXT; - --- CreateTable -CREATE TABLE "risk_catalog_entries" ( - "id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "category" TEXT NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT NOT NULL, - "controls" TEXT[] DEFAULT ARRAY[]::TEXT[], - "asset_types" TEXT[] DEFAULT ARRAY[]::TEXT[], - "standard_measure" TEXT NOT NULL, - "default_likelihood" INTEGER NOT NULL, - "default_impact" INTEGER NOT NULL, - "rationale" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "risk_catalog_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "risk_catalog_entries_code_key" ON "risk_catalog_entries"("code"); - --- CreateIndex -CREATE INDEX "risk_catalog_entries_category_idx" ON "risk_catalog_entries"("category"); - diff --git a/prisma/migrations/20260729103725_risk_acceptance/migration.sql b/prisma/migrations/20260729103725_risk_acceptance/migration.sql deleted file mode 100644 index f3e3f91..0000000 --- a/prisma/migrations/20260729103725_risk_acceptance/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "risks" ADD COLUMN "acceptance_rationale" TEXT, -ADD COLUMN "accepted_at" TIMESTAMP(3), -ADD COLUMN "accepted_by_id" TEXT; - diff --git a/prisma/migrations/20260729120000_control_assessments/migration.sql b/prisma/migrations/20260729120000_control_assessments/migration.sql deleted file mode 100644 index da59ac1..0000000 --- a/prisma/migrations/20260729120000_control_assessments/migration.sql +++ /dev/null @@ -1,35 +0,0 @@ --- CreateTable -CREATE TABLE "control_assessments" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "control" TEXT NOT NULL, - "suggested_value" INTEGER, - "confirmed_value" INTEGER NOT NULL, - "target" INTEGER NOT NULL, - "justification" TEXT, - "confirmed_by_id" TEXT, - "confirmed_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "control_assessments_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "control_assessments_tenant_id_idx" ON "control_assessments"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "control_assessments_tenant_id_control_key" ON "control_assessments"("tenant_id", "control"); - - --- Row Level Security (zweite Verteidigungslinie, analog übrige Mandanten-Tabellen) -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['control_assessments'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format( - 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', - t - ); - END LOOP; -END $$; diff --git a/prisma/migrations/20260729140000_task_description/migration.sql b/prisma/migrations/20260729140000_task_description/migration.sql deleted file mode 100644 index 7a87330..0000000 --- a/prisma/migrations/20260729140000_task_description/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "tasks" ADD COLUMN "description" TEXT; - diff --git a/prisma/migrations/20260729150000_control_implementations/migration.sql b/prisma/migrations/20260729150000_control_implementations/migration.sql deleted file mode 100644 index b132ce2..0000000 --- a/prisma/migrations/20260729150000_control_implementations/migration.sql +++ /dev/null @@ -1,36 +0,0 @@ --- CreateTable -CREATE TABLE "control_implementations" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "req_id" TEXT NOT NULL, - "control" TEXT NOT NULL, - "status" TEXT NOT NULL DEFAULT 'offen', - "note" TEXT, - "updated_by_id" TEXT, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "control_implementations_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "control_implementations_tenant_id_idx" ON "control_implementations"("tenant_id"); - --- CreateIndex -CREATE INDEX "control_implementations_tenant_id_control_idx" ON "control_implementations"("tenant_id", "control"); - --- CreateIndex -CREATE UNIQUE INDEX "control_implementations_tenant_id_req_id_key" ON "control_implementations"("tenant_id", "req_id"); - - --- Row Level Security (zweite Verteidigungslinie, analog übrige Mandanten-Tabellen) -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['control_implementations'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format( - 'CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', - t - ); - END LOOP; -END $$; diff --git a/prisma/migrations/20260730111105_login_lockout/migration.sql b/prisma/migrations/20260730111105_login_lockout/migration.sql deleted file mode 100644 index bf6f883..0000000 --- a/prisma/migrations/20260730111105_login_lockout/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ --- F-05: Brute-Force-Schutz Mandanten-Login — Fehlversuchszähler + Kontosperre. --- Nur eigene DDL (die geteilte lokale DB kann fremde Branch-Änderungen enthalten; --- diese wurden bewusst NICHT übernommen). Die Tabelle "users" hat bereits eine --- aktive RLS-Policy (tenant_isolation, Migration 20260702095816); das Hinzufügen --- von Spalten erfordert keinen neuen RLS-DO-Block. - --- AlterTable -ALTER TABLE "users" ADD COLUMN "failed_logins" INTEGER NOT NULL DEFAULT 0, -ADD COLUMN "locked_until" TIMESTAMP(3); diff --git a/prisma/migrations/20260730160000_rls_enforce/migration.sql b/prisma/migrations/20260730160000_rls_enforce/migration.sql deleted file mode 100644 index 6a4d110..0000000 --- a/prisma/migrations/20260730160000_rls_enforce/migration.sql +++ /dev/null @@ -1,391 +0,0 @@ --- ============================================================================ --- F-04: Row Level Security scharfschalten (FORCE + WITH CHECK + Kontext) --- ============================================================================ --- Diese Migration enthält NUR SQL (keine Schemaänderung) und ist idempotent. --- --- Ziel: Die in den bisherigen Migrationen definierten `tenant_isolation`-Policies --- werden für die eingeschränkte App-Rolle `isms_app` (NOBYPASSRLS) wirksam. --- --- Warum FORCE für den Owner UNSCHÄDLICH ist: --- PostgreSQL wendet RLS auf einen Superuser bzw. eine Rolle mit BYPASSRLS NIE --- an — auch nicht unter FORCE ROW LEVEL SECURITY. Die Owner-/Migrations-Rolle --- `isms` ist Superuser + BYPASSRLS, sieht also weiterhin ALLE Zeilen. Deshalb --- bleiben Migrationen, Seed und der mandantenübergreifende Login-Lookup (die --- über `DATABASE_URL` = Owner laufen) voll funktionsfähig. FORCE greift nur für --- `isms_app`. WICHTIG für Prod: Die Owner-/Migrate-Rolle MUSS BYPASSRLS bzw. --- Superuser sein, sonst sähe der Login keine Nutzer (null Zeilen). --- --- Warum WITH CHECK zwingend nötig ist (INSERT-Falle): --- Unter FORCE ROW LEVEL SECURITY lehnt Postgres jeden INSERT/UPDATE ab, dessen --- Zielzeile keine WITH-CHECK-Policy erfüllt. Die bisherigen Policies hatten nur --- USING (Lesefilter), KEIN WITH CHECK → damit würde `isms_app` KEINE Zeile mehr --- anlegen können. Jede Policy erhält deshalb hier zusätzlich --- `WITH CHECK (tenant_id = current_setting('app.tenant_id', true))`, sodass nur --- Zeilen des aktiven Mandanten geschrieben werden dürfen (verhindert auch das --- „Umschreiben" einer Zeile auf einen fremden Mandanten). --- --- Wie der Kontext gesetzt wird: --- `current_setting('app.tenant_id', true)` liest den GUC-Parameter (2. Arg true --- = kein Fehler, wenn ungesetzt → NULL). Die App setzt ihn TRANSAKTIONSLOKAL via --- `SELECT set_config('app.tenant_id', , true)` in derselben Transaktion --- wie die eigentliche Operation (siehe src/server/db.ts, dbForTenant, RLS-Pfad). --- Ist der Kontext NICHT gesetzt, ergibt `tenant_id = NULL` → NULL (nicht TRUE): --- `isms_app` sieht dann 0 Zeilen (fail-closed). Genau deshalb MUSS das --- Scharfschalten zusammen mit der Kontextsetzung (RLS_ENFORCED=true) erfolgen. --- --- App-Rolle / Secret (NICHT in dieser Migration): --- `isms_app` existiert bereits (NOLOGIN, NOBYPASSRLS, mit GRANTs). Für den --- scharfen Betrieb braucht sie LOGIN + starkes Passwort — das ist ein SECRET und --- gehört NICHT in versioniertes SQL. Setze es operativ (einmalig), z. B.: --- ALTER ROLE isms_app WITH LOGIN PASSWORD ''; --- und verbinde die App über RLS_DATABASE_URL als isms_app (RLS_ENFORCED=true). --- Siehe docs/DEPLOY-PROD-CONTABO.md und .env.prod.example. --- --- Idempotenz: ENABLE/FORCE sind idempotent; die Policy wird vor CREATE via --- DROP POLICY IF EXISTS entfernt (deckt Gruppe A = 12 bestehende USING-only- --- Policies und Gruppe B = 37 ohne Policy einheitlich ab). --- ============================================================================ - -ALTER TABLE "asset_relations" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "asset_relations"; -CREATE POLICY tenant_isolation ON "asset_relations" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "asset_relations" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "assets" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "assets"; -CREATE POLICY tenant_isolation ON "assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "assets" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "audit_logs" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "audit_logs"; -CREATE POLICY tenant_isolation ON "audit_logs" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "audit_logs" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "bia_entries" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "bia_entries"; -CREATE POLICY tenant_isolation ON "bia_entries" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "bia_entries" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "measures" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "measures"; -CREATE POLICY tenant_isolation ON "measures" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "measures" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "process_assets" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "process_assets"; -CREATE POLICY tenant_isolation ON "process_assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "process_assets" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "processes" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "processes"; -CREATE POLICY tenant_isolation ON "processes" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "processes" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risk_assets" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risk_assets"; -CREATE POLICY tenant_isolation ON "risk_assets" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risk_assets" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risk_measures" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risk_measures"; -CREATE POLICY tenant_isolation ON "risk_measures" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risk_measures" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risks" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risks"; -CREATE POLICY tenant_isolation ON "risks" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risks" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "roles" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "roles"; -CREATE POLICY tenant_isolation ON "roles" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "roles" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "users" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "users"; -CREATE POLICY tenant_isolation ON "users" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "users" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "tasks" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "tasks"; -CREATE POLICY tenant_isolation ON "tasks" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "tasks" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "task_comments" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "task_comments"; -CREATE POLICY tenant_isolation ON "task_comments" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "task_comments" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "managed_registers" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "managed_registers"; -CREATE POLICY tenant_isolation ON "managed_registers" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "managed_registers" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "register_rows" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "register_rows"; -CREATE POLICY tenant_isolation ON "register_rows" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "register_rows" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "onboarding_progress" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "onboarding_progress"; -CREATE POLICY tenant_isolation ON "onboarding_progress" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "onboarding_progress" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "wizard_facts" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "wizard_facts"; -CREATE POLICY tenant_isolation ON "wizard_facts" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "wizard_facts" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "wizard_scopes" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "wizard_scopes"; -CREATE POLICY tenant_isolation ON "wizard_scopes" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "wizard_scopes" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_package_states" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_package_states"; -CREATE POLICY tenant_isolation ON "policy_package_states" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_package_states" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "tenant_settings" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "tenant_settings"; -CREATE POLICY tenant_isolation ON "tenant_settings" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "tenant_settings" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "tenant_modules" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "tenant_modules"; -CREATE POLICY tenant_isolation ON "tenant_modules" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "tenant_modules" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "supplier_profiles" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "supplier_profiles"; -CREATE POLICY tenant_isolation ON "supplier_profiles" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "supplier_profiles" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "it_service_profiles" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "it_service_profiles"; -CREATE POLICY tenant_isolation ON "it_service_profiles" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "it_service_profiles" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "software_profiles" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "software_profiles"; -CREATE POLICY tenant_isolation ON "software_profiles" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "software_profiles" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "project_profiles" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "project_profiles"; -CREATE POLICY tenant_isolation ON "project_profiles" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "project_profiles" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "supplier_assessments" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "supplier_assessments"; -CREATE POLICY tenant_isolation ON "supplier_assessments" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "supplier_assessments" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "contracts" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "contracts"; -CREATE POLICY tenant_isolation ON "contracts" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "contracts" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "ndas" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "ndas"; -CREATE POLICY tenant_isolation ON "ndas" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "ndas" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "supplier_evidence" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "supplier_evidence"; -CREATE POLICY tenant_isolation ON "supplier_evidence" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "supplier_evidence" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "service_control_responsibilities" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "service_control_responsibilities"; -CREATE POLICY tenant_isolation ON "service_control_responsibilities" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "service_control_responsibilities" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "subcontractors" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "subcontractors"; -CREATE POLICY tenant_isolation ON "subcontractors" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "subcontractors" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "management_decisions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "management_decisions"; -CREATE POLICY tenant_isolation ON "management_decisions" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "management_decisions" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "maturity_assessments" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "maturity_assessments"; -CREATE POLICY tenant_isolation ON "maturity_assessments" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "maturity_assessments" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "control_assessments" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "control_assessments"; -CREATE POLICY tenant_isolation ON "control_assessments" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "control_assessments" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "control_implementations" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "control_implementations"; -CREATE POLICY tenant_isolation ON "control_implementations" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "control_implementations" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_documents" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_documents"; -CREATE POLICY tenant_isolation ON "policy_documents" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_documents" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_requirements" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_requirements"; -CREATE POLICY tenant_isolation ON "policy_requirements" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_requirements" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_variables" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_variables"; -CREATE POLICY tenant_isolation ON "policy_variables" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_variables" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_baseline_params" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_baseline_params"; -CREATE POLICY tenant_isolation ON "policy_baseline_params" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_baseline_params" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "policy_evidence" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_evidence"; -CREATE POLICY tenant_isolation ON "policy_evidence" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_evidence" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "crypto_entries" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "crypto_entries"; -CREATE POLICY tenant_isolation ON "crypto_entries" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "crypto_entries" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "classification_classes" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "classification_classes"; -CREATE POLICY tenant_isolation ON "classification_classes" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "classification_classes" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "handling_aspects" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "handling_aspects"; -CREATE POLICY tenant_isolation ON "handling_aspects" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "handling_aspects" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "handling_rules" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "handling_rules"; -CREATE POLICY tenant_isolation ON "handling_rules" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "handling_rules" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risk_matrix_classes" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risk_matrix_classes"; -CREATE POLICY tenant_isolation ON "risk_matrix_classes" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risk_matrix_classes" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risk_ew_levels" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risk_ew_levels"; -CREATE POLICY tenant_isolation ON "risk_ew_levels" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risk_ew_levels" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "risk_damage_dimensions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "risk_damage_dimensions"; -CREATE POLICY tenant_isolation ON "risk_damage_dimensions" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "risk_damage_dimensions" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "handbook_topics" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "handbook_topics"; -CREATE POLICY tenant_isolation ON "handbook_topics" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "handbook_topics" FORCE ROW LEVEL SECURITY; - diff --git a/prisma/migrations/20260730170000_mfa_totp_replay/migration.sql b/prisma/migrations/20260730170000_mfa_totp_replay/migration.sql deleted file mode 100644 index 1419426..0000000 --- a/prisma/migrations/20260730170000_mfa_totp_replay/migration.sql +++ /dev/null @@ -1,14 +0,0 @@ --- F-17: TOTP-Replay-Schutz — zuletzt akzeptierter RFC-6238-Zeitschritt je Identität. --- Ein bereits verwendeter (oder älterer) Zeitschritt wird beim Login/2FA abgelehnt. --- --- Nur EIGENE DDL (die geteilte lokale DB kann Änderungen paralleler Branches --- enthalten; fremde Blöcke wurden bewusst NICHT übernommen). "users" hat bereits --- eine aktive RLS-Policy (tenant_isolation, Migration 20260702095816); das reine --- Hinzufügen einer Spalte erfordert keinen neuen RLS-DO-Block. "platform_admins" --- ist plattformweit (kein tenant_id/RLS). - --- AlterTable -ALTER TABLE "platform_admins" ADD COLUMN "last_totp_step" BIGINT; - --- AlterTable -ALTER TABLE "users" ADD COLUMN "last_totp_step" BIGINT; diff --git a/prisma/migrations/20260730180000_mail_fundament/migration.sql b/prisma/migrations/20260730180000_mail_fundament/migration.sql deleted file mode 100644 index d9e4375..0000000 --- a/prisma/migrations/20260730180000_mail_fundament/migration.sql +++ /dev/null @@ -1,84 +0,0 @@ --- CreateTable -CREATE TABLE "mail_logs" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT, - "scope" TEXT NOT NULL DEFAULT 'tenant', - "to" TEXT NOT NULL, - "template" TEXT NOT NULL, - "locale" TEXT NOT NULL DEFAULT 'de', - "status" TEXT NOT NULL DEFAULT 'pending', - "provider_message_id" TEXT, - "error" TEXT, - "attempts" INTEGER NOT NULL DEFAULT 0, - "dedupe_key" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "sent_at" TIMESTAMP(3), - - CONSTRAINT "mail_logs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "notification_preferences" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "user_id" TEXT NOT NULL, - "event_type" TEXT NOT NULL, - "email" BOOLEAN NOT NULL DEFAULT true, - "locale" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "notification_preferences_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "mail_logs_dedupe_key_key" ON "mail_logs"("dedupe_key"); - --- CreateIndex -CREATE INDEX "mail_logs_tenant_id_created_at_idx" ON "mail_logs"("tenant_id", "created_at"); - --- CreateIndex -CREATE INDEX "mail_logs_status_idx" ON "mail_logs"("status"); - --- CreateIndex -CREATE INDEX "notification_preferences_tenant_id_idx" ON "notification_preferences"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "notification_preferences_user_id_event_type_key" ON "notification_preferences"("user_id", "event_type"); - - --- ============================================================================ --- Row Level Security (SEC1) --- --- Muster wie seit F-04 (20260730160000_rls_enforce): ENABLE + FORCE + Policy mit --- USING *und* WITH CHECK. Ohne WITH CHECK koennte die eingeschraenkte App-Rolle --- `isms_app` unter FORCE keine Zeilen mehr anlegen. --- --- mail_logs.tenant_id ist NULLABLE (Plattform-Mails ohne Mandantenbezug). Die --- Policy ist trotzdem identisch zu audit_logs, das dieselbe Situation hat: --- Zeilen mit tenant_id = NULL erfuellen die Bedingung nie und sind fuer `isms_app` --- weder les- noch schreibbar. Das ist beabsichtigt — Plattform-Zeilen werden --- ausschliesslich ueber den rohen `prisma`-Client (Owner-Rolle, BYPASSRLS) --- geschrieben, genau wie writePlatformAudit(). --- --- GRANT ist noetig, weil die pauschalen Rechte aus 20260702095816 nur fuer die --- damals existierenden Tabellen galten; ALTER DEFAULT PRIVILEGES greift nur fuer --- Objekte, die von der dort gesetzten Rolle erzeugt werden. --- ============================================================================ - -GRANT SELECT, INSERT, UPDATE, DELETE ON "mail_logs" TO isms_app; -GRANT SELECT, INSERT, UPDATE, DELETE ON "notification_preferences" TO isms_app; - -ALTER TABLE "mail_logs" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "mail_logs"; -CREATE POLICY tenant_isolation ON "mail_logs" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "mail_logs" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "notification_preferences" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "notification_preferences"; -CREATE POLICY tenant_isolation ON "notification_preferences" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "notification_preferences" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260730190000_auth_tokens_sessions/migration.sql b/prisma/migrations/20260730190000_auth_tokens_sessions/migration.sql deleted file mode 100644 index 2b0a13b..0000000 --- a/prisma/migrations/20260730190000_auth_tokens_sessions/migration.sql +++ /dev/null @@ -1,57 +0,0 @@ --- AlterTable -ALTER TABLE "platform_admins" ADD COLUMN "sessions_valid_after" TIMESTAMP(3); - --- AlterTable -ALTER TABLE "users" ADD COLUMN "sessions_valid_after" TIMESTAMP(3); - --- CreateTable -CREATE TABLE "auth_tokens" ( - "id" TEXT NOT NULL, - "principal_type" TEXT NOT NULL, - "principal_id" TEXT NOT NULL, - "tenant_id" TEXT, - "type" TEXT NOT NULL, - "token_hash" TEXT NOT NULL, - "new_email" TEXT, - "expires_at" TIMESTAMP(3) NOT NULL, - "used_at" TIMESTAMP(3), - "request_ip" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "auth_tokens_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "auth_tokens_token_hash_key" ON "auth_tokens"("token_hash"); - --- CreateIndex -CREATE INDEX "auth_tokens_principal_type_principal_id_type_idx" ON "auth_tokens"("principal_type", "principal_id", "type"); - --- CreateIndex -CREATE INDEX "auth_tokens_expires_at_idx" ON "auth_tokens"("expires_at"); - - --- ============================================================================ --- Row Level Security (SEC2) --- --- Muster wie seit F-04: ENABLE + FORCE + Policy mit USING *und* WITH CHECK. --- --- auth_tokens.tenant_id ist NULLABLE (Plattform-Admin-Tokens ohne --- Mandantenbezug) — dieselbe Situation wie bei audit_logs und mail_logs. Zeilen --- mit tenant_id = NULL sind fuer `isms_app` weder les- noch schreibbar. --- --- WICHTIG: Der Token-Lookup beim Einloesen laeuft ohne Session (der Nutzer ist --- gerade nicht angemeldet) und damit ohne Mandantenkontext. Er erfolgt deshalb --- bewusst ueber den rohen `prisma`-Client (Owner-Rolle, BYPASSRLS) — siehe --- src/server/auth-token.ts. Die Policy bleibt als zweite Verteidigungslinie --- gegen versehentliche Zugriffe ueber den Mandanten-Guard bestehen. --- ============================================================================ - -GRANT SELECT, INSERT, UPDATE, DELETE ON "auth_tokens" TO isms_app; - -ALTER TABLE "auth_tokens" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "auth_tokens"; -CREATE POLICY tenant_isolation ON "auth_tokens" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "auth_tokens" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260731120000_webauthn_credentials/migration.sql b/prisma/migrations/20260731120000_webauthn_credentials/migration.sql deleted file mode 100644 index c0ca3f5..0000000 --- a/prisma/migrations/20260731120000_webauthn_credentials/migration.sql +++ /dev/null @@ -1,38 +0,0 @@ --- CreateTable -CREATE TABLE "webauthn_credentials" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "user_id" TEXT NOT NULL, - "credential_id" TEXT NOT NULL, - "public_key" TEXT NOT NULL, - "counter" BIGINT NOT NULL DEFAULT 0, - "transports" TEXT[] DEFAULT ARRAY[]::TEXT[], - "device_name" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "last_used_at" TIMESTAMP(3), - - CONSTRAINT "webauthn_credentials_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "webauthn_credentials_credential_id_key" ON "webauthn_credentials"("credential_id"); - --- CreateIndex -CREATE INDEX "webauthn_credentials_tenant_id_idx" ON "webauthn_credentials"("tenant_id"); - --- CreateIndex -CREATE INDEX "webauthn_credentials_user_id_idx" ON "webauthn_credentials"("user_id"); - --- AddForeignKey -ALTER TABLE "webauthn_credentials" ADD CONSTRAINT "webauthn_credentials_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- Row Level Security (analog übrige Mandanten-Tabellen) -DO $$ -DECLARE t text; -BEGIN - FOREACH t IN ARRAY ARRAY['webauthn_credentials'] LOOP - EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t); - EXECUTE format('CREATE POLICY tenant_isolation ON %I USING (tenant_id = current_setting(''app.tenant_id'', true))', t); - END LOOP; -END $$; diff --git a/prisma/migrations/20260731123409_tisax_m0_foundation/migration.sql b/prisma/migrations/20260731123409_tisax_m0_foundation/migration.sql deleted file mode 100644 index c2bc1e3..0000000 --- a/prisma/migrations/20260731123409_tisax_m0_foundation/migration.sql +++ /dev/null @@ -1,15 +0,0 @@ --- CreateEnum -CREATE TYPE "Domain" AS ENUM ('GOVERNANCE', 'HR', 'PHYSICAL', 'BCM', 'IT', 'PROCUREMENT', 'COMPLIANCE', 'DATA_PROTECTION', 'PROTOTYPE'); - --- CreateEnum -CREATE TYPE "RaciKind" AS ENUM ('RESPONSIBLE', 'ACCOUNTABLE', 'CONSULTED', 'INFORMED'); - --- CreateEnum -CREATE TYPE "InfoLabel" AS ENUM ('NONE', 'INFO_HIGH', 'INFO_VERY_HIGH', 'PROTOTYPE', 'PERSONAL_DATA'); - --- AlterTable -ALTER TABLE "tasks" ADD COLUMN "domain" "Domain", -ADD COLUMN "order_idx" INTEGER NOT NULL DEFAULT 0; - --- CreateIndex -CREATE INDEX "tasks_tenant_id_domain_status_idx" ON "tasks"("tenant_id", "domain", "status"); diff --git a/prisma/migrations/20260731130000_platform_admin_role/migration.sql b/prisma/migrations/20260731130000_platform_admin_role/migration.sql deleted file mode 100644 index 4570068..0000000 --- a/prisma/migrations/20260731130000_platform_admin_role/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "platform_admins" ADD COLUMN "role" TEXT NOT NULL DEFAULT 'full'; - diff --git a/prisma/migrations/20260731152213_tisax_m1_m4_consolidated/migration.sql b/prisma/migrations/20260731152213_tisax_m1_m4_consolidated/migration.sql deleted file mode 100644 index 36a1e34..0000000 --- a/prisma/migrations/20260731152213_tisax_m1_m4_consolidated/migration.sql +++ /dev/null @@ -1,136 +0,0 @@ --- AlterTable -ALTER TABLE "assets" ADD COLUMN "label" "InfoLabel" NOT NULL DEFAULT 'NONE', -ADD COLUMN "normalized_name" TEXT; - --- AlterTable -ALTER TABLE "tasks" ADD COLUMN "effective_until" TIMESTAMP(3), -ADD COLUMN "recurrence" TEXT, -ADD COLUMN "remind_at" TIMESTAMP(3); - --- CreateTable -CREATE TABLE "process_catalog" ( - "id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "name" TEXT NOT NULL, - "category" "ProcessCategory" NOT NULL, - "suggested_asset_types" "AssetType"[], - "suggested_risk_codes" TEXT[] DEFAULT ARRAY[]::TEXT[], - "suggested_info_labels" "InfoLabel"[], - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "process_catalog_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "task_participants" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "task_id" TEXT NOT NULL, - "user_id" TEXT NOT NULL, - "raci" "RaciKind" NOT NULL, - - CONSTRAINT "task_participants_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "control_domain_map" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT, - "control" TEXT NOT NULL, - "domain" "Domain" NOT NULL, - "raci" "RaciKind" NOT NULL DEFAULT 'RESPONSIBLE', - "function_key" TEXT, - - CONSTRAINT "control_domain_map_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "evidence" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "title" TEXT NOT NULL, - "kind" TEXT NOT NULL, - "file_ref" TEXT, - "task_id" TEXT, - "control" TEXT, - "valid_from" TIMESTAMP(3), - "valid_until" TIMESTAMP(3), - "created_by_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "evidence_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "project_function_assignments" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "function_key" TEXT NOT NULL, - "user_id" TEXT, - "domain" "Domain", - "invited_email" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "project_function_assignments_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "process_catalog_code_key" ON "process_catalog"("code"); - --- CreateIndex -CREATE INDEX "task_participants_tenant_id_user_id_idx" ON "task_participants"("tenant_id", "user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "task_participants_tenant_id_task_id_user_id_raci_key" ON "task_participants"("tenant_id", "task_id", "user_id", "raci"); - --- CreateIndex -CREATE INDEX "control_domain_map_tenant_id_control_idx" ON "control_domain_map"("tenant_id", "control"); - --- CreateIndex -CREATE INDEX "control_domain_map_control_idx" ON "control_domain_map"("control"); - --- CreateIndex -CREATE INDEX "evidence_tenant_id_control_idx" ON "evidence"("tenant_id", "control"); - --- CreateIndex -CREATE INDEX "evidence_tenant_id_task_id_idx" ON "evidence"("tenant_id", "task_id"); - --- CreateIndex -CREATE INDEX "project_function_assignments_tenant_id_function_key_idx" ON "project_function_assignments"("tenant_id", "function_key"); - --- CreateIndex -CREATE UNIQUE INDEX "assets_tenant_id_normalized_name_key" ON "assets"("tenant_id", "normalized_name"); - --- AddForeignKey -ALTER TABLE "task_participants" ADD CONSTRAINT "task_participants_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "evidence" ADD CONSTRAINT "evidence_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE SET NULL ON UPDATE CASCADE; - - --- ── Tenant-Isolation (RLS) für neue mandantenbezogene Tabellen ─────────────── --- Muster wie seit F-04: ENABLE + FORCE + Policy mit USING *und* WITH CHECK. --- Globale Tabellen (process_catalog) und die tenant-optionale control_domain_map --- (globale Defaults mit tenant_id = NULL) sind bewusst ausgenommen. -ALTER TABLE "project_function_assignments" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "project_function_assignments"; -CREATE POLICY tenant_isolation ON "project_function_assignments" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "project_function_assignments" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "task_participants" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "task_participants"; -CREATE POLICY tenant_isolation ON "task_participants" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "task_participants" FORCE ROW LEVEL SECURITY; - -ALTER TABLE "evidence" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "evidence"; -CREATE POLICY tenant_isolation ON "evidence" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "evidence" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260731154215_tisax_rekey_onboarding_steps/migration.sql b/prisma/migrations/20260731154215_tisax_rekey_onboarding_steps/migration.sql deleted file mode 100644 index d668851..0000000 --- a/prisma/migrations/20260731154215_tisax_rekey_onboarding_steps/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- TISAX M1: Onboarding-Step-Keys umbenannt (scoping→scope, policies→policy). --- Bestehende onboarding_progress-Zeilen umschlüsseln, damit der Fortschritt erhalten --- bleibt (sonst verwaisen die Alt-Keys und die Schritte starten wieder als „offen"). --- NOT EXISTS-Guard gegen die @@unique(tenant_id, step_key): existiert der Ziel-Key --- bereits, wird nicht umbenannt (die neue Zeile gewinnt). -UPDATE "onboarding_progress" o SET "step_key" = 'scope' -WHERE o."step_key" = 'scoping' - AND NOT EXISTS ( - SELECT 1 FROM "onboarding_progress" i - WHERE i."tenant_id" = o."tenant_id" AND i."step_key" = 'scope' - ); - -UPDATE "onboarding_progress" o SET "step_key" = 'policy' -WHERE o."step_key" = 'policies' - AND NOT EXISTS ( - SELECT 1 FROM "onboarding_progress" i - WHERE i."tenant_id" = o."tenant_id" AND i."step_key" = 'policy' - ); diff --git a/prisma/migrations/20260731165818_tisax_v3_process_fields/migration.sql b/prisma/migrations/20260731165818_tisax_v3_process_fields/migration.sql deleted file mode 100644 index 833dc6b..0000000 --- a/prisma/migrations/20260731165818_tisax_v3_process_fields/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- AlterTable -ALTER TABLE "processes" ADD COLUMN "bia_status" TEXT NOT NULL DEFAULT 'offen', -ADD COLUMN "catalog_code" TEXT, -ADD COLUMN "data_protection_relevant" BOOLEAN NOT NULL DEFAULT false, -ADD COLUMN "deputy_owner_id" TEXT, -ADD COLUMN "in_scope" BOOLEAN NOT NULL DEFAULT true, -ADD COLUMN "interfaces" TEXT, -ADD COLUMN "legal_basis" TEXT, -ADD COLUMN "parent_id" TEXT, -ADD COLUMN "prototype_relevant" BOOLEAN NOT NULL DEFAULT false, -ADD COLUMN "purpose" TEXT; - --- CreateIndex -CREATE INDEX "processes_tenant_id_parent_id_idx" ON "processes"("tenant_id", "parent_id"); - --- AddForeignKey -ALTER TABLE "processes" ADD CONSTRAINT "processes_parent_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "processes"("id") ON DELETE SET NULL ON UPDATE CASCADE; - diff --git a/prisma/migrations/20260801134128_tisax_v4_catalog_parent/migration.sql b/prisma/migrations/20260801134128_tisax_v4_catalog_parent/migration.sql deleted file mode 100644 index 5b32242..0000000 --- a/prisma/migrations/20260801134128_tisax_v4_catalog_parent/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "process_catalog" ADD COLUMN "parent_code" TEXT; - diff --git a/prisma/migrations/20260804111342_tisax_v5_audit/migration.sql b/prisma/migrations/20260804111342_tisax_v5_audit/migration.sql deleted file mode 100644 index 3d0c2bf..0000000 --- a/prisma/migrations/20260804111342_tisax_v5_audit/migration.sql +++ /dev/null @@ -1,94 +0,0 @@ --- CreateEnum -CREATE TYPE "AuditType" AS ENUM ('INTERNAL', 'EXTERNAL'); - --- CreateEnum -CREATE TYPE "AuditStatus" AS ENUM ('PLANNED', 'IN_PREPARATION', 'DONE'); - --- CreateTable -CREATE TABLE "audits" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "type" "AuditType" NOT NULL, - "title" TEXT NOT NULL, - "planned_date" TEXT, - "scope" TEXT, - "assessment_level" TEXT, - "provider" TEXT, - "auditor_user_id" TEXT, - "status" "AuditStatus" NOT NULL DEFAULT 'PLANNED', - "preparation_deadline" TIMESTAMP(3), - "result" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "audits_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "audit_evidence_items" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "audit_id" TEXT NOT NULL, - "control" TEXT NOT NULL, - "title" TEXT NOT NULL, - "req_id" TEXT, - "assigned_user_id" TEXT, - "assigned_function_key" TEXT, - "status" TEXT NOT NULL DEFAULT 'offen', - "evidence_id" TEXT, - "task_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "audit_evidence_items_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "control_descriptions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "control" TEXT NOT NULL, - "req_id" TEXT NOT NULL, - "draft_text" TEXT, - "source_ref" TEXT, - "confidence" TEXT, - "status" TEXT NOT NULL DEFAULT 'open', - "open_answer" TEXT, - "updated_by_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "control_descriptions_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "audits_tenant_id_status_idx" ON "audits"("tenant_id", "status"); - --- CreateIndex -CREATE INDEX "audit_evidence_items_tenant_id_audit_id_idx" ON "audit_evidence_items"("tenant_id", "audit_id"); - --- CreateIndex -CREATE INDEX "audit_evidence_items_tenant_id_assigned_user_id_status_idx" ON "audit_evidence_items"("tenant_id", "assigned_user_id", "status"); - --- CreateIndex -CREATE INDEX "control_descriptions_tenant_id_control_idx" ON "control_descriptions"("tenant_id", "control"); - --- CreateIndex -CREATE UNIQUE INDEX "control_descriptions_tenant_id_req_id_key" ON "control_descriptions"("tenant_id", "req_id"); - --- AddForeignKey -ALTER TABLE "audit_evidence_items" ADD CONSTRAINT "audit_evidence_items_audit_id_fkey" FOREIGN KEY ("audit_id") REFERENCES "audits"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- ── Tenant-Isolation (RLS) für neue Audit-Tabellen (Muster wie F-04) ────────── -ALTER TABLE "audits" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "audits"; -CREATE POLICY tenant_isolation ON "audits" USING ("tenant_id" = current_setting('app.tenant_id', true)) WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "audits" FORCE ROW LEVEL SECURITY; -ALTER TABLE "audit_evidence_items" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "audit_evidence_items"; -CREATE POLICY tenant_isolation ON "audit_evidence_items" USING ("tenant_id" = current_setting('app.tenant_id', true)) WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "audit_evidence_items" FORCE ROW LEVEL SECURITY; -ALTER TABLE "control_descriptions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "control_descriptions"; -CREATE POLICY tenant_isolation ON "control_descriptions" USING ("tenant_id" = current_setting('app.tenant_id', true)) WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "control_descriptions" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260805093503_tisax_v7_policy_domain/migration.sql b/prisma/migrations/20260805093503_tisax_v7_policy_domain/migration.sql deleted file mode 100644 index 4b16200..0000000 --- a/prisma/migrations/20260805093503_tisax_v7_policy_domain/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "policy_documents" ADD COLUMN "domain" "Domain"; - diff --git a/prisma/migrations/20260810120000_policy_templates/migration.sql b/prisma/migrations/20260810120000_policy_templates/migration.sql deleted file mode 100644 index af78521..0000000 --- a/prisma/migrations/20260810120000_policy_templates/migration.sql +++ /dev/null @@ -1,158 +0,0 @@ --- Globale Richtlinien-Vorlagen (Plattform-Ebene, KEIN tenantId/RLS — analog implementation_hints). --- Master-Quelle für den Mandanten-Import; Versionierung (DRAFT/PUBLISHED) + Sprache (locale). - --- CreateEnum -CREATE TYPE "PolicyTemplateStatus" AS ENUM ('DRAFT', 'PUBLISHED', 'ARCHIVED'); - --- CreateTable -CREATE TABLE "policy_template_versions" ( - "id" TEXT NOT NULL, - "version" TEXT NOT NULL, - "status" "PolicyTemplateStatus" NOT NULL DEFAULT 'DRAFT', - "notes" TEXT, - "published_by" TEXT, - "published_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "policy_template_versions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_template_docs" ( - "id" TEXT NOT NULL, - "version_id" TEXT NOT NULL, - "locale" TEXT NOT NULL, - "code" TEXT NOT NULL, - "type" "PolicyDocType" NOT NULL, - "title" TEXT NOT NULL, - "doc_version" TEXT NOT NULL DEFAULT '1.0', - "policy_code" TEXT, - "fulfills" TEXT[] DEFAULT ARRAY[]::TEXT[], - "domain" "Domain", - "raw_markdown" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "policy_template_docs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_template_requirements" ( - "id" TEXT NOT NULL, - "version_id" TEXT NOT NULL, - "locale" TEXT NOT NULL, - "req_id" TEXT NOT NULL, - "policy_code" TEXT NOT NULL, - "control" TEXT NOT NULL, - "obligation" TEXT NOT NULL, - "condition" TEXT, - "requirement" TEXT NOT NULL, - "implementation" TEXT NOT NULL, - "va_codes" TEXT[] DEFAULT ARRAY[]::TEXT[], - "nachweis_link" TEXT, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_template_requirements_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_template_variables" ( - "id" TEXT NOT NULL, - "version_id" TEXT NOT NULL, - "locale" TEXT NOT NULL, - "key" TEXT NOT NULL, - "title" TEXT NOT NULL, - "kind" TEXT NOT NULL, - "group_name" TEXT, - "value" TEXT NOT NULL, - "required" BOOLEAN NOT NULL DEFAULT false, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_template_variables_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_template_baseline_params" ( - "id" TEXT NOT NULL, - "version_id" TEXT NOT NULL, - "locale" TEXT NOT NULL, - "bl_id" TEXT NOT NULL, - "section" TEXT NOT NULL, - "name" TEXT NOT NULL, - "vorgabe" TEXT NOT NULL, - "order_idx" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_template_baseline_params_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_template_evidence" ( - "id" TEXT NOT NULL, - "version_id" TEXT NOT NULL, - "locale" TEXT NOT NULL, - "nr" INTEGER NOT NULL, - "policy_code" TEXT NOT NULL, - "nachweis" TEXT NOT NULL, - "quelle" TEXT NOT NULL, - "verantwortlich" TEXT NOT NULL, - "turnus" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_template_evidence_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_versions_version_key" ON "policy_template_versions"("version"); - --- CreateIndex -CREATE INDEX "policy_template_versions_status_idx" ON "policy_template_versions"("status"); - --- CreateIndex -CREATE INDEX "policy_template_docs_version_id_locale_idx" ON "policy_template_docs"("version_id", "locale"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_docs_version_id_locale_code_key" ON "policy_template_docs"("version_id", "locale", "code"); - --- CreateIndex -CREATE INDEX "policy_template_requirements_version_id_locale_idx" ON "policy_template_requirements"("version_id", "locale"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_requirements_version_id_locale_req_id_key" ON "policy_template_requirements"("version_id", "locale", "req_id"); - --- CreateIndex -CREATE INDEX "policy_template_variables_version_id_locale_idx" ON "policy_template_variables"("version_id", "locale"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_variables_version_id_locale_key_key" ON "policy_template_variables"("version_id", "locale", "key"); - --- CreateIndex -CREATE INDEX "policy_template_baseline_params_version_id_locale_idx" ON "policy_template_baseline_params"("version_id", "locale"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_baseline_params_version_id_locale_bl_id_key" ON "policy_template_baseline_params"("version_id", "locale", "bl_id"); - --- CreateIndex -CREATE INDEX "policy_template_evidence_version_id_locale_idx" ON "policy_template_evidence"("version_id", "locale"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_evidence_version_id_locale_nr_key" ON "policy_template_evidence"("version_id", "locale", "nr"); - --- AddForeignKey -ALTER TABLE "policy_template_docs" ADD CONSTRAINT "policy_template_docs_version_id_fkey" FOREIGN KEY ("version_id") REFERENCES "policy_template_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "policy_template_requirements" ADD CONSTRAINT "policy_template_requirements_version_id_fkey" FOREIGN KEY ("version_id") REFERENCES "policy_template_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "policy_template_variables" ADD CONSTRAINT "policy_template_variables_version_id_fkey" FOREIGN KEY ("version_id") REFERENCES "policy_template_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "policy_template_baseline_params" ADD CONSTRAINT "policy_template_baseline_params_version_id_fkey" FOREIGN KEY ("version_id") REFERENCES "policy_template_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "policy_template_evidence" ADD CONSTRAINT "policy_template_evidence_version_id_fkey" FOREIGN KEY ("version_id") REFERENCES "policy_template_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260811120000_identity_foundation/migration.sql b/prisma/migrations/20260811120000_identity_foundation/migration.sql deleted file mode 100644 index 640e10c..0000000 --- a/prisma/migrations/20260811120000_identity_foundation/migration.sql +++ /dev/null @@ -1,54 +0,0 @@ --- ============================================================================ --- WS0 (Option C) — Fundament: globale `Identity` + `User.identity_id` --- ============================================================================ --- Umbau "Zentrale Identität mit Mandanten-Mitgliedschaften" (docs/FEINDESIGN- --- identity-mandanten.md). Dieser Schritt ist ADDITIV (Expand/Contract): --- * `identities` wird neu angelegt und ist GLOBAL — bewusst OHNE --- `tenant_id`, OHNE RLS/Policy und NICHT in TENANT_MODELS. Der Zugriff läuft --- ausschließlich über den Owner-`prisma`-Client (wie heute der Login-Lookup). --- isms_app (RLS-Rolle) greift nie direkt auf `identities` zu; die FK-Prüfung --- beim INSERT in `users` läuft über den internen RI-Trigger (Owner-Recht) und --- braucht kein SELECT-GRANT für isms_app. --- * `users` behält in diesem Schritt seine Auth-Spalten (Legacy), damit Login/ --- Guards grün und lauffähig bleiben. Die Konsumenten wandern in WS1–WS4 auf --- Identity; die dann ungenutzten Spalten entfernt eine spätere Contract-Migration. --- --- Hinweis Entscheidung D: kein Backfill/Merge. `identity_id` ist NOT NULL — die --- Migration wird deshalb auf einer LEEREN `users`-Tabelle angewandt (Test/Coolify: --- DB leeren -> migrate deploy -> seed; lokal via `prisma migrate reset --force`). --- ============================================================================ - --- CreateTable (GLOBAL — keine RLS!) -CREATE TABLE "identities" ( - "id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "password_hash" TEXT NOT NULL, - "must_change_password" BOOLEAN NOT NULL DEFAULT false, - "mfa_secret" TEXT, - "mfa_enrolled_at" TIMESTAMP(3), - "recovery_codes" JSONB NOT NULL DEFAULT '[]', - "last_totp_step" BIGINT, - "failed_logins" INTEGER NOT NULL DEFAULT 0, - "locked_until" TIMESTAMP(3), - "sessions_valid_after" TIMESTAMP(3), - "status" TEXT NOT NULL DEFAULT 'ACTIVE', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "identities_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "identities_email_key" ON "identities"("email"); - --- AlterTable: Mitgliedschaft -> Identity (Option C) -ALTER TABLE "users" ADD COLUMN "identity_id" TEXT NOT NULL; - --- CreateIndex -CREATE INDEX "users_identity_id_idx" ON "users"("identity_id"); - --- CreateIndex: eine Person max. 1 Mitgliedschaft je Mandant -CREATE UNIQUE INDEX "users_tenant_id_identity_id_key" ON "users"("tenant_id", "identity_id"); - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_identity_id_fkey" FOREIGN KEY ("identity_id") REFERENCES "identities"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20260811140000_webauthn_identity/migration.sql b/prisma/migrations/20260811140000_webauthn_identity/migration.sql deleted file mode 100644 index 59d6f1f..0000000 --- a/prisma/migrations/20260811140000_webauthn_identity/migration.sql +++ /dev/null @@ -1,35 +0,0 @@ --- ============================================================================ --- WS4b (Option C) — WebAuthn/Passkeys von der Mitgliedschaft auf die GLOBALE Identity --- ============================================================================ --- Passkeys sind identitäts-, nicht mandantengebunden. Die Tabelle verliert daher --- tenant_id + RLS und verweist künftig auf `identities`. `webauthn_credentials` ist --- damit ein GLOBALES Modell (nicht mehr in TENANT_MODELS). --- --- Reihenfolge: erst RLS/Policy abbauen (die Policy referenziert tenant_id), dann die --- Spalten umbauen. identity_id ist NOT NULL — die Migration läuft auf einer leeren --- Tabelle (im Seed werden keine Passkeys angelegt; Bestand = Testdaten). --- ============================================================================ - --- RLS/Policy entfernen (war über webauthn_credentials-Migration + rls_enforce gesetzt). -ALTER TABLE "webauthn_credentials" DISABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "webauthn_credentials"; - --- DropForeignKey -ALTER TABLE "webauthn_credentials" DROP CONSTRAINT "webauthn_credentials_user_id_fkey"; - --- DropIndex -DROP INDEX "webauthn_credentials_tenant_id_idx"; - --- DropIndex -DROP INDEX "webauthn_credentials_user_id_idx"; - --- AlterTable: tenant_id/user_id → identity_id -ALTER TABLE "webauthn_credentials" DROP COLUMN "tenant_id", -DROP COLUMN "user_id", -ADD COLUMN "identity_id" TEXT NOT NULL; - --- CreateIndex -CREATE INDEX "webauthn_credentials_identity_id_idx" ON "webauthn_credentials"("identity_id"); - --- AddForeignKey -ALTER TABLE "webauthn_credentials" ADD CONSTRAINT "webauthn_credentials_identity_id_fkey" FOREIGN KEY ("identity_id") REFERENCES "identities"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260811150000_contract_user_auth_columns/migration.sql b/prisma/migrations/20260811150000_contract_user_auth_columns/migration.sql deleted file mode 100644 index 6798b5d..0000000 --- a/prisma/migrations/20260811150000_contract_user_auth_columns/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- ============================================================================ --- Contract-Migration (Option C) — ungenutzte Auth-Spalten von `users` entfernen --- ============================================================================ --- Abschluss des Expand/Contract-Umbaus: Passwort/MFA/Kill-Switch/Lockout leben seit --- WS1–WS4b ausschließlich an der GLOBALEN `Identity`. Die per-Mandant-Zeile `users` --- ist reine Mitgliedschaft — ihre früheren (Legacy-)Auth-Spalten werden hier entfernt. --- Nach WS1–WS4b referenziert kein Code diese Spalten mehr (durch tsc verifiziert). --- DROP COLUMN läuft auch auf gefüllten Tabellen (kein Reset nötig). --- ============================================================================ - -ALTER TABLE "users" DROP COLUMN "failed_logins", -DROP COLUMN "is_platform_admin", -DROP COLUMN "last_totp_step", -DROP COLUMN "locked_until", -DROP COLUMN "mfa_enrolled_at", -DROP COLUMN "mfa_secret", -DROP COLUMN "must_change_password", -DROP COLUMN "password_hash", -DROP COLUMN "recovery_codes", -DROP COLUMN "sessions_valid_after"; diff --git a/prisma/migrations/20260811160000_identity_ui_locale/migration.sql b/prisma/migrations/20260811160000_identity_ui_locale/migration.sql deleted file mode 100644 index fffbca5..0000000 --- a/prisma/migrations/20260811160000_identity_ui_locale/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ --- ============================================================================ --- Identity.uiLocale — persönliche UI-Sprache (Option C) --- ============================================================================ --- Neue, personengebundene Präferenz an der GLOBALEN `Identity` (folgt der Person --- über alle Mandanten). Steuert die UI-Sprache (next-intl, src/i18n/request.ts). --- Bewusst getrennt von `tenant_settings.locale` (= Vorlagen-Import-Sprache, Inhalt). --- Kein RLS (Identity ist global, nicht in TENANT_MODELS). Werte: 'de' | 'en'. --- ADD COLUMN mit Default läuft auch auf gefüllten Tabellen (kein Reset nötig). --- ============================================================================ - --- AlterTable -ALTER TABLE "identities" ADD COLUMN "ui_locale" TEXT NOT NULL DEFAULT 'de'; diff --git a/prisma/migrations/20260812090000_backup_dsgvo_foundation/migration.sql b/prisma/migrations/20260812090000_backup_dsgvo_foundation/migration.sql deleted file mode 100644 index 646751f..0000000 --- a/prisma/migrations/20260812090000_backup_dsgvo_foundation/migration.sql +++ /dev/null @@ -1,40 +0,0 @@ --- Backup/DSGVO-Lane: globale Modelle (kein tenant_id, kein RLS, analog Kataloge). - --- CreateTable -CREATE TABLE "deletion_certificates" ( - "id" TEXT NOT NULL, - "scope" TEXT NOT NULL, - "tenant_id" TEXT, - "tenant_slug" TEXT, - "subject_identity_id" TEXT, - "subject_email" TEXT, - "actor_id" TEXT, - "deleted_counts" JSONB NOT NULL DEFAULT '{}', - "anonymized_counts" JSONB NOT NULL DEFAULT '{}', - "snapshot_id" TEXT, - "reason" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "deletion_certificates_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tombstone_entries" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "model" TEXT NOT NULL, - "target_field" TEXT NOT NULL, - "target_value" TEXT NOT NULL, - "action" TEXT NOT NULL, - "anonymized_fields" JSONB NOT NULL DEFAULT '{}', - "reason" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "tombstone_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "deletion_certificates_tenant_id_idx" ON "deletion_certificates"("tenant_id"); -CREATE INDEX "deletion_certificates_subject_identity_id_idx" ON "deletion_certificates"("subject_identity_id"); -CREATE INDEX "tombstone_entries_tenant_id_idx" ON "tombstone_entries"("tenant_id"); -CREATE UNIQUE INDEX "tombstone_entries_tenant_id_model_target_value_key" ON "tombstone_entries"("tenant_id", "model", "target_value"); diff --git a/prisma/migrations/20260812100000_backup_jobs/migration.sql b/prisma/migrations/20260812100000_backup_jobs/migration.sql deleted file mode 100644 index 5c12c8b..0000000 --- a/prisma/migrations/20260812100000_backup_jobs/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ --- Backup-Portal-Lane: Job-Status für Portal-Restore, Export-jetzt und DSGVO-Zustellung. --- GLOBAL (kein tenant_id-RLS, analog deletion_certificates) — Betreiber-/Plattform-Fähigkeit. - --- CreateTable -CREATE TABLE "backup_jobs" ( - "id" TEXT NOT NULL, - "kind" TEXT NOT NULL, - "status" TEXT NOT NULL DEFAULT 'queued', - "tenant_id" TEXT NOT NULL, - "tenant_slug" TEXT, - "subject_identity_id" TEXT, - "actor_id" TEXT, - "snapshot_id" TEXT, - "params" JSONB NOT NULL DEFAULT '{}', - "result" JSONB, - "error" TEXT, - "download_token" TEXT, - "download_expires_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "backup_jobs_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "backup_jobs_download_token_key" ON "backup_jobs"("download_token"); -CREATE INDEX "backup_jobs_tenant_id_idx" ON "backup_jobs"("tenant_id"); -CREATE INDEX "backup_jobs_status_idx" ON "backup_jobs"("status"); diff --git a/prisma/migrations/20260817120000_backup_target_config/migration.sql b/prisma/migrations/20260817120000_backup_target_config/migration.sql deleted file mode 100644 index 5f77d1c..0000000 --- a/prisma/migrations/20260817120000_backup_target_config/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- Lane „Konfigurierbarer Backup-Zielspeicher": PlatformSetting (Singleton) um die --- Zielspeicher-Konfiguration erweitern. Additiv, alle Spalten nullable bzw. mit --- Default → kein Backfill nötig, bestehende Zeile bleibt gültig (Default 'local'). --- Das S3-Secret wird NUR verschlüsselt abgelegt (backup_s3_secret_key_enc, secret-crypto). - -ALTER TABLE "platform_settings" - ADD COLUMN "backup_target" TEXT NOT NULL DEFAULT 'local', - ADD COLUMN "backup_local_dir" TEXT, - ADD COLUMN "backup_s3_endpoint" TEXT, - ADD COLUMN "backup_s3_bucket" TEXT, - ADD COLUMN "backup_s3_region" TEXT, - ADD COLUMN "backup_s3_access_key" TEXT, - ADD COLUMN "backup_s3_secret_key_enc" TEXT; diff --git a/prisma/migrations/20260817130000_incidents/migration.sql b/prisma/migrations/20260817130000_incidents/migration.sql deleted file mode 100644 index 557a11c..0000000 --- a/prisma/migrations/20260817130000_incidents/migration.sql +++ /dev/null @@ -1,246 +0,0 @@ --- 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 $$; diff --git a/prisma/migrations/20260818090000_incident_links_c/migration.sql b/prisma/migrations/20260818090000_incident_links_c/migration.sql deleted file mode 100644 index 3368aa1..0000000 --- a/prisma/migrations/20260818090000_incident_links_c/migration.sql +++ /dev/null @@ -1,43 +0,0 @@ --- Modul „Vorfälle" IM-C: Verknüpfung Nachweise + Abschluss-Felder (§8/§9). --- --- 1) incidents: Wirksamkeit der Maßnahmen + optionaler Post-Incident-Review --- (Kurzbericht, speist das Management-Review als Feld/Notiz). --- 2) incident_evidence: n:m-Verknüpfung Vorfall ↔ zentrales Evidence-Modell --- (Beweissicherung, ISO A.5.28). - --- AlterTable -ALTER TABLE "incidents" ADD COLUMN "measures_effectiveness" TEXT, -ADD COLUMN "post_incident_review" TEXT; - --- CreateTable -CREATE TABLE "incident_evidence" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "incident_id" TEXT NOT NULL, - "evidence_id" TEXT NOT NULL, - "note" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "incident_evidence_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "incident_evidence_tenant_id_idx" ON "incident_evidence"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "incident_evidence_incident_id_evidence_id_key" ON "incident_evidence"("incident_id", "evidence_id"); - --- AddForeignKey -ALTER TABLE "incident_evidence" ADD CONSTRAINT "incident_evidence_incident_id_fkey" FOREIGN KEY ("incident_id") REFERENCES "incidents"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "incident_evidence" ADD CONSTRAINT "incident_evidence_evidence_id_fkey" FOREIGN KEY ("evidence_id") REFERENCES "evidence"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- Row Level Security (analog übrige Mandanten-Tabellen; FORCE + WITH CHECK für den --- scharfen isms_app-Pfad, F-04). Lokal (Owner-Rolle ohne FORCE) unverändert. -ALTER TABLE "incident_evidence" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "incident_evidence"; -CREATE POLICY tenant_isolation ON "incident_evidence" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "incident_evidence" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260818100000_incident_inbound_d/migration.sql b/prisma/migrations/20260818100000_incident_inbound_d/migration.sql deleted file mode 100644 index 985dfbe..0000000 --- a/prisma/migrations/20260818100000_incident_inbound_d/migration.sql +++ /dev/null @@ -1,67 +0,0 @@ --- IM-D — E-Mail-to-Ticket (Inbound) + Provisionierung. --- 1) incidents.inbound_message_id: Idempotenz gegen Doppel-Ticket beim erneuten --- IMAP-Abholen derselben Mail (Dedupe per Message-ID, KONZEPT §2). --- 2) incident_intake_config: mandantengebundene Intake-Konfiguration (RLS). --- 3) incident_inbound_review: plattformweite Review-Queue (KEINE RLS — tenant-los). - --- AlterTable -ALTER TABLE "incidents" ADD COLUMN "inbound_message_id" TEXT; - --- CreateIndex -CREATE INDEX "incidents_inbound_message_id_idx" ON "incidents"("inbound_message_id"); - --- CreateTable (mandantengebunden, RLS) -CREATE TABLE "incident_intake_config" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "token" TEXT NOT NULL, - "allowlist_domains" TEXT[] DEFAULT ARRAY[]::TEXT[], - "source_address" TEXT, - "status" TEXT NOT NULL DEFAULT 'weiterleitung_ausstehend', - "verified_at" TIMESTAMP(3), - "notify_email" TEXT, - "notify_locale" TEXT NOT NULL DEFAULT 'de', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "incident_intake_config_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "incident_intake_config_tenant_id_key" ON "incident_intake_config"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "incident_intake_config_token_key" ON "incident_intake_config"("token"); - --- CreateTable (plattformweit, KEINE RLS — tenant-los) -CREATE TABLE "incident_inbound_review" ( - "id" TEXT NOT NULL, - "message_id" TEXT, - "sender" TEXT NOT NULL, - "subject" TEXT, - "recipient" TEXT, - "token" TEXT, - "reason" TEXT NOT NULL, - "status" TEXT NOT NULL DEFAULT 'offen', - "tenant_id" TEXT, - "received_at" TIMESTAMP(3) NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "incident_inbound_review_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "incident_inbound_review_status_idx" ON "incident_inbound_review"("status"); - --- CreateIndex -CREATE INDEX "incident_inbound_review_message_id_idx" ON "incident_inbound_review"("message_id"); - --- Row Level Security auf der mandantengebundenen Intake-Konfiguration (analog übrige --- Mandanten-Tabellen; FORCE + WITH CHECK für den scharfen isms_app-Pfad, F-04). Die --- Review-Queue bleibt bewusst OHNE RLS (plattformweite Betreiber-Sicht). -ALTER TABLE "incident_intake_config" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "incident_intake_config"; -CREATE POLICY tenant_isolation ON "incident_intake_config" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "incident_intake_config" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260821090000_framework_core/migration.sql b/prisma/migrations/20260821090000_framework_core/migration.sql deleted file mode 100644 index f8d640d..0000000 --- a/prisma/migrations/20260821090000_framework_core/migration.sql +++ /dev/null @@ -1,67 +0,0 @@ --- CreateEnum -CREATE TYPE "Framework" AS ENUM ('ISO_27001', 'TISAX'); - --- DropIndex -DROP INDEX "policy_package_states_tenant_id_key"; - --- DropIndex -DROP INDEX "policy_template_versions_version_key"; - --- AlterTable -ALTER TABLE "policy_package_states" ADD COLUMN "framework" "Framework" NOT NULL DEFAULT 'TISAX'; - --- AlterTable -ALTER TABLE "policy_requirements" ADD COLUMN "framework" "Framework" NOT NULL DEFAULT 'TISAX'; - --- AlterTable -ALTER TABLE "policy_template_versions" ADD COLUMN "framework" "Framework" NOT NULL DEFAULT 'TISAX'; - --- CreateTable -CREATE TABLE "tenant_frameworks" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "framework" "Framework" NOT NULL, - "is_primary" BOOLEAN NOT NULL DEFAULT false, - "config" JSONB, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "tenant_frameworks_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "tenant_frameworks_tenant_id_idx" ON "tenant_frameworks"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "tenant_frameworks_tenant_id_framework_key" ON "tenant_frameworks"("tenant_id", "framework"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_package_states_tenant_id_framework_key" ON "policy_package_states"("tenant_id", "framework"); - --- CreateIndex -CREATE INDEX "policy_requirements_tenant_id_framework_idx" ON "policy_requirements"("tenant_id", "framework"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_template_versions_framework_version_key" ON "policy_template_versions"("framework", "version"); - - --- ───────────────────────────────────────────────────────────────────────────── --- AP1 — Framework-Dimension: Backfill + RLS (manuell an das Diff-DDL angehängt). --- ───────────────────────────────────────────────────────────────────────────── - --- Backfill: Bestandsmandanten führen bisher TISAX → je Mandant eine TenantFramework- --- Zeile (isPrimary=true) anlegen. Idempotent über den (tenant_id, framework)-Unique. --- Die framework-Spalten in policy_requirements/policy_package_states/ --- policy_template_versions sind bereits über DEFAULT 'TISAX' backfilled (s. o.). -INSERT INTO "tenant_frameworks" ("id", "tenant_id", "framework", "is_primary", "created_at") -SELECT gen_random_uuid()::text, t."id", 'TISAX', true, CURRENT_TIMESTAMP -FROM "tenants" t -ON CONFLICT ("tenant_id", "framework") DO NOTHING; - --- Row Level Security auf der mandantengebundenen Framework-Tabelle (F-04-Muster: --- FORCE + WITH CHECK für den scharfen isms_app-Pfad, analog übrige Mandanten-Tabellen). -ALTER TABLE "tenant_frameworks" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "tenant_frameworks"; -CREATE POLICY tenant_isolation ON "tenant_frameworks" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "tenant_frameworks" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260821100000_soa_entries/migration.sql b/prisma/migrations/20260821100000_soa_entries/migration.sql deleted file mode 100644 index 5f64379..0000000 --- a/prisma/migrations/20260821100000_soa_entries/migration.sql +++ /dev/null @@ -1,37 +0,0 @@ --- CreateTable -CREATE TABLE "soa_entries" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "framework" "Framework" NOT NULL, - "control" TEXT NOT NULL, - "title" TEXT, - "applicable" BOOLEAN NOT NULL DEFAULT true, - "justification" TEXT NOT NULL DEFAULT '', - "source" TEXT, - "implementation_status" TEXT NOT NULL DEFAULT 'geplant', - "owner_id" TEXT, - "policy_code" TEXT, - "evidence_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "soa_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "soa_entries_tenant_id_idx" ON "soa_entries"("tenant_id"); - --- CreateIndex -CREATE INDEX "soa_entries_tenant_id_framework_idx" ON "soa_entries"("tenant_id", "framework"); - --- CreateIndex -CREATE UNIQUE INDEX "soa_entries_tenant_id_framework_control_key" ON "soa_entries"("tenant_id", "framework", "control"); - - --- AP3 — Row Level Security auf der SoA-Tabelle (F-04: FORCE + WITH CHECK). -ALTER TABLE "soa_entries" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "soa_entries"; -CREATE POLICY tenant_isolation ON "soa_entries" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "soa_entries" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260821110000_mgmt_clauses/migration.sql b/prisma/migrations/20260821110000_mgmt_clauses/migration.sql deleted file mode 100644 index 4da778d..0000000 --- a/prisma/migrations/20260821110000_mgmt_clauses/migration.sql +++ /dev/null @@ -1,175 +0,0 @@ --- CreateTable -CREATE TABLE "kpis" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "data_source" TEXT, - "unit" TEXT, - "target" TEXT, - "cadence" TEXT NOT NULL DEFAULT 'monatlich', - "owner_id" TEXT, - "active" BOOLEAN NOT NULL DEFAULT true, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "kpis_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "kpi_values" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "kpi_id" TEXT NOT NULL, - "period" TEXT NOT NULL, - "value" TEXT NOT NULL, - "note" TEXT, - "recorded_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "kpi_values_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "management_reviews" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "review_date" TIMESTAMP(3) NOT NULL, - "status" TEXT NOT NULL DEFAULT 'entwurf', - "inputs" TEXT, - "results" TEXT, - "owner_id" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "management_reviews_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "management_review_decisions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "review_id" TEXT NOT NULL, - "decision" TEXT NOT NULL, - "owner_id" TEXT, - "due_date" TIMESTAMP(3), - "status" TEXT NOT NULL DEFAULT 'offen', - - CONSTRAINT "management_review_decisions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "nonconformities" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "ref_no" TEXT NOT NULL, - "source" TEXT NOT NULL, - "description" TEXT NOT NULL, - "immediate_correction" TEXT, - "status" TEXT NOT NULL DEFAULT 'offen', - "owner_id" TEXT, - "detected_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "closed_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "nonconformities_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "corrective_actions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "nonconformity_id" TEXT NOT NULL, - "root_cause" TEXT, - "action" TEXT NOT NULL, - "owner_id" TEXT, - "due_date" TIMESTAMP(3), - "status" TEXT NOT NULL DEFAULT 'geplant', - "effectiveness_check" TEXT, - "effectiveness_confirmed_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "corrective_actions_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "kpis_tenant_id_idx" ON "kpis"("tenant_id"); - --- CreateIndex -CREATE INDEX "kpi_values_tenant_id_idx" ON "kpi_values"("tenant_id"); - --- CreateIndex -CREATE INDEX "kpi_values_kpi_id_idx" ON "kpi_values"("kpi_id"); - --- CreateIndex -CREATE UNIQUE INDEX "kpi_values_tenant_id_kpi_id_period_key" ON "kpi_values"("tenant_id", "kpi_id", "period"); - --- CreateIndex -CREATE INDEX "management_reviews_tenant_id_idx" ON "management_reviews"("tenant_id"); - --- CreateIndex -CREATE INDEX "management_review_decisions_tenant_id_idx" ON "management_review_decisions"("tenant_id"); - --- CreateIndex -CREATE INDEX "management_review_decisions_review_id_idx" ON "management_review_decisions"("review_id"); - --- CreateIndex -CREATE INDEX "nonconformities_tenant_id_idx" ON "nonconformities"("tenant_id"); - --- CreateIndex -CREATE UNIQUE INDEX "nonconformities_tenant_id_ref_no_key" ON "nonconformities"("tenant_id", "ref_no"); - --- CreateIndex -CREATE INDEX "corrective_actions_tenant_id_idx" ON "corrective_actions"("tenant_id"); - --- CreateIndex -CREATE INDEX "corrective_actions_nonconformity_id_idx" ON "corrective_actions"("nonconformity_id"); - --- AddForeignKey -ALTER TABLE "kpi_values" ADD CONSTRAINT "kpi_values_kpi_id_fkey" FOREIGN KEY ("kpi_id") REFERENCES "kpis"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "management_review_decisions" ADD CONSTRAINT "management_review_decisions_review_id_fkey" FOREIGN KEY ("review_id") REFERENCES "management_reviews"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "corrective_actions" ADD CONSTRAINT "corrective_actions_nonconformity_id_fkey" FOREIGN KEY ("nonconformity_id") REFERENCES "nonconformities"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- AP4 — Row Level Security (F-04: FORCE + WITH CHECK) für die neuen Mandanten-Tabellen. -ALTER TABLE "kpis" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "kpis"; -CREATE POLICY tenant_isolation ON "kpis" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "kpis" FORCE ROW LEVEL SECURITY; -ALTER TABLE "kpi_values" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "kpi_values"; -CREATE POLICY tenant_isolation ON "kpi_values" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "kpi_values" FORCE ROW LEVEL SECURITY; -ALTER TABLE "management_reviews" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "management_reviews"; -CREATE POLICY tenant_isolation ON "management_reviews" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "management_reviews" FORCE ROW LEVEL SECURITY; -ALTER TABLE "management_review_decisions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "management_review_decisions"; -CREATE POLICY tenant_isolation ON "management_review_decisions" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "management_review_decisions" FORCE ROW LEVEL SECURITY; -ALTER TABLE "nonconformities" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "nonconformities"; -CREATE POLICY tenant_isolation ON "nonconformities" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "nonconformities" FORCE ROW LEVEL SECURITY; -ALTER TABLE "corrective_actions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "corrective_actions"; -CREATE POLICY tenant_isolation ON "corrective_actions" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "corrective_actions" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260821120000_doc_control/migration.sql b/prisma/migrations/20260821120000_doc_control/migration.sql deleted file mode 100644 index d86bdc8..0000000 --- a/prisma/migrations/20260821120000_doc_control/migration.sql +++ /dev/null @@ -1,68 +0,0 @@ --- AlterTable -ALTER TABLE "policy_documents" ADD COLUMN "next_review_at" TIMESTAMP(3), -ADD COLUMN "review_cycle" TEXT; - --- CreateTable -CREATE TABLE "policy_acknowledgements" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "policy_document_id" TEXT NOT NULL, - "version" TEXT NOT NULL, - "user_id" TEXT NOT NULL, - "acknowledged_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_acknowledgements_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "policy_document_versions" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "policy_document_id" TEXT NOT NULL, - "version" TEXT NOT NULL, - "title" TEXT NOT NULL, - "change_note" TEXT, - "created_by" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "policy_document_versions_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "policy_acknowledgements_tenant_id_idx" ON "policy_acknowledgements"("tenant_id"); - --- CreateIndex -CREATE INDEX "policy_acknowledgements_policy_document_id_version_idx" ON "policy_acknowledgements"("policy_document_id", "version"); - --- CreateIndex -CREATE UNIQUE INDEX "policy_acknowledgements_tenant_id_policy_document_id_user_i_key" ON "policy_acknowledgements"("tenant_id", "policy_document_id", "user_id", "version"); - --- CreateIndex -CREATE INDEX "policy_document_versions_tenant_id_idx" ON "policy_document_versions"("tenant_id"); - --- CreateIndex -CREATE INDEX "policy_document_versions_policy_document_id_idx" ON "policy_document_versions"("policy_document_id"); - --- CreateIndex -CREATE INDEX "policy_documents_tenant_id_next_review_at_idx" ON "policy_documents"("tenant_id", "next_review_at"); - --- AddForeignKey -ALTER TABLE "policy_acknowledgements" ADD CONSTRAINT "policy_acknowledgements_policy_document_id_fkey" FOREIGN KEY ("policy_document_id") REFERENCES "policy_documents"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "policy_document_versions" ADD CONSTRAINT "policy_document_versions_policy_document_id_fkey" FOREIGN KEY ("policy_document_id") REFERENCES "policy_documents"("id") ON DELETE CASCADE ON UPDATE CASCADE; - - --- AP5 — Row Level Security (F-04) für die neuen Mandanten-Tabellen. -ALTER TABLE "policy_acknowledgements" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_acknowledgements"; -CREATE POLICY tenant_isolation ON "policy_acknowledgements" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_acknowledgements" FORCE ROW LEVEL SECURITY; -ALTER TABLE "policy_document_versions" ENABLE ROW LEVEL SECURITY; -DROP POLICY IF EXISTS tenant_isolation ON "policy_document_versions"; -CREATE POLICY tenant_isolation ON "policy_document_versions" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "policy_document_versions" FORCE ROW LEVEL SECURITY; diff --git a/prisma/migrations/20260907084110_add_process_dependencies/migration.sql b/prisma/migrations/20260907084110_add_process_dependencies/migration.sql deleted file mode 100644 index df132d0..0000000 --- a/prisma/migrations/20260907084110_add_process_dependencies/migration.sql +++ /dev/null @@ -1,35 +0,0 @@ --- CreateTable -CREATE TABLE "process_dependencies" ( - "id" TEXT NOT NULL, - "tenant_id" TEXT NOT NULL, - "source_process_id" TEXT NOT NULL, - "target_process_id" TEXT NOT NULL, - "note" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "process_dependencies_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "process_dependencies_tenant_id_idx" ON "process_dependencies"("tenant_id"); - --- CreateIndex -CREATE INDEX "process_dependencies_target_process_id_idx" ON "process_dependencies"("target_process_id"); - --- CreateIndex -CREATE UNIQUE INDEX "process_dependencies_source_process_id_target_process_id_key" ON "process_dependencies"("source_process_id", "target_process_id"); - --- AddForeignKey -ALTER TABLE "process_dependencies" ADD CONSTRAINT "process_dependencies_source_process_id_fkey" FOREIGN KEY ("source_process_id") REFERENCES "processes"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "process_dependencies" ADD CONSTRAINT "process_dependencies_target_process_id_fkey" FOREIGN KEY ("target_process_id") REFERENCES "processes"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- Row Level Security: Mandantentrennung (analog übrige tenant-scoped Tabellen) -ALTER TABLE "process_dependencies" ENABLE ROW LEVEL SECURITY; - -CREATE POLICY tenant_isolation ON "process_dependencies" - USING ("tenant_id" = current_setting('app.tenant_id', true)) - WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true)); -ALTER TABLE "process_dependencies" FORCE ROW LEVEL SECURITY;