Neither is a Phase 16 defect; both surfaced while testing it and would otherwise have been lost with the session. 1. Module activation has no licence check — a tenant admin can activate any catalogue module for their own tenant. The grants matrix is NOT the hole: it only distributes what is already active. Carries open product questions (who issues licences, what expiry does), so it is written up as a draft, not a decision. 2. The LDAP bind password is stored in clear text although an AES-256-GCM service already exists and is used for calendar, DKV and tender inbox credentials. Hashing is not an option here — the password must be replayable to bind against the directory — so encryption at rest is the fix. No open questions, just work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2.7 KiB
created, title, area, severity, trigger, files
| created | title | area | severity | trigger | files | |||
|---|---|---|---|---|---|---|---|---|
| 2026-08-11 | Jeder Mandanten-Admin kann sich jedes Modul selbst freischalten — Aktivierung ohne Lizenzpruefung | module-registry | major | vor dem Verkauf an externe Kunden — intern folgenlos, extern haelt die Modullizenzierung nicht |
|
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.