Rolleneditor: eigene Rollen neben den Standardrollen

Rechte sind jetzt die Grundgröße, eine Rolle ist ein benanntes Bündel davon.
Die elf Standardrollen stehen weiterhin im Code und sind unveränderlich –
Prüfvermerke und Bescheinigungen berufen sich auf sie. Daneben lassen sich
eigene Rollen anlegen.

Rolleneditor unter Verwaltung > Rollen & Rechte
- Rolle aus einer Standardrolle als Vorlage ableiten oder von Grund auf anlegen
- Rechte nach Modul gruppiert ankreuzen, mit Sammelschaltern je Gruppe
- Seite des Bauvertrags festlegen (Auftraggeber / Auftragnehmer / Zuarbeit) –
  bestimmt die Datensicht, unabhängig von der Rechteliste
- bearbeiten, stilllegen, löschen; die Rechtematrix zeigt eigene Rollen mit an

Grenzen
- Administrationsrechte sind nicht über eine Rolle vergebbar, sonst ließe sich
  über eine selbst gebaute Rolle die Benutzerverwaltung erreichen
- eine Rolle ohne jedes Recht wird abgelehnt
- eine vergebene Rolle lässt sich weder stilllegen noch löschen
- die Vier-Augen-Trennung bleibt unabhängig von der Rolle bestehen

Umbau der Rechteauswertung
- Rechte werden an genau einer Stelle ermittelt: rechteImProjekt() führt
  Standardrollen und eigene Rollen zu einer Rechtelage zusammen, die Seiten und
  Server Actions über rechte.darf() bzw. rechte.pruefe() befragen
- die Firmenschranke hängt nun an der Seite des Bauvertrags, die jede Rolle
  mitbringt, statt an einer festen Liste privilegierter Standardrollen
- ProjectMember trägt wahlweise eine Standardrolle oder eine eigene Rolle

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-08-11 14:13:38 +02:00
co-authored by Claude Opus 5
parent 8af6b384ea
commit 84eb93c59f
39 changed files with 1078 additions and 252 deletions
+38 -2
View File
@@ -40,6 +40,34 @@ enum Role {
LESER
}
/// Auf welcher Seite des Bauvertrags eine Rolle steht. Bestimmt, ob die
/// Trägerin oder der Träger das ganze Projekt oder nur die eigene Firma sieht.
enum RolleSeite {
AG // Bauherrenseite – sieht das gesamte Projekt
AN // Auftragnehmerseite – sieht ausschließlich die eigene Firma
NEUTRAL // Zuarbeit, Prüfung, Mitlesen – ebenfalls auf die eigene Firma beschränkt
}
/// Frei definierbare Rolle. Die elf Standardrollen stehen im Code
/// (src/lib/rbac.ts) und sind unveränderlich; hier entstehen zusätzliche
/// Rollen, wahlweise auf Grundlage einer Standardrolle als Vorlage.
model CustomRole {
id String @id @default(cuid())
schluessel String @unique // technischer Name, z.B. "fbue-oberleitung"
name String
beschreibung String?
seite RolleSeite @default(NEUTRAL)
vorlage Role? // Standardrolle, von der abgeleitet wurde
rechte String[] @default([]) // Schlüssel aus PERMISSIONS
aktiv Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdById String?
createdBy User? @relation("CustomRoleAutor", fields: [createdById], references: [id])
mitglieder ProjectMember[]
}
model Company {
id String @id @default(cuid())
name String
@@ -111,6 +139,7 @@ model User {
auditLogs AuditLog[]
slotResponsible ProjectDocumentSlot[] @relation("SlotResponsible")
tasks Task[] @relation("TaskAssignee")
eigeneRollen CustomRole[] @relation("CustomRoleAutor")
@@index([companyId])
}
@@ -140,13 +169,20 @@ model ProjectMember {
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role
// Entweder eine Standardrolle aus dem Code oder eine selbst angelegte.
// Genau eines von beiden ist gesetzt; darauf achtet die Anwendungsschicht.
role Role?
customRoleId String?
customRole CustomRole? @relation(fields: [customRoleId], references: [id])
// Fachbereiche, auf die sich diese Rolle beschränkt (leer = alle)
fachbereiche String[] @default([])
createdAt DateTime @default(now())
@@unique([projectId, userId, role])
@@unique([projectId, userId, role, customRoleId])
@@index([userId])
@@index([customRoleId])
}
// ═══════════════════════════════════════════════════════════════════