--- created: 2026-08-11 title: Jeder Mandanten-Admin kann sich jedes Modul selbst freischalten — Aktivierung ohne Lizenzpruefung area: module-registry severity: major trigger: ganz zum Schluss, erst wenn alle Module intern laufen — Entscheidung des Users 2026-08-11, bis dahin nicht vorlegen und nicht ansprechen files: - apps/api/src/module-registry/module-registry.service.ts:53 - apps/api/src/module-registry/module-registry.controller.ts:103 - apps/api/prisma/schema.prisma:97 --- ## Problem `POST /modules/:moduleId/activate` ist nur mit `@Roles(ADMIN, SUPER_ADMIN)` geschuetzt. Die `tenantId` stammt aus dem Login des Aufrufers, betrifft also immer den eigenen Mandanten — insoweit ist die Mandantentrennung intakt. `activateForTenant()` (`module-registry.service.ts:53`) prueft danach nur noch, ob das Modul im Katalog existiert, und legt dann die `TenantModuleActivation` an. Es gibt keine Pruefung, ob der Mandant dieses Modul erworben hat. Im Datenmodell existiert dafuer auch nichts: es gibt `Module` (Katalog) und `TenantModuleActivation` (an/aus pro Mandant), aber kein Objekt, das eine Lizenz, ein Kontingent oder eine Laufzeit abbildet. **"Aktiviert" und "lizenziert" sind heute dasselbe.** Ein Kunden-Administrator kann sich damit unter Admin → Module jedes Modul aus dem Katalog selbst freischalten. Nicht betroffen ist die Freigaben-Matrix — die verteilt nur innerhalb des bereits Aktivierten: `getMatrix()` (`module-grants.service.ts:174`) listet ausschliesslich Module mit `isActive = true`, und `grant()` (`:93`) weist ein nicht aktiviertes Modul mit einer Fehlermeldung ab. Die Luecke sitzt eine Ebene darueber, in der Aktivierung selbst. Aufgefallen am 2026-08-11, als der User beim Blick auf die Freigaben-Matrix fragte, ob Mandantenfaehigkeit und Modullizenzierung so ueberhaupt tragen. ## Solution Noch offen — als Entwurf, nicht als Entscheidung: Lizenzierung von Aktivierung trennen. Ein eigenes Objekt (etwa `TenantModuleLicense`) haelt fest, welche Module ein Mandant erworben hat, mit Laufzeit und ggf. Nutzerzahl. `activateForTenant()` prueft dagegen und weist ab, was nicht lizenziert ist — analog zu der Pruefung, die `grant()` bereits gegen die Aktivierung macht. Zu klaeren, bevor gebaut wird — das sind Produktfragen fuer den User: - Wer vergibt Lizenzen? Nur der Plattform-Betreiber (SUPER_ADMIN eines Betreiber-Mandanten), oder gibt es eine Selbstbedienung ueber den Marktplatz? - Was passiert mit einer ablaufenden Lizenz — sofort abschalten, Schonfrist, nur noch lesend? - Braucht es ueberhaupt Laufzeiten, oder reicht "gekauft / nicht gekauft"? - Wie kommt eine Lizenz technisch in eine Kundeninstallation, die beim Kunden im eigenen Netz laeuft? (Lizenzdatei, Schluessel, Online-Abgleich?) Solange Tessera nur intern laeuft, ist der Ist-Zustand folgenlos.