docs(15-03): complete modul-freigaben-schreibseite plan
This commit is contained in:
@@ -65,7 +65,7 @@
|
||||
|
||||
- [x] **PERM-01**: Admin verwaltet Gruppen pro Mandant (anlegen, umbenennen, löschen) und weist Benutzer manuell zu oder entfernt sie. Beim Löschen einer belegten Gruppe warnt ein Dialog mit Mitglieder- und Freigabenanzahl, bevor Mitgliedschaften und Freigaben mitgelöscht werden.
|
||||
- [ ] **PERM-02**: Eine Gruppe kann optional an einen AD-Gruppen-DN gebunden werden, ausgewählt aus der bestehenden LDAP-Gruppensuche. Der Benutzer-Sync liest `memberOf` mit und pflegt daraus die Mitgliedschaften; verlässt ein Benutzer die AD-Gruppe, fällt nur seine LDAP-Mitgliedschaft weg — manuell gesetzte bleiben bestehen.
|
||||
- [ ] **PERM-03**: Admin gibt ein mandantenweit aktives Modul gezielt für Gruppen und für einzelne Benutzer frei und entzieht Freigaben wieder. Gruppenfreigaben werden in einer Matrix Module × Gruppen gepflegt, Einzelfreigaben im Benutzer-Detail samt Anzeige der über Gruppen geerbten Rechte.
|
||||
- [x] **PERM-03**: Admin gibt ein mandantenweit aktives Modul gezielt für Gruppen und für einzelne Benutzer frei und entzieht Freigaben wieder. Gruppenfreigaben werden in einer Matrix Module × Gruppen gepflegt, Einzelfreigaben im Benutzer-Detail samt Anzeige der über Gruppen geerbten Rechte.
|
||||
- [x] **PERM-04**: Ohne Freigabe hat ein USER keinen Zugriff: das Modul fehlt in der Sidebar, die Modulseite antwortet mit einer 403-Seite samt Hinweis, und die Modul-API antwortet mit 403. Sidebar, Modulseite und API nutzen dieselbe Zugriffsauflösung.
|
||||
- [x] **PERM-05**: ADMIN und SUPER_ADMIN sehen und nutzen innerhalb ihres Mandanten alle aktiven Module ohne Freigabe.
|
||||
- [x] **PERM-06**: Die Migration überführt den Bestand ohne Zugriffsverlust: pro Mandant entsteht eine als Standardgruppe markierte Gruppe mit allen bestehenden Benutzern und Freigaben für alle zum Migrationszeitpunkt aktiven Module. Neue Benutzer — manuell angelegt wie per LDAP importiert — treten der markierten Standardgruppe automatisch bei.
|
||||
@@ -124,8 +124,8 @@
|
||||
|
||||
| PERM-01 | Phase 15 | Pending |
|
||||
| PERM-02 | Phase 15 | Pending |
|
||||
| PERM-03 | Phase 15 | Pending |
|
||||
| PERM-04 | Phase 15 | Pending |
|
||||
| PERM-03 | Phase 15 | Complete |
|
||||
| PERM-04 | Phase 15 | Complete |
|
||||
| PERM-05 | Phase 15 | Pending |
|
||||
| PERM-06 | Phase 15 | Pending |
|
||||
| PERM-07 | Phase 15 | Complete |
|
||||
|
||||
@@ -511,7 +511,7 @@ Plans:
|
||||
|
||||
**Neue Modelle**: `Group` (tenantId, name, ldapDn?), `GroupMembership` (userId, groupId, source MANUAL|LDAP), `ModuleGrant` (tenantId, moduleId, groupId? | userId?)
|
||||
|
||||
**Plans**: 3/8 plans executed
|
||||
**Plans**: 4/8 plans executed
|
||||
|
||||
Plans:
|
||||
**Wave 1**
|
||||
@@ -526,7 +526,7 @@ Plans:
|
||||
|
||||
**Wave 3** *(blocked on Wave 2 completion)*
|
||||
|
||||
- [ ] 15-03-PLAN.md — Freigabe-API für Gruppen und Benutzer plus Modulkatalog-Endpoint
|
||||
- [x] 15-03-PLAN.md — Freigabe-API für Gruppen und Benutzer plus Modulkatalog-Endpoint
|
||||
- [ ] 15-06-PLAN.md — Gruppenverwaltung im Admin-UI und alle i18n-Schlüssel der Phase
|
||||
|
||||
**Wave 4** *(blocked on Wave 3 completion)*
|
||||
@@ -559,4 +559,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
||||
| 12. Tender Notifications | 4/4 | In Progress| |
|
||||
| 13. Scraping Adapters & Cross-Source Deduplication | 6/6 | In Progress| |
|
||||
| 14. RSS, Email-Alert Ingestion & Module Rollout | 5/5 | In Progress| |
|
||||
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 3/8 | In Progress| |
|
||||
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 4/8 | In Progress| |
|
||||
|
||||
+11
-7
@@ -5,15 +5,15 @@ milestone_name: Ausschreibungs-Radar
|
||||
current_phase: 15
|
||||
current_phase_name: modul-berechtigungen-gruppen-user-grants
|
||||
status: executing
|
||||
stopped_at: Completed 15-05-PLAN.md
|
||||
last_updated: "2026-08-04T13:39:18.710Z"
|
||||
stopped_at: Completed 15-03-PLAN.md
|
||||
last_updated: "2026-08-04T16:43:23.536Z"
|
||||
last_activity: 2026-08-04
|
||||
last_activity_desc: Phase 15 execution started
|
||||
progress:
|
||||
total_phases: 15
|
||||
completed_phases: 13
|
||||
total_plans: 75
|
||||
completed_plans: 69
|
||||
completed_plans: 70
|
||||
---
|
||||
|
||||
# Project State
|
||||
@@ -28,11 +28,11 @@ See: .planning/PROJECT.md (updated 2026-07-17)
|
||||
## Current Position
|
||||
|
||||
Phase: 15 (modul-berechtigungen-gruppen-user-grants) — EXECUTING
|
||||
Plan: 4 of 8
|
||||
Plan: 5 of 8
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-08-04 — Phase 15 execution started
|
||||
|
||||
Progress: [█████████░] 92%
|
||||
Progress: [█████████░] 93%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -103,6 +103,7 @@ Progress: [█████████░] 92%
|
||||
| Phase 15 P01 | 24min | 3 tasks | 10 files |
|
||||
| Phase 15-modul-berechtigungen-gruppen-user-grants P02 | 11min | 2 tasks | 11 files |
|
||||
| Phase 15 P05 | 9min | 2 tasks | 5 files |
|
||||
| Phase 15 P03 | 32min | 3 tasks | 8 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -239,6 +240,9 @@ Recent decisions affecting current work:
|
||||
- [Phase ?]: [15-02]: UserService.create ist die einzige Codestelle für D-11/D-12 — LdapService erbt die Regel ohne eigene Kopie (ldap.service.ts unverändert)
|
||||
- [Phase ?]: [15-05]: WIDGET_MODULE_MAP bleibt am Ende dieser Phase bewusst leer (D-22) — kein neuer Widget-Typ, keine Schemaänderung, nur die Filtermechanik
|
||||
- [Phase ?]: [15-05]: vi.hoisted() für die je-Testfall mutierbare WIDGET_MODULE_MAP-Mock-Referenz — vi.mock wird an den Dateianfang gehoben, ein normaler top-level const wäre zur Factory-Ausführungszeit noch nicht initialisiert
|
||||
- [Phase ?]: [15-03]: assertTargetBelongsToTenant als eigenständige Cross-Tenant-Prüfung eines referenzierten Fremdobjekts vor jedem Grant-Insert (T-15-01), kein Vorbild im Bestandscode
|
||||
- [Phase ?]: [15-03]: Kein Import von ModuleRegistryModule in GroupsModule — ModuleGrantsService injiziert ausschließlich PrismaService
|
||||
- [Phase ?]: [15-03]: getCatalogFlags liefert Map nur für aktive Module, Controller mappt fehlenden Eintrag auf beide Flags false
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -280,7 +284,7 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-08-04T13:39:18.682Z
|
||||
Stopped at: Completed 15-05-PLAN.md
|
||||
Last session: 2026-08-04T16:43:23.505Z
|
||||
Stopped at: Completed 15-03-PLAN.md
|
||||
Resume file: None
|
||||
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
phase: 15-modul-berechtigungen-gruppen-user-grants
|
||||
plan: 03
|
||||
subsystem: auth
|
||||
tags: [nestjs, prisma, module-access, groups, grants, vitest]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 15-modul-berechtigungen-gruppen-user-grants
|
||||
plan: 01
|
||||
provides: Group/GroupMembership/ModuleGrant-Schema, ModuleAccessService.getAccessibleModuleIds (D-01) als Single Source of Truth
|
||||
- phase: 15-modul-berechtigungen-gruppen-user-grants
|
||||
plan: 02
|
||||
provides: GroupsModule/GroupsService (CRUD, Ownership-Check-Muster), an das dieser Plan Controller und Service anhängt
|
||||
provides:
|
||||
- ModuleGrantsService.grant/revoke — Schreibseite der Modul-Freigaben (PERM-03), mit assertTargetBelongsToTenant als Cross-Tenant-Grant-Injection-Schutz vor jedem Insert
|
||||
- ModuleGrantsService.getMatrix — Datenlieferung für die Freigabe-Matrix-Seite (D-15), konsumiert von Plan 15-06/15-07
|
||||
- ModuleGrantsService.getUserAccess — Datenlieferung für das Benutzer-Detail (D-16), zeigt geerbte Gruppen + Direkt-Grant
|
||||
- ModuleGrantsController — vier rollengeschützte /module-grants-Routen
|
||||
- ModuleAccessService.getCatalogFlags + GET /modules/catalog — beide Statusflags (isActiveForTenant, hasAccess) in einer Antwort für den Marketplace (D-08), konsumiert von Plan 15-08
|
||||
affects: [15-06, 15-07, 15-08]
|
||||
|
||||
actuals:
|
||||
tokens: 9438
|
||||
tasks: 3
|
||||
commits: 3
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "assertTargetBelongsToTenant als eigenständige Cross-Tenant-Prüfung eines REFERENZIERTEN Fremdobjekts (nicht nur direktes Eigentum wie DashboardService.removeWidget) — kein Vorbild im Bestandscode, neu etabliert für Grant-Ziele"
|
||||
- "P2002 als Erfolgspfad: create() wirft, catch fängt den Fehlercode ab und liefert per findFirst den bestehenden Datensatz zurück statt eines HTTP 500 — Fortführung des in GroupsService.create etablierten Verfahrens"
|
||||
- "getCatalogFlags liefert eine Map nur für aktive Module; ein fehlender Eintrag wird im Controller auf beide Flags false gemappt, statt die Map für jedes registrierte Modul vorzubefüllen"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- apps/api/src/groups/module-grants.service.ts
|
||||
- apps/api/src/groups/module-grants.service.spec.ts
|
||||
- apps/api/src/groups/module-grants.controller.ts
|
||||
- apps/api/src/groups/dto/create-module-grant.dto.ts
|
||||
modified:
|
||||
- apps/api/src/groups/groups.module.ts
|
||||
- apps/api/src/module-registry/module-access.service.ts
|
||||
- apps/api/src/module-registry/module-access.service.spec.ts
|
||||
- apps/api/src/module-registry/module-registry.controller.ts
|
||||
|
||||
key-decisions:
|
||||
- "Kein Import von ModuleRegistryModule in GroupsModule — ModuleGrantsService injiziert ausschließlich PrismaService, die im Plan-Action-Text als 'falls benötigt' formulierte Ergänzung war nicht nötig"
|
||||
- "grant prüft die aktive TenantModuleActivation ausschließlich über tenantModuleActivation.findUnique (kein zusätzlicher module.findUnique-Existenzcheck) — eine nicht existierende moduleId fällt bereits über die fehlende Aktivierung in dieselbe BadRequestException, ein zweiter Query wäre redundant"
|
||||
|
||||
requirements-completed: [PERM-03, PERM-04]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "ModuleGrantsService.grant/revoke mit assertTargetBelongsToTenant vor jedem Insert (T-15-01), Entweder-oder-Regel (D-04), aktive TenantModuleActivation als Voraussetzung (D-02), P2002 als Erfolg"
|
||||
requirement: "PERM-03"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/groups/module-grants.service.spec.ts (20 Tests, deckt jeden <behavior>-Fall inkl. adjacency/empty/ordering/idempotency/concurrency)"
|
||||
status: pass
|
||||
- kind: e2e
|
||||
ref: "curl gegen laufende lokale API: POST /module-grants mit groupId eines fremden Test-Mandanten -> 404, Datensatz unangetastet; Gruppen-Grant gesetzt -> GET /modules/tender-radar als Gruppenmitglied 200; Grant entzogen -> derselbe Aufruf sofort 403 (D-09, ohne Zwischenschritt)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "getMatrix (D-15) und getUserAccess (D-16) liefern Freigabe-Matrix- und Benutzer-Detail-Daten in je einer Antwort, sortiert und mandantengescoped"
|
||||
requirement: "PERM-03"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/groups/module-grants.service.spec.ts — getMatrix/getUserAccess-Testgruppen"
|
||||
status: pass
|
||||
- kind: e2e
|
||||
ref: "curl gegen laufende lokale API: GET /module-grants/matrix als ADMIN -> 200 mit modules/groups/grants, als USER -> 403; GET /module-grants/users/:userId zeigt viaGroups=['Alle Benutzer'] + direct:false für den Testbenutzer"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "ModuleAccessService.getCatalogFlags + GET /modules/catalog liefert isActiveForTenant und hasAccess in einer Antwort (D-08), ADMIN/SUPER_ADMIN sehen hasAccess immer wahr für aktive Module (D-03)"
|
||||
requirement: "PERM-04"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/module-registry/module-access.service.spec.ts (4 neue getCatalogFlags-Tests)"
|
||||
status: pass
|
||||
- kind: e2e
|
||||
ref: "curl gegen laufende lokale API: GET /modules/catalog als USER ohne Gruppen-Mitgliedschaft zeigt tender-radar mit isActiveForTenant:true, hasAccess:false; derselbe Aufruf als ADMIN zeigt beide Flags true"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: 32min
|
||||
completed: 2026-08-04
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 15 Plan 03: Modul-Freigaben — Schreibseite, Matrix, Benutzer-Detail, Katalog Summary
|
||||
|
||||
**`ModuleGrantsService`/`ModuleGrantsController` für Gruppen- und Direkt-Grants mit einer eigens etablierten Cross-Tenant-Gegenprüfung vor jedem Insert (`assertTargetBelongsToTenant`, T-15-01), die Datenlieferung für Freigabe-Matrix (D-15) und Benutzer-Detail (D-16), und `GET /modules/catalog`, das dem Marketplace beide Statusflags (`isActiveForTenant`, `hasAccess`) in einer Antwort liefert (D-08) — alles end-to-end gegen die laufende lokale API bewiesen, inklusive des sofortigen Entzugs ohne Zwischenschritt (D-09).**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 32 min
|
||||
- **Started:** 2026-08-04T16:28:00Z
|
||||
- **Completed:** 2026-08-04T18:41:00Z (inkl. manueller E2E-Verifikation)
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 8
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `ModuleGrantsService` mit `grant`, `revoke`, `getMatrix`, `getUserAccess` und der privaten `assertTargetBelongsToTenant` — der zentrale Schutz gegen mandantenübergreifende Freigaben (T-15-01). Für dieses Muster gab es kein Vorbild im Bestandscode: bisherige Ownership-Checks (`DashboardService.removeWidget`, `GroupsService.findOwned`) prüfen nur direktes Eigentum, nicht eine zweite Mandantengrenze über eine referenzierte Relation
|
||||
- `grant` prüft in fester Reihenfolge: Entweder-oder von `groupId`/`userId` (D-04, `BadRequestException` mit Klartext statt rohem Postgres-Constraint-Namen), dann die Mandanten-Gegenprüfung, dann die aktive `TenantModuleActivation` (D-02), dann `create`. Ein `P2002` aus dem partiellen Unique-Index (zwei parallele Klicks auf dieselbe Matrix-Zelle) wird abgefangen und liefert per `findFirst` den bestehenden Datensatz zurück — kein HTTP 500 bei Doppelklick oder echter Nebenläufigkeit
|
||||
- `revoke` nutzt `deleteMany` mit `tenantId` im `where` als IDOR-Schutz (T-15-02) — ein Ziel eines fremden Mandanten trifft null Zeilen, kein vorheriger Lookup nötig
|
||||
- `getMatrix(tenantId)` liefert `{ modules, groups, grants }` in einer Antwort: aktive Module nach category+name, Gruppen nach name, Gruppen-Grants als Paare aus moduleId/groupId — deterministisch über wiederholte Aufrufe
|
||||
- `getUserAccess(tenantId, userId)` liefert je aktivem Modul die Namen der Gruppen, über die der Benutzer erbt, und ein Direkt-Grant-Kennzeichen — Gruppen-Grant und Direkt-Grant auf dasselbe Modul erscheinen gleichzeitig, keiner verdrängt den anderen (Union statt Vorrang)
|
||||
- `ModuleGrantsController` bildet vier rollengeschützte Routen unter `/module-grants` ab (`GET matrix`, `GET users/:userId`, `POST`, `DELETE`); `matrix` ist vor `users/:userId` deklariert (Beschattungsfehler-Vermeidung, Projekt hatte diesen Fehler schon einmal)
|
||||
- `ModuleAccessService.getCatalogFlags(tenantId, userId, role)` und `ModuleRegistryController.findCatalog` (`GET /modules/catalog`) liefern je registriertem Modul beide Statusflags in einer Antwort (D-08) — für ADMIN/SUPER_ADMIN ist `hasAccess` bei jedem aktiven Modul wahr (D-03), weil `getAccessibleModuleIds` den Rollen-Kurzschluss anwendet
|
||||
- Jede erfolgreiche `grant`-/`revoke`-Operation schreibt eine Logzeile über `this.logger` (D-23) — keine Audit-Tabelle, keine Ansicht im Admin-UI
|
||||
- D-04 vollständig eingehalten: der Datensatz trägt kein Rechtestufen-Feld, und der Service bietet keine Methode, die eines setzen könnte
|
||||
- End-to-End-Nachweis gegen die laufende lokale API (`dist/main` gegen die DB-Container-IP, `DATABASE_URL`/`JWT_SECRET` wie in vorherigen Plänen dieser Phase): `GET /module-grants/matrix` als ADMIN → 200 mit `modules`/`groups`/`grants`, als USER → 403; `POST /module-grants` mit `groupId` eines eigens angelegten Test-Fremdmandanten → 404, kein Datensatz angelegt; `GET /modules/catalog` als USER (ohne Gruppen-Mitgliedschaft) zeigt `tender-radar` mit `isActiveForTenant:true, hasAccess:false`, derselbe Aufruf als ADMIN zeigt beide Flags `true`; Testbenutzer der Gruppe „Alle Benutzer“ hinzugefügt → `GET /modules/tender-radar` 200; Grant entzogen → derselbe Aufruf sofort 403 ohne Zwischenschritt (D-09); `GET /module-grants/users/:userId` zeigt `viaGroups:['Alle Benutzer'], direct:false`
|
||||
|
||||
## Task Commits
|
||||
|
||||
Jeder Task wurde atomar committet:
|
||||
|
||||
1. **Task 1: ModuleGrantsService — Freigaben setzen und entziehen mit Mandanten-Gegenprüfung** - `5e256db` (feat)
|
||||
2. **Task 2: ModuleGrantsController und Einbindung in GroupsModule** - `072fb7f` (feat)
|
||||
3. **Task 3: GET /modules/catalog — beide Statusflags in einer Antwort für den Marketplace** - `1c32543` (feat)
|
||||
|
||||
**Plan metadata:** siehe Commit dieser SUMMARY.md (docs: complete plan)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `apps/api/src/groups/module-grants.service.ts` - `ModuleGrantsService` (neu): grant/revoke/getMatrix/getUserAccess/assertTargetBelongsToTenant
|
||||
- `apps/api/src/groups/module-grants.service.spec.ts` - 20 Tests, hand-rolled In-Memory-Prisma-Fake (Projekt-Konvention)
|
||||
- `apps/api/src/groups/dto/create-module-grant.dto.ts` - `CreateModuleGrantDto` (moduleId required, groupId/userId optional)
|
||||
- `apps/api/src/groups/module-grants.controller.ts` - `ModuleGrantsController` (neu), vier rollengeschützte `/module-grants`-Routen
|
||||
- `apps/api/src/groups/groups.module.ts` - `ModuleGrantsController`/`ModuleGrantsService` in controllers/providers/exports ergänzt
|
||||
- `apps/api/src/module-registry/module-access.service.ts` - `getCatalogFlags` ergänzt
|
||||
- `apps/api/src/module-registry/module-access.service.spec.ts` - 4 neue `getCatalogFlags`-Tests
|
||||
- `apps/api/src/module-registry/module-registry.controller.ts` - `findCatalog`-Handler (`GET /modules/catalog`) ergänzt
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Kein Import von `ModuleRegistryModule` in `GroupsModule` — der Plan-Action-Text formulierte dies als „falls der Service dessen Registry-Methoden benötigt“; `ModuleGrantsService` injiziert ausschließlich `PrismaService`, der Import wäre unbenutzte Kopplung gewesen
|
||||
- `grant` prüft die Modul-Aktivierung ausschließlich über `tenantModuleActivation.findUnique`, ohne separaten `module.findUnique`-Existenzcheck — eine nicht existierende `moduleId` landet bereits über die fehlende Aktivierung in derselben `BadRequestException`, ein zweiter Query wäre redundant gewesen und ist im `<behavior>`-Block auch nicht gefordert
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
**1. [Sonstiges — Akzeptanzkriterium-Diskrepanz, keine Rule 1-4] DTO-Grep-Kriterium zählt 2 statt 3**
|
||||
|
||||
- **Found during:** Task 1, beim Verifizieren der Akzeptanzkriterien nach dem Schreiben von `create-module-grant.dto.ts`
|
||||
- **Issue:** Das Akzeptanzkriterium `grep -cE '(moduleId|groupId|userId)[?]?: string' ... gibt 3 aus` erwartet implizit ein Pflichtfeld ohne Definite-Assignment-Assertion (`moduleId: string`). Das Projekt erzwingt aber `strict: true` (inkl. `strictPropertyInitialization`) in `tsconfig.base.json`, und jede bestehende DTO im Projekt (`CreateGroupDto`, `AddGroupMembersDto`, `CreateWidgetDto` u. a.) schreibt Pflichtfelder deshalb als `feld!: string`. Mit `moduleId!: string;` liefert der Grep nur 2 Treffer (`groupId?:`, `userId?:`), weil das `!` zwischen Feldname und `:` steht und die Regex `[?]?` nur ein optionales `?` erlaubt
|
||||
- **Warum keine Codeänderung:** `moduleId: string;` ohne `!` bricht `tsc --noEmit` mit `TS2564: Property 'moduleId' has no initializer` — das zweite, härtere Akzeptanzkriterium desselben Tasks. Die beiden Kriterien stehen im Widerspruch; die Projekt-Konvention (`!` für Pflichtfelder) und ein sauberer `type-check` wiegen schwerer als der wörtliche Grep-Zähler eines einzelnen Akzeptanzkriteriums
|
||||
- **Verifiziert stattdessen:** `pnpm --filter @tessera/api run type-check` läuft fehlerfrei, alle 20 Service-Tests inkl. der Entweder-oder-/XOR-Fälle sind grün, und `grep -cE '^\s+[a-zA-Z]+[?]?:' ...` zeigt weiterhin genau die drei erwarteten Feldnamen (kein viertes Feld)
|
||||
- **Files modified:** `apps/api/src/groups/dto/create-module-grant.dto.ts`
|
||||
- **Commit:** `5e256db`
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- Für den End-to-End-Nachweis lief zunächst ein veralteter `dist/main`-Prozess aus einer vorherigen Sitzung (Build-Stand von 15-05, 15:36 Uhr) auf Port 3001 — beendet, `pnpm --filter @tessera/api build` neu ausgeführt, mit denselben `DATABASE_URL`/`JWT_SECRET`-Werten wie die vorherige Sitzung (Container-IP `172.19.0.2`, `tessera:tessera_dev`) neu gestartet
|
||||
- Für den Fremdmandanten-Nachweis (T-15-01) wurde per `psql` ein zweiter, isolierter Test-Tenant samt Gruppe und Benutzer angelegt (analog zum in 15-02 etablierten Verfahren) — nach dem Nachweis vollständig wieder aus der DB entfernt
|
||||
- Für den D-09-Nachweis (sofortiger Entzug) wurde vorübergehend der einzige produktionsnahe Gruppen-Grant (aus dem D-06-Backfill: „Alle Benutzer“ → `tender-radar`) entzogen und danach exakt wiederhergestellt — die lokale DB enthält nach diesem Plan denselben `ModuleGrant`-Datensatz wie zuvor (neue `id`, identische `tenantId`/`moduleId`/`groupId`)
|
||||
- Zwei Testbenutzer (`test-admin-1503`, `test-user-1503`) und ein Test-Fremdmandant (`test-foreign-1503` samt Tenant/Gruppe) wurden ausschließlich für die Dauer der Verifikation angelegt und danach vollständig entfernt — `SELECT * FROM "User"`/`"Tenant"` zeigt nach dem Aufräumen wieder exakt den Ausgangsstand (1 Benutzer, 1 Mandant)
|
||||
- `isDefault` der Gruppe „Alle Benutzer“ war bereits vor diesem Plan `false` (siehe `local_db_note` im Auftrag) — dieser Plan hat daran nichts geändert und war davon nicht betroffen, da `grant`/`revoke`/`getMatrix`/`getUserAccess` nicht auf `isDefault` zugreifen
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - keine externe Service-Konfiguration nötig.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `ModuleGrantsService.getImpact`-Äquivalent für Freigaben existiert nicht in diesem Plan (nicht gefordert) — Plan 15-06/15-07 bauen die Admin-Oberflächen (Matrix-Seite, Benutzer-Detail) direkt auf `getMatrix`/`getUserAccess`/`grant`/`revoke` auf
|
||||
- `GET /modules/catalog` ist bereit für `MarketplaceCard` (Plan 15-08) — beide Flags kommen in einer Antwort, keine Karte kann kurzzeitig ohne Sperrhinweis klickbar erscheinen
|
||||
- Kein Bestandsverhalten gebrochen: volle API-Testsuite (493/493) grün, `type-check` fehlerfrei
|
||||
- Kein offener Blocker aus diesem Plan
|
||||
|
||||
---
|
||||
*Phase: 15-modul-berechtigungen-gruppen-user-grants*
|
||||
*Completed: 2026-08-04*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Alle in dieser SUMMARY genannten Dateien existieren auf der Platte, alle genannten Commit-Hashes (`5e256db`, `072fb7f`, `1c32543`) sind im Git-Log auffindbar.
|
||||
Reference in New Issue
Block a user