- user.service.ts: findById/update/deactivate/delete bekommen einen
Pflicht-Mandanten und laufen ueber forTenant(); create/update
uebersetzen die plattformweite Eindeutigkeitsverletzung (P2002) in eine
deutsche Konfliktmeldung ohne Halter/Mandant zu nennen; zwei neue
Methoden (findAllForPlatformAdmin, findByIdForPlatformAdmin) bilden die
Plattform-Administratorsicht als Schleife ueber alle Mandanten mit je
einem gebundenen Lesezugriff nach; findByUsername bleibt bewusst
ungebunden, Kopfkommentar richtiggestellt (Anmeldeweg laeuft seit
Etappe 1 ueber SECURITY-DEFINER-Funktionen, kein Aufrufer mehr)
- admin-seed.service.ts: Erstanlage-Pruefung bleibt ungebunden (mit
Begruendung), Erstanlage des Administrators bindet an den unmittelbar
zuvor angelegten Mandanten (Befund J-Korrektur); P2002 bei der Anlage
wird wie "Administrator existiert bereits" behandelt statt den Start
abzubrechen -- jeder andere Fehler bricht weiterhin ab
- user.controller.ts: die vier Aufrufstellen der geaenderten Signaturen
auf currentUser.tenantId umgestellt (Signatur-Minimalanpassung; die
Rollenlogik inkl. Plattform-Administratorsicht folgt in Aufgabe 3)
- Zwei-Klienten-Nachweis in beiden Testdateien (Muster
groups.service.spec.ts), Falsifizierungsnachweis fuer beide Bereiche
durchgefuehrt und zurueckgenommen (siehe SUMMARY)
- docs/mandantentrennung-zugriffsklassifikation.md: Zwischenstand fuer
(user.service.ts, user) und (admin-seed.service.ts, user) auf gemischt
korrigiert, neue Zeile (user.service.ts, tenant) ergaenzt -- volle
Klassenkorrektur mit Begruendung sowie die vier handgepflegten
Uebersichtstabellen folgen in Aufgabe 3
- .planning/WINDOWS.md: offener Eintrag fuer die plattformweite
Eindeutigkeit von username/email (Produktentscheidung fuer Etappe 3)
- 802 Tests gruen (13 neue in user.service.spec.ts, 4 neue in
admin-seed.service.spec.ts), Typpruefung sauber, Wegwerf-Werkzeug
meldet weiterhin alle 53 Pruefungen bestanden
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- TenantService.create ruft nach prisma.tenant.create ensureDefaultGroup
auf; Fehler werden protokolliert, nicht propagiert (Muster aus
UserService.create)
- tenant.module.ts importiert GroupsModule (keine Zirkularitaet, wie
UserModule bereits vormacht)
- AdminSeedService.onApplicationBootstrap besteht jetzt aus zwei
sequenziellen await-Schritten: seedAdmin() (bisheriger Rumpf, plus
ensureDefaultGroup nach dem Tenant-Upsert und VOR user.create), dann
ensureDefaultGroupsForAllTenants() als abschliessende Reparatur ueber
ALLE Mandanten — laeuft unabhaengig von seedAdmin()s fruehen
Rueckkehrpfaden (fehlende ENV / Admin existiert bereits) und ist je
Mandant sowie insgesamt try/catch-gekapselt, blockiert den API-Start nie
- Reparatur sitzt bewusst NICHT als eigener onApplicationBootstrap-Hook
in GroupsModule (Ordering-Falle aus tender-scheduler.service.ts)
- tenant.service.spec.ts, admin-seed.service.spec.ts (neu): Reihenfolge,
beide fruehen Rueckkehrpfade, Fehlerisolation je Mandant, Idempotenz
ueber zwei Bootstrap-Laeufe
Username lookups (login, admin seed, LDAP sync) compared case-sensitively
against a stored value with whatever casing it was created with, so
"Admin" and "admin" were treated as different accounts.
Normalizes at every write and read path: UserService.create/update
lowercase the username before persisting, findByUsername lowercases
the lookup input, AuthService.validateUser lowercases before the login
query, AdminSeedService lowercases the configured admin username, and
the LDAP sync loop lowercases the mapped sAMAccountName before using it
for lookup/create/update -- so AD casing differences don't create
duplicate accounts either.
Added a data migration to lowercase any existing mixed-case usernames.
It relies on the User.username unique constraint to fail loudly if two
existing accounts would collide after normalizing, rather than silently
merging them.
Verified locally: logged in with "ADMIN" (uppercase) against the
existing lowercase "admin" account after rebuilding the API image.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Create UserService with findByUsername (unscoped), create, update, deactivate, delete
- Create AdminSeedService that seeds Super-Admin from Docker ENV on bootstrap (D-05/D-07/D-13)
- Create TenantService with findAll, findById, create, update
- Create TenantMiddleware extracting tenantId from JWT with Super-Admin tenant switching (D-08/D-10)
- Wire PrismaModule, AuthModule, UserModule, TenantModule into AppModule
- Register JwtAuthGuard and RolesGuard as global APP_GUARD providers
- Apply TenantMiddleware to all routes via NestModule.configure
- Add @Public() decorator to HealthController for unauthenticated access