Projekt-Fundament: Spec, Docker-Stack, Prisma-Basis, Tenant-Guard
- docs/SPEC.md (v1.2: Assets & BIA als ein Modul, Risiko-Detailansicht mit Maßnahmen/betroffenen Assets, Risiko-Verknüpfungen in allen Detailansichten) + UI-Mockup als Referenz - Docker-Compose: app, worker, postgres (pgvector), redis, minio, mailhog - Prisma 7: Fundament-Schema (Tenant, User, RBAC, AuditLog), prisma.config.ts, pg-Treiberadapter - Tenant-Guard (src/server/db.ts): erzwungene Mandanten-Isolation für alle tenant-gebundenen Modelle - AGENTS.md/README mit Projektregeln (Isolation, RBAC, Audit-Log, i18n, Lizenz) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
// 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[]
|
||||
|
||||
@@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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user