// ISMS-Tool — Prisma-Schema // Fundament (Iteration 1): Mandanten, Nutzer, RBAC, Audit-Log. // Fachliche Entitäten (Assets, BIA, Risiken, …) folgen je Iteration — siehe docs/SPEC.md §5. // // Multi-Tenant-Regel: Jede fachliche Tabelle trägt tenantId. Zugriff nur über den // zentralen Tenant-Guard (src/server/db.ts); zusätzlich Postgres RLS (prisma/rls.sql). generator client { provider = "prisma-client-js" previewFeatures = ["postgresqlExtensions"] } datasource db { provider = "postgresql" extensions = [pgvector(map: "vector")] } enum TenantStatus { ACTIVE SUSPENDED ARCHIVED } enum UserStatus { ACTIVE INVITED LOCKED DEACTIVATED } model Tenant { id String @id @default(cuid()) name String slug String @unique short String? sector String? status TenantStatus @default(ACTIVE) config Json @default("{}") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") users User[] roles Role[] auditLogs AuditLog[] settings TenantSettings? modules TenantModule[] @@map("tenants") } // Mandanten-Einstellungen: Quelle der ISMS-Template-Variablen (§4.1) + Branding/Policy model TenantSettings { id String @id @default(cuid()) tenantId String @unique @map("tenant_id") orgName String @map("org_name") orgShort String? @map("org_short") address String? sector String? duns String? ismsScope String? @map("isms_scope") ismsScopeDescription String? @map("isms_scope_description") roleManagement String? @map("role_management") roleIsb String? @map("role_isb") roleItLead String? @map("role_it_lead") roleDpo String? @map("role_dpo") logoKey String? @map("logo_key") accent String? locale String @default("de") timezone String @default("Europe/Berlin") tisaxLevel String @default("AL2") @map("tisax_level") // AL2 | AL3 securityPolicy Json @default("{}") @map("security_policy") // pw/mfa/session smtp Json @default("{}") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("tenant_settings") } // Modul-Freischaltung je Mandant (Feature-Toggle) — gated Navigation, API und Daten model TenantModule { id String @id @default(cuid()) tenantId String @map("tenant_id") moduleKey String @map("module_key") enabled Boolean @default(true) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@unique([tenantId, moduleKey]) @@index([tenantId]) @@map("tenant_modules") } model User { id String @id @default(cuid()) tenantId String @map("tenant_id") email String passwordHash String @map("password_hash") name String status UserStatus @default(ACTIVE) // Erzwungener Passwortwechsel beim nächsten Login (Initial-/Reset-Passwort). mustChangePassword Boolean @default(false) @map("must_change_password") // Optionale TOTP-MFA je Nutzer (Paket C). recoveryCodes = SHA-256-Hashes. mfaSecret String? @map("mfa_secret") mfaEnrolledAt DateTime? @map("mfa_enrolled_at") recoveryCodes Json @default("[]") @map("recovery_codes") // Plattform-Admin ist mandantenübergreifend (Betreiberrolle) isPlatformAdmin Boolean @default(false) @map("is_platform_admin") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") tenant Tenant @relation(fields: [tenantId], references: [id]) userRoles UserRole[] ownedAssets Asset[] @relation("assetOwner") ownedProcesses Process[] @relation("processOwner") ownedRisks Risk[] @relation("riskOwner") ownedMeasures Measure[] @relation("measureOwner") @@unique([tenantId, email]) @@index([tenantId]) @@map("users") } model Role { id String @id @default(cuid()) tenantId String @map("tenant_id") key String // z. B. tenant-admin, isb, auditor, owner, user name String tenant Tenant @relation(fields: [tenantId], references: [id]) userRoles UserRole[] rolePermissions RolePermission[] @@unique([tenantId, key]) @@index([tenantId]) @@map("roles") } model Permission { id String @id @default(cuid()) key String @unique // z. B. asset:read, risk:write, incident:manage rolePermissions RolePermission[] @@map("permissions") } model UserRole { userId String @map("user_id") roleId String @map("role_id") user User @relation(fields: [userId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) @@id([userId, roleId]) @@map("user_roles") } model RolePermission { roleId String @map("role_id") permissionId String @map("permission_id") role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@id([roleId, permissionId]) @@map("role_permissions") } // Plattform-Administratoren (Betreiber). Getrennter Store OHNE tenant_id, eigener // Login (/platform/login), TOTP-MFA-Pflicht + Recovery-Codes. Kein Zugriff auf // Mandanten-Fachdaten (nur Betrieb: Mandanten, Module, Lebenszyklus). §Phase-1-Härtung. model PlatformAdmin { id String @id @default(cuid()) email String @unique passwordHash String @map("password_hash") name String status String @default("ACTIVE") // ACTIVE | LOCKED | DISABLED mfaSecret String? @map("mfa_secret") mfaEnrolledAt DateTime? @map("mfa_enrolled_at") recoveryCodes Json @default("[]") @map("recovery_codes") // SHA-256-Hashes der Recovery-Codes failedLogins Int @default(0) @map("failed_logins") lockedUntil DateTime? @map("locked_until") lastLoginAt DateTime? @map("last_login_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("platform_admins") } // Plattformweite Betriebseinstellungen (Singleton). Steuert u. a., ob MFA für // Plattform-Admins verpflichtend ist (Default: aus → MFA optional, Paket C). model PlatformSetting { id String @id @default("singleton") mfaRequired Boolean @default(false) @map("mfa_required") updatedAt DateTime @updatedAt @map("updated_at") @@map("platform_settings") } // ── Assets & BIA (ein Modul, SPEC §4.1) ───────────────────────────────────── enum AssetType { INFORMATION SYSTEM APPLICATION LOCATION SUPPLIER IT_SERVICE SOFTWARE PROJECT PERSON DATA } enum AssetStatus { ACTIVE PLANNED RETIRED } enum ProcessAssetRole { PRIMARY SECONDARY } enum ProcessCategory { CORE // Kernprozess MANAGEMENT // Managementprozess SUPPORT // Unterstützender Prozess } model Asset { id String @id @default(cuid()) tenantId String @map("tenant_id") name String description String? type AssetType status AssetStatus @default(ACTIVE) ownerId String? @map("owner_id") location String? tags String[] @default([]) // Schutzbedarf 1–4 je Schutzziel (C/I/A) confidentiality Int @default(1) integrity Int @default(1) availability Int @default(1) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") owner User? @relation("assetOwner", fields: [ownerId], references: [id]) relationsFrom AssetRelation[] @relation("relationFrom") relationsTo AssetRelation[] @relation("relationTo") processAssets ProcessAsset[] riskAssets RiskAsset[] // Lieferanten-/IT-Service-Erweiterungen (1:1) + Kind-Entitäten supplierProfile SupplierProfile? serviceProfile ITServiceProfile? @relation("serviceAsset") serviceProvided ITServiceProfile[] @relation("serviceProvider") softwareProfile SoftwareProfile? @relation("softwareAsset") softwareProvided SoftwareProfile[] @relation("softwareProvider") projectProfile ProjectProfile? assessments SupplierAssessment[] evidence SupplierEvidence[] decisions ManagementDecision[] contracts Contract[] ndas Nda[] subcontractors Subcontractor[] raci ServiceControlResponsibility[] maturity MaturityAssessment? @@index([tenantId]) @@index([tenantId, type]) @@map("assets") } model AssetRelation { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") relatedAssetId String @map("related_asset_id") // z. B. depends_on, hosts, processes_data_of type String @default("depends_on") asset Asset @relation("relationFrom", fields: [assetId], references: [id], onDelete: Cascade) relatedAsset Asset @relation("relationTo", fields: [relatedAssetId], references: [id], onDelete: Cascade) @@unique([assetId, relatedAssetId, type]) @@index([tenantId]) @@map("asset_relations") } model Process { id String @id @default(cuid()) tenantId String @map("tenant_id") name String description String? category ProcessCategory @default(CORE) ownerId String? @map("owner_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") owner User? @relation("processOwner", fields: [ownerId], references: [id]) processAssets ProcessAsset[] bia BiaEntry? risks Risk[] @@index([tenantId]) @@map("processes") } model ProcessAsset { id String @id @default(cuid()) tenantId String @map("tenant_id") processId String @map("process_id") assetId String @map("asset_id") role ProcessAssetRole @default(SECONDARY) process Process @relation(fields: [processId], references: [id], onDelete: Cascade) asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@unique([processId, assetId]) @@index([tenantId]) @@map("process_assets") } model BiaEntry { id String @id @default(cuid()) tenantId String @map("tenant_id") processId String @unique @map("process_id") // Wiederanlaufparameter in Stunden (SPEC §4.1.2) rtoHours Int? @map("rto_hours") rpoHours Int? @map("rpo_hours") mtdHours Int? @map("mtd_hours") // Schadenshöhe 1–4 je Schutzziel impactC Int @default(1) @map("impact_c") impactI Int @default(1) @map("impact_i") impactA Int @default(1) @map("impact_a") // Kritikalität 1–4, abgeleitet (Max-Prinzip), redundant gespeichert für Listen/Filter criticality Int @default(1) notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") process Process @relation(fields: [processId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("bia_entries") } // ── Risikoanalyse (SPEC §4.2) ─────────────────────────────────────────────── enum RiskTreatment { AVOID // Vermeiden MITIGATE // Vermindern TRANSFER // Übertragen ACCEPT // Akzeptieren } enum RiskStatus { OPEN IN_TREATMENT ACCEPTED CLOSED } model Risk { id String @id @default(cuid()) tenantId String @map("tenant_id") // Laufende Nummer je Mandant → Anzeige als "R-001" refNo Int @map("ref_no") title String description String? threat String? // Bedrohung (Katalog folgt, SPEC §5) vulnerability String? // Schwachstelle // Brutto-Bewertung: Eintrittswahrscheinlichkeit × Auswirkung (je 1–5) likelihood Int @default(1) impact Int @default(1) score Int @default(1) // redundant für Listen/Heatmap // Rest-Risiko nach Maßnahmen (berechnet, dezimal wegen Teil-Minderungen) residualLikelihood Float? @map("residual_likelihood") residualImpact Float? @map("residual_impact") residualScore Float? @map("residual_score") treatment RiskTreatment @default(MITIGATE) status RiskStatus @default(OPEN) ownerId String? @map("owner_id") processId String? @map("process_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") owner User? @relation("riskOwner", fields: [ownerId], references: [id]) process Process? @relation(fields: [processId], references: [id]) riskAssets RiskAsset[] riskMeasures RiskMeasure[] @@unique([tenantId, refNo]) @@index([tenantId]) @@index([tenantId, status]) @@map("risks") } model RiskAsset { id String @id @default(cuid()) tenantId String @map("tenant_id") riskId String @map("risk_id") assetId String @map("asset_id") risk Risk @relation(fields: [riskId], references: [id], onDelete: Cascade) asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@unique([riskId, assetId]) @@index([tenantId]) @@map("risk_assets") } // ── Maßnahmen (SPEC §4.4) ─────────────────────────────────────────────────── enum MeasureStatus { OPEN IN_PROGRESS DONE } enum MeasurePriority { LOW MEDIUM HIGH } model Measure { id String @id @default(cuid()) tenantId String @map("tenant_id") refNo Int @map("ref_no") // Anzeige als "M-001" title String description String? status MeasureStatus @default(OPEN) priority MeasurePriority @default(MEDIUM) ownerId String? @map("owner_id") dueDate DateTime? @map("due_date") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") owner User? @relation("measureOwner", fields: [ownerId], references: [id]) riskMeasures RiskMeasure[] @@unique([tenantId, refNo]) @@index([tenantId]) @@index([tenantId, status]) @@map("measures") } // Verknüpfung Risiko ↔ Maßnahme inkl. erwarteter Risikominderung: // Das Rest-Risiko wird aus likelihood/impact minus der Summe der // Minderungen aller verknüpften Maßnahmen berechnet (siehe src/server/risk-calc.ts). model RiskMeasure { id String @id @default(cuid()) tenantId String @map("tenant_id") riskId String @map("risk_id") measureId String @map("measure_id") // Minderung je Dimension (0,00–4,00) — mehrere Maßnahmen summieren sich, // bis ein voller Punkt (oder mehr) erreicht ist reductionLikelihood Float @default(0) @map("reduction_likelihood") reductionImpact Float @default(0) @map("reduction_impact") risk Risk @relation(fields: [riskId], references: [id], onDelete: Cascade) measure Measure @relation(fields: [measureId], references: [id], onDelete: Cascade) @@unique([riskId, measureId]) @@index([tenantId]) @@map("risk_measures") } // Globale Kataloge (SPEC §5: Threat/Vulnerability, mandantenübergreifend). // Risiken speichern weiterhin Freitext — der Katalog liefert Vorschläge (datalist), // eigene Formulierungen bleiben möglich. model Threat { id String @id @default(cuid()) name String @unique @@map("threats") } model Vulnerability { id String @id @default(cuid()) name String @unique @@map("vulnerabilities") } // ── Lieferanten- & IT-Service-Management (VDA-ISA 2027 Kap. 6, NIS2 Art. 21(2)(d)) ── // Grundsatz: Lieferanten UND IT-Services sind Assets (type SUPPLIER | IT_SERVICE). // SupplierProfile/ITServiceProfile sind 1:1-Erweiterungen eines Asset; alle // Kind-Entitäten hängen am Asset (subject) — keine parallele Datenhaltung. enum SupplierLifecycle { ACTIVE ONBOARDING UNDER_REVIEW OFFBOARDED } enum AssessmentType { QUESTIONNAIRE SELF_ASSESSMENT AUDIT } enum EvidenceKind { CERTIFICATE TISAX_LABEL ATTESTATION AUDIT_REPORT SELF_ASSESSMENT } enum RaciResponsibility { PROVIDER US SHARED } model SupplierProfile { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @unique @map("asset_id") refNo Int @map("ref_no") sector String? serviceDesc String? @map("service_desc") criticality Int @default(1) dataCategories String[] @default([]) @map("data_categories") nis2Relevant Boolean @default(false) @map("nis2_relevant") lifecycle SupplierLifecycle @default(ACTIVE) contact String? tisaxLabel String? @map("tisax_label") tisaxValidTo DateTime? @map("tisax_valid_to") nextReview DateTime? @map("next_review") notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@unique([tenantId, refNo]) @@index([tenantId]) @@map("supplier_profiles") } model ITServiceProfile { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @unique @map("asset_id") refNo Int @map("ref_no") providerAssetId String? @map("provider_asset_id") // Asset(type SUPPLIER) criticality Int @default(1) internal Boolean @default(false) notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") asset Asset @relation("serviceAsset", fields: [assetId], references: [id], onDelete: Cascade) provider Asset? @relation("serviceProvider", fields: [providerAssetId], references: [id]) @@unique([tenantId, refNo]) @@index([tenantId]) @@map("it_service_profiles") } // Software-Whitelist als Asset (analog IT-Service, R11/VA-16): eine freigegebene // Software ist ein Asset(type SOFTWARE) mit optionalem Anbieter (Asset type SUPPLIER). model SoftwareProfile { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @unique @map("asset_id") refNo Int @map("ref_no") providerAssetId String? @map("provider_asset_id") // Asset(type SUPPLIER) version String? // Version/Patch-Stand approvalStatus SoftwareApprovalStatus @default(BEANTRAGT) @map("approval_status") approvedBy String? @map("approved_by") criticality Int @default(1) nextReview DateTime? @map("next_review") notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") asset Asset @relation("softwareAsset", fields: [assetId], references: [id], onDelete: Cascade) provider Asset? @relation("softwareProvider", fields: [providerAssetId], references: [id]) @@unique([tenantId, refNo]) @@index([tenantId]) @@map("software_profiles") } enum SoftwareApprovalStatus { BEANTRAGT // Freigabe beantragt FREIGEGEBEN // freigegeben (Whitelist) GESPERRT // gesperrt/untersagt } // Informationssicherheit in Projekten (R01/VA-19): ein Projekt ist ein Asset(type PROJECT) // mit Kritikalität (C/I/A am Asset) und Risiko-Verknüpfung (RiskAsset). model ProjectProfile { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @unique @map("asset_id") refNo Int @map("ref_no") classification String? // IS-Klassifizierung isbInvolved Boolean @default(false) @map("isb_involved") status ProjectStatus @default(GEPLANT) notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") createdBy String? @map("created_by") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@unique([tenantId, refNo]) @@index([tenantId]) @@map("project_profiles") } enum ProjectStatus { GEPLANT LAUFEND ABGESCHLOSSEN ABGEBROCHEN } // Bewertung & Nachweise (6.1.1) model SupplierAssessment { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") type AssessmentType score Int? selfScore Int? @map("self_score") verifiedScore Int? @map("verified_score") date DateTime? nextReview DateTime? @map("next_review") result String? createdAt DateTime @default(now()) @map("created_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("supplier_assessments") } model SupplierEvidence { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") // Subjekt (Lieferant ODER Service) kind EvidenceKind name String? protectsCia String? @map("protects_cia") validTo DateTime? @map("valid_to") adequacyChecked Boolean @default(false) @map("adequacy_checked") createdAt DateTime @default(now()) @map("created_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("supplier_evidence") } model ManagementDecision { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") reasonNoAudit String @map("reason_no_audit") decision String decidedBy String? @map("decided_by") date DateTime @default(now()) recordRef String? @map("record_ref") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("management_decisions") } // Vertrag & Geheimhaltung (6.1.1 / 6.1.2) model Contract { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") type String @default("service") avDpa Boolean @default(false) @map("av_dpa") securityClauses Boolean @default(false) @map("security_clauses") flowdown Boolean @default(false) customerRequirementsPassed Boolean @default(false) @map("customer_requirements_passed") validFrom DateTime? @map("valid_from") validTo DateTime? @map("valid_to") reference String? createdAt DateTime @default(now()) @map("created_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("contracts") } model Nda { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") parties String? infoScope String? @map("info_scope") subject String? validFrom DateTime? @map("valid_from") validTo DateTime? @map("valid_to") obligations String? beyondTerm Boolean @default(false) @map("beyond_term") extensionStatus String? @map("extension_status") createdAt DateTime @default(now()) @map("created_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("ndas") } model Subcontractor { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") name String flowdownObligation Boolean @default(false) @map("flowdown_obligation") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("subcontractors") } // Verantwortung je IT-Service (6.1.3) — RACI über den ISA-Control-Katalog model ServiceControlResponsibility { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @map("asset_id") // Asset(type IT_SERVICE) controlRef String @map("control_ref") title String? applicable Boolean @default(true) responsibility RaciResponsibility @default(SHARED) evidenceRef String? @map("evidence_ref") createdAt DateTime @default(now()) @map("created_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("service_control_responsibilities") } // Reifegrad je Subjekt — Fragebogen-Selbstauskunft, durch Nachweise verifiziert, ISB-Freigabe model MaturityAssessment { id String @id @default(cuid()) tenantId String @map("tenant_id") assetId String @unique @map("asset_id") selfValue Float? @map("self_value") verifiedValue Float? @map("verified_value") computedValue Float? @map("computed_value") isbValue Float? @map("isb_value") isbJustification String? @map("isb_justification") approvedBy String? @map("approved_by") approvedAt DateTime? @map("approved_at") updatedAt DateTime @updatedAt @map("updated_at") asset Asset @relation(fields: [assetId], references: [id], onDelete: Cascade) @@index([tenantId]) @@map("maturity_assessments") } model AuditLog { id String @id @default(cuid()) // Nullable: Plattform-Ereignisse (scope=platform, z. B. Superadmin-Login) haben keinen Mandantenbezug. tenantId String? @map("tenant_id") scope String @default("tenant") // tenant | platform actorId String? @map("actor_id") action String // create | update | delete | login | denied | export | … entity String // z. B. asset, risk, user entityId String? @map("entity_id") before Json? after Json? createdAt DateTime @default(now()) @map("created_at") tenant Tenant? @relation(fields: [tenantId], references: [id]) @@index([tenantId, entity, entityId]) @@index([tenantId, createdAt]) @@map("audit_logs") } // Generisches Aufgaben-/Freigabe-Modell (erweiterbar). Erster Typ: policy_approval // (Richtlinien-Freigabe an eine konkrete Person). Kommentare/Statuswechsel historisiert. model Task { id String @id @default(cuid()) tenantId String @map("tenant_id") type String // policy_approval | … title String status String @default("OPEN") // OPEN | DONE | REJECTED | CANCELLED entityType String? @map("entity_type") // z. B. policy_document entityId String? @map("entity_id") entityRef String? @map("entity_ref") // Deep-Link-Referenz, z. B. Policy-Code assigneeId String? @map("assignee_id") // wer bearbeiten/freigeben soll createdById String? @map("created_by_id") dueDate DateTime? @map("due_date") resolvedById String? @map("resolved_by_id") resolvedAt DateTime? @map("resolved_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") comments TaskComment[] @@index([tenantId, status]) @@index([tenantId, assigneeId, status]) @@map("tasks") } model TaskComment { id String @id @default(cuid()) tenantId String @map("tenant_id") taskId String @map("task_id") authorId String? @map("author_id") kind String @default("comment") // comment | approve | reject | submit body String createdAt DateTime @default(now()) @map("created_at") task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) @@index([tenantId, taskId]) @@map("task_comments") } // ── Richtlinien & Verfahren (VDA-ISA 2027 Vorlagenpaket) ────────────────────── // Dokumente werden aus ihrer echten Markdown-Vorlage gerendert; Variablen, // Feature-Flags und Baseline-Parameter sind die eine Pflegestelle (§7). enum PolicyDocType { LEITLINIE // L00 RICHTLINIE // R01–R14 VERFAHREN // VA-01–VA-13 REGISTER // verwaltete Tabellen (Baseline, Nachweisregister, …) HANDBUCH // Anwender-Handbuch EIGENES // Word-Upload } enum PolicyStatus { ENTWURF IN_FREIGABE FREIGEGEBEN ARCHIVIERT } model PolicyDocument { id String @id @default(cuid()) tenantId String @map("tenant_id") code String // L00 | R01..R14 | VA-01..VA-13 | BASELINE | NACHWEIS | ISA_MAPPING type PolicyDocType title String version String @default("1.0") status PolicyStatus @default(FREIGEGEBEN) owner String? policyCode String? @map("policy_code") // bei VA: operationalisierte Richtlinie (R..) fulfills String[] @default([]) // bei VA: erfüllte Anforderungs-IDs rawMarkdown String @map("raw_markdown") // Vorlagen-Markdown mit Platzhaltern orderIdx Int @default(0) @map("order_idx") // Freigabe-Workflow (Vier-Augen): wer eingereicht/genehmigt hat submittedBy String? @map("submitted_by") approvedBy String? @map("approved_by") approvedAt DateTime? @map("approved_at") // Schutzbedarf-/TISAX-Level-Override je Richtlinie: null = global, "AL2" | "AL3" protectionOverride String? @map("protection_override") // Lifecycle (nicht-destruktiver Re-Import): gesetzt, wenn das Dokument nicht mehr // im Vorlagenpaket enthalten ist. null = aktiv. Orthogonal zum Freigabe-`status`. archivedAt DateTime? @map("archived_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([tenantId, code]) @@index([tenantId]) @@map("policy_documents") } model PolicyRequirement { id String @id @default(cuid()) tenantId String @map("tenant_id") reqId String @map("req_id") // z. B. 4.1.2-M1 policyCode String @map("policy_code") // R08 control String // 4.1.2 obligation String // MUSS | SOLL condition String? // FLAG_… oder null requirement String implementation String vaCodes String[] @default([]) @map("va_codes") // operationalisierende Verfahren nachweisLink String? @map("nachweis_link") // Lifecycle (nicht-destruktiver Re-Import): gesetzt, wenn die Anforderung nicht mehr // im Vorlagenpaket enthalten ist. null = aktiv. archivedAt DateTime? @map("archived_at") createdAt DateTime @default(now()) @map("created_at") @@unique([tenantId, reqId]) @@index([tenantId]) @@index([tenantId, control]) @@map("policy_requirements") } // Wizard-Variablen und Feature-Flags (variables.schema.json) — eine Pflegestelle model PolicyVariable { id String @id @default(cuid()) tenantId String @map("tenant_id") key String // UPPER_SNAKE, z. B. PW_MIN_LENGTH / FLAG_CLOUD_USED title String kind String // string | boolean groupName String? @map("group_name") value String // aktueller Wert (Default aus Schema); Flags als "true"/"false" required Boolean @default(false) orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([tenantId, key]) @@index([tenantId]) @@map("policy_variables") } model PolicyBaselineParam { id String @id @default(cuid()) tenantId String @map("tenant_id") blId String @map("bl_id") // BL-IAM-01 section String // "1. Identitäts- und Zugriffsmanagement" name String vorgabe String // Vorlagentext (kann {{VARIABLE}} enthalten) orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") @@unique([tenantId, blId]) @@index([tenantId]) @@map("policy_baseline_params") } model PolicyEvidence { id String @id @default(cuid()) tenantId String @map("tenant_id") nr Int policyCode String @map("policy_code") nachweis String quelle String verantwortlich String turnus String createdAt DateTime @default(now()) @map("created_at") @@index([tenantId]) @@map("policy_evidence") } // ── Verwaltete Register-Tabellen (§7b) ─────────────────────────────────────── // Verschlüsselungsmechanismen-Register (VA-07 Kryptokonzept & Schlüsselverwaltung) model CryptoEntry { id String @id @default(cuid()) tenantId String @map("tenant_id") dienst String schluessel String algorithmus String? ablaufdatum DateTime? @map("ablauf_datum") verantwortlich String speicherort String? baselineRef String? @map("baseline_ref") // z. B. BL-CRY-02 notes String? orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@index([tenantId]) @@map("crypto_entries") } // Klassifizierungs-Handhabungsmatrix (R02 / VA-08) — Schutzklassen × Aspekte model ClassificationClass { id String @id @default(cuid()) tenantId String @map("tenant_id") name String description String? orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") rules HandlingRule[] @@index([tenantId]) @@map("classification_classes") } model HandlingAspect { id String @id @default(cuid()) tenantId String @map("tenant_id") name String category String? orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") rules HandlingRule[] @@index([tenantId]) @@map("handling_aspects") } model HandlingRule { id String @id @default(cuid()) tenantId String @map("tenant_id") classId String @map("class_id") aspectId String @map("aspect_id") text String class ClassificationClass @relation(fields: [classId], references: [id], onDelete: Cascade) aspect HandlingAspect @relation(fields: [aspectId], references: [id], onDelete: Cascade) @@unique([classId, aspectId]) @@index([tenantId]) @@map("handling_rules") } // Risiko-Bewertungsmatrix (R03 / VA-09) — zentrale Pflegestelle (Defaults FB-80-04) model RiskMatrixClass { id String @id @default(cuid()) tenantId String @map("tenant_id") name String // Niedrig | Mittel | Hoch | Kritisch maxScore Int @map("max_score") // obere Schwelle des Risikowerts acceptance String // Akzeptanzinstanz tone String // ok | warn | orange | risk orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") @@index([tenantId]) @@map("risk_matrix_classes") } model RiskEwLevel { id String @id @default(cuid()) tenantId String @map("tenant_id") level Int label String definition String createdAt DateTime @default(now()) @map("created_at") @@index([tenantId]) @@map("risk_ew_levels") } model RiskDamageDimension { id String @id @default(cuid()) tenantId String @map("tenant_id") name String levels Json // { "1": "…", "2": "…", "3": "…", "4": "…" } orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") @@index([tenantId]) @@map("risk_damage_dimensions") } // Anwender-Handbuch (§9.7) — kuratierte Themen mit Deep-Links; Werte via Templating model HandbookTopic { id String @id @default(cuid()) tenantId String @map("tenant_id") category String title String bodyMd String @map("body_md") // Markdown mit {{VARIABLE}} (bleibt via Baseline synchron) sourceRefs String[] @default([]) @map("source_refs") // Deep-Links, z. B. R08#4.1.2 orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@index([tenantId]) @@map("handbook_topics") } // Generisches verwaltetes Register (GAP-Report WP3.0): benannte Register mit // Register-ID (REG-…), definierten Pflichtspalten, Review-Turnus und optionalen // Cross-Links an Lieferanten (R13/VA-10) und Asset-Inventar (R02/VA-08). model ManagedRegister { id String @id @default(cuid()) tenantId String @map("tenant_id") code String // REG-PROJECTS, REG-SW-WHITELIST, … title String description String? columns Json @default("[]") // [{ key, label }] Pflichtspalten reviewCycle String? @map("review_cycle") responsible String? supplierLink Boolean @default(false) @map("supplier_link") // Cross-Link Lieferant (Asset) assetLink Boolean @default(false) @map("asset_link") // Cross-Link Asset createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") rows RegisterRow[] @@unique([tenantId, code]) @@index([tenantId]) @@map("managed_registers") } model RegisterRow { id String @id @default(cuid()) tenantId String @map("tenant_id") registerId String @map("register_id") values Json @default("{}") // { spaltenKey: wert } supplierRef String? @map("supplier_ref") // Asset-ID (Lieferant) assetRef String? @map("asset_ref") // Asset-ID orderIdx Int @default(0) @map("order_idx") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") register ManagedRegister @relation(fields: [registerId], references: [id], onDelete: Cascade) @@index([tenantId, registerId]) @@map("register_rows") }