12 KiB
Phase 15: Modul-Berechtigungen: Gruppen & User-Grants - Context
Gathered: 2026-08-04 Status: Ready for planning
## Phase BoundaryModulzugriff wird zweistufig. Die bestehende Mandanten-Aktivierung (TenantModuleActivation) bleibt Voraussetzung; darüber entscheiden neue Freigaben pro Gruppe und pro einzelnem Benutzer, wer ein Modul sieht und dessen API nutzen darf. Dazu kommen Tessera-eigene Gruppen mit optionaler AD-Bindung, eine Admin-Oberfläche für Gruppen und Freigaben, und eine Migration, die den Bestand ohne Zugriffsverlust überführt.
Nicht in dieser Phase: Rechtestufen (read/write/admin) innerhalb eines Moduls, eigene Rollendefinitionen jenseits von SUPER_ADMIN/ADMIN/USER, Self-Service-Workflows zum Anfordern von Zugriff.
## Implementation DecisionsZugriffsmodell
- D-01: Zugriff auf ein Modul = Mandanten-Aktivierung UND (Rolle ADMIN/SUPER_ADMIN ODER Direkt-Grant für den Benutzer ODER Grant über eine seiner Gruppen). Eine einzige Auflösungsfunktion bedient sowohl
ModuleGuardals auchGET /modules/active, damit Sidebar, Modulseiten und API dieselbe Wahrheit sehen — Reversibility: one-way — die Auflösung wird zur Zugriffsgrundlage jedes Modul-Endpoints; ein späterer Wechsel des Modells müsste jeden Guard-Aufrufpfad und die bereits vergebenen Grants migrieren. - D-02: Default ist geschlossen: ohne Grant kein Zugriff. Die Mandanten-Aktivierung allein genügt nicht mehr — Reversibility: costly — ein Rückbau auf „offen, sofern nicht eingeschränkt" verlangt eine erneute Datenmigration und kehrt die Bedeutung aller bestehenden Grant-Datensätze um.
- D-03: ADMIN und SUPER_ADMIN umgehen Grants innerhalb ihres Mandanten und sehen alle dort aktiven Module. Verhindert Aussperren beim Konfigurieren.
- D-04: Nur Zugriff an/aus. Keine Rechtestufen innerhalb der Module — Module werten keine Grant-Attribute aus.
- D-05: Gruppen sind Tessera-eigene Objekte pro Mandant mit optionaler AD-Bindung über einen Gruppen-DN. Mitgliedschaften tragen ihre Herkunft (
MANUALoderLDAP), damit der Sync nur seine eigenen Einträge aufräumt — Reversibility: one-way — Group/GroupMembership/ModuleGrant sind neue Tabellen mit Fremdschlüsseln auf User, Tenant und Module; ein Umbau nach Vergabe echter Freigaben ist eine Datenmigration. - D-06: Die Migration legt pro Mandant eine Gruppe „Alle Benutzer" an, nimmt alle bestehenden Benutzer auf und erzeugt Grants für alle zum Migrationszeitpunkt aktiven Module. Kein bestehender Benutzer verliert beim Deploy Zugriff — Reversibility: one-way — die Migration schreibt Bestandsdaten; ein Rückbau erfordert ein eigenes Rückabwicklungsskript.
Sperr-Verhalten im Portal
- D-07: Ruft ein Benutzer die URL eines nicht freigegebenen Moduls direkt auf, erscheint eine 403-Seite mit dem Hinweis „Kein Zugriff auf dieses Modul — wende dich an deinen Administrator". Kein stiller Redirect, kein 404. Die Prüfung erfolgt serverseitig in der Modulseiten-Route — das Ausblenden in der Sidebar ist ausdrücklich keine Zugriffskontrolle.
- D-08: Der Marketplace zeigt auch nicht freigegebene Module weiter, gekennzeichnet mit einem Badge „Kein Zugriff"; öffnen lassen sie sich nicht. Der Katalog bleibt Schaufenster,
GET /modulesbleibt für authentifizierte Benutzer offen. - D-09: Ein Freigabe-Entzug wirkt in der API sofort, weil der Guard jede Anfrage prüft. Die Sidebar zieht beim nächsten Seitenaufruf nach. Kein Polling, keine Push-Verbindung.
Standard-Zuweisungen
- D-10: Aktiviert ein Admin ein Modul für seinen Mandanten, fragt ein Dialog, ob es sofort für die Standardgruppe freigegeben oder erst konfiguriert werden soll. Der bisherige Aktivieren-Toggle bekommt damit eine Rückfrage.
- D-11: Manuell angelegte Benutzer werden automatisch Mitglied der Standardgruppe.
- D-12: Per LDAP importierte Benutzer ebenso — eine Regel für beide Herkünfte. AD-gebundene Gruppen steuern die Feinverteilung zusätzlich.
- D-13: Welche Gruppe die Standardgruppe ist, entscheidet eine Markierung an der Gruppe, nicht ihr Name. Genau eine Gruppe pro Mandant trägt sie; die Migration setzt sie auf „Alle Benutzer". Der Admin kann die Gruppe umbenennen, die Markierung auf eine andere Gruppe umhängen oder ganz abschalten — ohne Markierung wandert kein neuer Benutzer automatisch irgendwohin. Die Gruppe selbst ist eine normale Gruppe: bearbeitbar, umbenennbar, löschbar.
Admin-Oberfläche
- D-14: Gruppenverwaltung als eigener Navigationspunkt
/admin/groups, sechster Eintrag neben LDAP, Benutzern, Modulen, Mandanten und SMTP. Dort: Gruppen anlegen, umbenennen, löschen, Mitglieder manuell zuweisen und entfernen. - D-15: Die Modulfreigaben für Gruppen werden auf einer eigenen Matrix-Seite gepflegt: Module × Gruppen mit Häkchen, gesamter Berechtigungsstand auf einen Blick.
- D-16: Freigaben für einzelne Benutzer werden im Benutzer-Detail in
/admin/usersgepflegt, zusammen mit der Anzeige dessen, was der Benutzer bereits über seine Gruppen erbt. - D-17: Beim Löschen einer Gruppe mit Mitgliedern oder Freigaben erscheint ein Warndialog mit beiden Anzahlen und dem Hinweis, dass betroffene Benutzer den Zugriff verlieren, sofern sie ihn nicht anderweitig haben. Nach Bestätigung werden Mitgliedschaften und Grants mitgelöscht.
AD-Bindung und Sync
- D-18: Die AD-Gruppe wird aus einer Liste gewählt, nicht als DN abgetippt. Die vorhandene Gruppen-/OU-Suche aus dem LDAP-Service (genutzt für den selektiven Import-Filter) wird dafür wiederverwendet.
- D-19: Verschwindet ein Benutzer aus der gebundenen AD-Gruppe, wird seine
LDAP-Mitgliedschaft entfernt. Manuell gesetzte Mitgliedschaften bleiben unberührt. Für gebundene Gruppen ist das AD die Wahrheit. - D-20: Mischbetrieb ist erlaubt: einer AD-gebundenen Gruppe dürfen zusätzlich Benutzer von Hand hinzugefügt werden — für Externe oder Testkonten ohne AD-Mitgliedschaft.
- D-21: Die Gruppen-Mitgliedschaften werden im bestehenden Benutzer-Sync mitgeführt: der Sync liest
memberOfje Benutzer zusätzlich aus und aktualisiert die Mitgliedschaften im selben Durchlauf, manuell per Button wie über das eingestellte Intervall. Kein separater Gruppen-Sync-Job und kein zweiter Button.
Widgets und Nachvollziehbarkeit
- D-22: Dashboard-Widgets bekommen einen optionalen Modul-Bezug, und Widgets eines für den Benutzer gesperrten Moduls verschwinden vom Dashboard. Heute trägt
WidgetInstance.widgetTypeeinen freien String ohne Modulverweis — die Zuordnung Widget-Typ → Modul wird in dieser Phase mitgebaut. Bewusste Erweiterung gegenüber den ursprünglichen Success Criteria der Roadmap — Reversibility: costly — betrifft Schema, Dashboard-Auslieferung und die Widget-Registrierung; ein Rückbau berührt alle drei. - D-23: Änderungen an Freigaben werden nur ins Server-Log geschrieben. Keine Audit-Tabelle in der Datenbank, keine Ansicht im Admin-UI.
Claude's Discretion
- Konkrete Schema-Details der neuen Modelle: Feldnamen, Indizes, Kaskadenregeln, und wie die Entweder-oder-Beziehung von
ModuleGrantauf Gruppe bzw. Benutzer erzwungen wird - Aufbau und Ort der Zugriffsauflösung im NestJS-Code sowie ob und wie ihr Ergebnis pro Request zwischengespeichert wird
- Technik der Migration: Prisma-Migration mit Datenschritt oder separates Seed-Skript
- Darstellung der Matrix bei vielen Modulen und Gruppen (Scrollverhalten, Gruppierung, Suche)
- Namensraum und Schnitt der i18n-Keys für die neuen Oberflächen
- Mechanik der Widget-Typ-→-Modul-Zuordnung: statische Registrierung im Code oder Feld in der Datenbank
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Projekt-Kontext
.planning/ROADMAP.md— Abschnitt „### Phase 15" mit Goal, sechs Success Criteria, den vier Grundsatzentscheidungen und den vorgesehenen neuen Modellen.planning/PROJECT.md— Core Value, Mandantenfähigkeit als Architekturkonstante.planning/REQUIREMENTS.md— Requirement-IDs (für Phase 15 noch nicht vergeben)
Vorherige Entscheidungen
.planning/phases/02-authentication-multi-tenancy/02-CONTEXT.md— D-08 bis D-13 (Tenant-Zuordnung pro Benutzer, RLS, drei Rollen), D-14 bis D-18 (LDAP-Sync-Verhalten, Feld-Mapping, LDAP-Konfig pro Mandant)
Bestehender Code, der das Verhalten definiert
apps/api/prisma/schema.prisma—Module(Z. 93),TenantModuleActivation(Z. 107),WidgetInstance(Z. 130),Role-Enum (Z. 21),User.ldapDnapps/api/src/module-registry/module-registry.service.ts—findActiveForTenant,isModuleActive: die beiden Stellen, an denen die zweite Stufe ansetztapps/api/src/module-registry/module.guard.ts—ModuleGuardprüft heute ausschließlich die Mandanten-Aktivierungapps/api/src/module-registry/module-registry.controller.ts—GET /modules,GET /modules/active, Aktivieren/Deaktivieren mitRolesGuardapps/api/src/ldap/ldap.service.ts— Gruppen-/OU-Suche für den Import-Filter (wiederverwendbar für D-18),memberOf-Behandlung als Suchfilter, Attributliste aus dem Feld-Mappingapps/web/src/components/layout/sidebar.tsx— lädtGET /modules/activebeim Mount und baut daraus die Modulnavigation
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
- Gruppen-/OU-Suche im LDAP-Service: liefert Gruppen samt DN und bedient bereits das Admin-UI für den selektiven Import — deckt die Auswahl-Oberfläche aus D-18 ohne neue LDAP-Logik ab
RolesGuard+@Roles(...)-Dekorator: das etablierte Muster für rollengeschützte Admin-Endpoints, direkt übertragbar auf die neuen Gruppen- und Grant-Routen- Admin-Bereich mit fünf bestehenden Unterseiten (
ldap,users,modules,tenants,smtp): Layout, Navigation und Formularmuster für/admin/groupssind vorhanden - i18n über
apps/web/src/messages/de.jsonunden.jsonmituseTranslations, wie zuletzt in Phase 14 durchgezogen
Established Patterns
- Zugriffskontrolle sitzt in NestJS-Guards, nicht in Controllern — die neue Auflösung gehört dorthin, wo
ModuleGuardheute schon greift tenantIdkommt aus dem JWT über die Tenant-Middleware, nie aus Benutzereingaben (Bedrohungsmodell T-03-04 aus Phase 3) — für Gruppen und Grants gilt dasselbe- Deaktivierungen sind Soft-Deletes mit erhaltenem Datensatz (
TenantModuleActivation.isActive), Benutzer werden bei LDAP-Löschung deaktiviert statt entfernt
Integration Points
ModuleGuard.canActivate— hier kommt die Benutzer-Dimension hinzuGET /modules/active— muss vom Mandanten- auf den Benutzer-Blick wechseln, sonst zeigt die Sidebar mehr als die API erlaubt- Modulseiten-Route im Frontend (
(portal)/modules/[category]) — braucht die serverseitige Prüfung aus D-07 - Marketplace-Katalog — braucht die Kennzeichnung aus D-08
- LDAP-Sync-Durchlauf — braucht
memberOfin der Attributliste und den Abgleich aus D-19/D-21 - Dashboard-Auslieferung der Widget-Instanzen — braucht den Modul-Bezug aus D-22
</code_context>
## Specific Ideas- Die 403-Seite soll benennen, was zu tun ist („wende dich an deinen Administrator"), statt nur zu sperren
- Im Benutzer-Detail soll sichtbar sein, was der Benutzer über Gruppen erbt und was ihm zusätzlich direkt gegeben wurde — sonst ist nicht erkennbar, warum jemand Zugriff hat
- Der Löschdialog einer Gruppe nennt konkrete Zahlen (Mitglieder, Freigaben), keine allgemeine Warnung
- Rechtestufen innerhalb eines Moduls (read / write / admin pro Grant) — eigene Phase, verlangt Anpassung jedes bestehenden Moduls
- Self-Service „Zugriff anfragen" aus dem Marketplace heraus, inklusive Genehmigungsfluss — neue Fähigkeit
- Audit-Trail für Berechtigungsänderungen in der Datenbank mit Ansicht im Admin-UI — bewusst gegen die Datenbankvariante entschieden (D-23), für ein verkauftes Produkt später plausibel nachzurüsten
- Mehrfach-Mandantenzugehörigkeit eines Benutzers — steht seit Phase 2 (D-09) aus und würde die Gruppenzuordnung erneut berühren
Phase: 15-modul-berechtigungen-gruppen-user-grants Context gathered: 2026-08-04