Fundament des VDA-ISA-2027-Richtlinienmoduls (Spec §1–9):
- Datenmodell: PolicyDocument, PolicyRequirement, PolicyVariable (Variablen +
Feature-Flags), PolicyBaselineParam, PolicyEvidence — inkl. RLS + Tenant-Guard
- Seed-Importer (import-policies.ts): liest die echten .md-Dateien (15 Richtlinien
L00/R01–R14 + 11 Verfahren), mapping.json (120 Anforderungen/46 Controls),
variables.schema.json (53 Variablen/Flags), Technische-Sicherheits-Baseline
(31 BL-Parameter) und Nachweisregister; idempotent pro Mandant
- 6 im Vorlagenpaket beschädigte Variablen-Tokens (VA-08/09/10/12/13) repariert
(dokumentiert im README des Übergabepakets)
- Rendering-Engine (policy-render.ts, Handlebars + marked): verschachtelte
{{#if FLAG}}, {{VARIABLE}}, {{LINK:…}}-Deeplinks, Hidden-Anker + BL-Referenzen
im Lesemodus entfernt (Wert bleibt), zentral verwaltete Abschnitte unterdrückt,
wiederholtes „Umsetzung bei <Org>" reduziert (§7a); lenienter Fallback +
Residue-Check über alle Flag-Kombinationen (analog _verify.py)
- UI: Bibliothek mit Typ-Chips/KPIs, Lesemodus-Popup (einklappbare Info-Tabelle,
Control-Chips, Richtlinie↔Verfahren-Verlinkung), Coverage-Matrix
(Control → Richtlinie → MUSS/SOLL → Verfahren → Anforderungs-IDs)
- Nav-Punkt „Richtlinien" aktiviert; de/en-Übersetzungen
Verifiziert: Import 28 Dokumente/120 Anforderungen; Rendering rückstandsfrei
über alle Flag-Kombinationen; Bibliothek, Lesemodus (R08 nested flags), Coverage
im Browser.
Später (Phase 2+): Bearbeiten/Freigabe-Workflow mit Versionierung, verwaltete
Tabellen (Krypto-/Risiko-/Klassifizierungsregister), Anwender-Handbuch,
DOCX/PDF-Export, Word-Upload, KI-Wizard, zentrale Baseline-/Variablen-Einstellseite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
768 lines
24 KiB
Plaintext
768 lines
24 KiB
Plaintext
// 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
|
||
}
|
||
|
||
enum UserStatus {
|
||
ACTIVE
|
||
INVITED
|
||
LOCKED
|
||
DEACTIVATED
|
||
}
|
||
|
||
model Tenant {
|
||
id String @id @default(cuid())
|
||
name String
|
||
slug String @unique
|
||
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[]
|
||
|
||
@@map("tenants")
|
||
}
|
||
|
||
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)
|
||
mfaSecret String? @map("mfa_secret")
|
||
// 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")
|
||
}
|
||
|
||
// ── Assets & BIA (ein Modul, SPEC §4.1) ─────────────────────────────────────
|
||
|
||
enum AssetType {
|
||
INFORMATION
|
||
SYSTEM
|
||
APPLICATION
|
||
LOCATION
|
||
SUPPLIER
|
||
IT_SERVICE
|
||
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")
|
||
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")
|
||
}
|
||
|
||
// 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())
|
||
tenantId String @map("tenant_id")
|
||
actorId String? @map("actor_id")
|
||
action String // create | update | delete | login | 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")
|
||
}
|
||
|
||
// ── 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")
|
||
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")
|
||
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")
|
||
}
|