feat(16-01): tracer — select and import AD groups end-to-end
Task 1 checkpoint resolved: approve-both, granted 2026-08-06 by the
project owner (D-04 one-way schema extension: Group.internalName +
Group.ldapObjectGuid, both nullable, one versioned migration).
Adds the Phase 16 tracer slice through every layer:
- Prisma schema: Group.internalName, Group.ldapObjectGuid,
@@unique([tenantId, ldapObjectGuid]) (Prisma client regenerated;
the versioned migration itself is Task 3, separately blocking).
- LdapService: listGroups() now reads objectGUID via
explicitBufferAttributes and flags alreadyImported per tenant;
new importGroupsByDn() creates a Group per checked DN with
name/ldapDn/ldapObjectGuid, reject-with-report on name collision
(P2002 on name -> nameCollisions, P2002 on ldapObjectGuid ->
skipped), never aborts the batch on one DN's error; new static
escapeLdapFilterBuffer() for Plan 16-03's later existence sweep.
- DTO/controller: ImportGroupsDto, POST /ldap/groups/import
(ADMIN/SUPER_ADMIN), listGroups route now tenant-scoped.
- Frontend: new "AD-Gruppen importieren" section in /admin/ldap,
own discovery/import handlers with a visible error state
(Owner decision 2026-08-06 — no silent catch{} for these two
handlers), i18n keys in de.json/en.json.
- Tests: 8 new cases covering the full <behavior> list plus
listGroups sort order and alreadyImported.
Flagged assumption (RESEARCH.md A1/A2): objectGUID rename-stability
and the binary filter syntax are unverified against a real AD —
this plan only WRITES the GUID, Plan 16-03 reads it back live.
This commit is contained in:
@@ -135,19 +135,22 @@ enum MembershipSource {
|
||||
}
|
||||
|
||||
model Group {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
name String
|
||||
ldapDn String? // optionale AD-Bindung (D-05)
|
||||
isDefault Boolean @default(false) // D-13 — genau eine pro Mandant, DB-erzwungen (Hand-SQL)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
memberships GroupMembership[]
|
||||
grants ModuleGrant[]
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
name String
|
||||
ldapDn String? // optionale AD-Bindung (D-05)
|
||||
internalName String? // D-04: vom Sync nie berührt, überschreibt die Anzeige wenn gesetzt
|
||||
ldapObjectGuid String? // D-04/SC-3: hex-kodierter objectGUID, rename-stabiler Sync-Match-Key (ldapDn bleibt Anzeige-/Debug-Feld)
|
||||
isDefault Boolean @default(false) // D-13 — genau eine pro Mandant, DB-erzwungen (Hand-SQL)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
memberships GroupMembership[]
|
||||
grants ModuleGrant[]
|
||||
|
||||
@@unique([tenantId, name]) // Gruppennamen sind pro Mandant eindeutig
|
||||
@@unique([tenantId, ldapDn]) // NULL ist in Postgres je Zeile distinct — mehrere ungebundene Gruppen sind erlaubt
|
||||
@@unique([tenantId, ldapObjectGuid]) // gleiches NULL-ist-distinct-Muster wie @@unique([tenantId, ldapDn])
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user