Files
certvia/prisma/schema.prisma
T
MartinandClaude Fable 5 392bb246cf Iteration 2: Assets & BIA als gemeinsames Modul
- App-Shell mit Seitennavigation (alle 12 Module, kommende ausgegraut),
  shadcn/ui-Setup (Base-UI-Variante) mit hellem Theme
- Asset-Inventar: filterbare Liste (Suche/Typ/Status), Anlegen/Bearbeiten/
  Löschen, Detailansicht mit Schutzbedarf (C/I/A 1–4), Abhängigkeiten
  (beide Richtungen, hinzufügen/entfernen), zugeordneten Prozessen mit
  Primär-/Sekundär-Rolle und Platzhalter für zugeordnete Risiken
- Prozesse & BIA: Liste mit Kritikalität/RTO/MTD, Prozess-Detail mit
  Asset-Zuordnung nach Rolle (primär/sekundär), BIA-Formular
  (RTO/RPO/MTD, Schadenshöhe je Schutzziel, Kritikalität nach Max-Prinzip)
- Server-Actions mit Zod-Validierung, requirePermission und Audit-Log
  für jede schreibende Aktion; Tenant-Guard um upsert-Injektion erweitert
- Prisma: Asset, AssetRelation, Process, ProcessAsset, BiaEntry
  inkl. RLS-Policies; Seed mit Beispiel-Assets, Prozessen und BIA
- Im Browser verifiziert: CRUD, Relationen, BIA-Speichern, Audit-Einträge

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 12:35:19 +02:00

256 lines
7.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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")
@@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
PERSON
DATA
}
enum AssetStatus {
ACTIVE
PLANNED
RETIRED
}
enum ProcessAssetRole {
PRIMARY
SECONDARY
}
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 14 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[]
@@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?
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?
@@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 14 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 14, 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")
}
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")
}