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:
2026-08-06 15:09:35 +02:00
parent c4a25511f3
commit 3523e43a13
8 changed files with 767 additions and 31 deletions
+15
View File
@@ -346,6 +346,21 @@
"emptyMeansAll": "No selection - all users under the base DN(s) are synced.",
"save": "Save filter"
},
"groupImport": {
"title": "Import AD groups",
"description": "Selected AD groups are created as Tessera groups and kept in sync with every subsequent run — name, membership and deletion in AD carry over automatically.",
"discover": "Search AD groups",
"searchPlaceholder": "Search AD groups...",
"noneFound": "No AD groups found.",
"noMatches": "No matches for this search.",
"alreadyImported": "Already imported",
"importSelected": "Import selected",
"resultSummary": "{imported} imported, {skipped} skipped{errors, plural, =0 {} other {, # errors}}",
"membershipHint": "Memberships are filled in automatically by the next sync run (manual or scheduled).",
"nameCollisionError": "Group \"{name}\" could not be imported: the name is already taken. Rename the existing local group or give it an internal name.",
"discoverError": "AD groups could not be retrieved. Check the LDAP connection and try again.",
"importError": "The import could not be executed. No group was created."
},
"userExclude": {
"title": "Exclude users (denylist)",
"description": "Individual usernames that are never imported - e.g. service accounts like administrator, krbtgt, guest or ldap$. Applies on top of the group/OU filter.",