docs(16-03): complete Gruppen-Rekonziliation plan
This commit is contained in:
@@ -562,7 +562,7 @@ Plans:
|
||||
|
||||
**Offen für die Planung**: ob ein Admin eine automatisch angelegte Gruppe in Tessera umbenennen darf (würde beim nächsten Sync überschrieben), und wie sich die Auswahl-Oberfläche zur bestehenden AD-Gruppenauswahl in `/admin/groups` verhält.
|
||||
|
||||
**Plans**: 2/5 plans executed
|
||||
**Plans**: 3/5 plans executed
|
||||
|
||||
Plans:
|
||||
**Wave 1**
|
||||
@@ -575,7 +575,7 @@ Plans:
|
||||
|
||||
**Wave 3** *(blocked on Wave 2 completion)*
|
||||
|
||||
- [ ] 16-03-PLAN.md — Sync-Rekonziliation: Umbenennung, Loeschung, Alt-Bindungen; eingehaengt vor dem Mitgliedschafts-Abgleich (D-03, D-05, D-06)
|
||||
- [x] 16-03-PLAN.md — Sync-Rekonziliation: Umbenennung, Loeschung, Alt-Bindungen; eingehaengt vor dem Mitgliedschafts-Abgleich (D-03, D-05, D-06)
|
||||
- [ ] 16-04-PLAN.md — GroupFormModal ohne AD-Bindung, gesperrtes Namensfeld, interner Name, i18n-Bereinigung (D-03, D-04, D-07)
|
||||
|
||||
**Wave 4** *(blocked on Wave 3 completion)*
|
||||
|
||||
+11
-7
@@ -5,15 +5,15 @@ milestone_name: Ausschreibungs-Radar
|
||||
current_phase: 16
|
||||
current_phase_name: AD-Gruppen-Synchronisation
|
||||
status: executing
|
||||
stopped_at: Completed 16-02-PLAN.md
|
||||
last_updated: "2026-08-06T14:03:42.541Z"
|
||||
stopped_at: Completed 16-03-PLAN.md
|
||||
last_updated: "2026-08-06T14:24:13.324Z"
|
||||
last_activity: 2026-08-06
|
||||
last_activity_desc: Phase 16 execution started
|
||||
progress:
|
||||
total_phases: 16
|
||||
completed_phases: 13
|
||||
total_plans: 80
|
||||
completed_plans: 75
|
||||
completed_plans: 76
|
||||
---
|
||||
|
||||
# Project State
|
||||
@@ -28,11 +28,11 @@ See: .planning/PROJECT.md (updated 2026-07-17)
|
||||
## Current Position
|
||||
|
||||
Phase: 16 (AD-Gruppen-Synchronisation) — EXECUTING
|
||||
Plan: 3 of 5
|
||||
Plan: 4 of 5
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-08-06 — Phase 16 execution started
|
||||
|
||||
Progress: [█████████░] 94%
|
||||
Progress: [██████████] 95%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -109,6 +109,7 @@ Progress: [█████████░] 94%
|
||||
| Phase 15 P08 | 30min | 2 tasks | 12 files |
|
||||
| Phase 16 P01 | 34min | 3 tasks | 10 files |
|
||||
| Phase 16-ad-gruppen-synchronisation P02 | 5min | 3 tasks | 5 files |
|
||||
| Phase 16-ad-gruppen-synchronisation P03 | 12min | 2 tasks | 3 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -262,6 +263,9 @@ Recent decisions affecting current work:
|
||||
- [Phase ?]: [16-02]: reassignDefaultBeforeDelete() nutzt bewusst nicht findOwned() — eigenes still-false-Muster fuer Batch-Sync-Laeufe (D-06), kein NotFoundException-Abbruch
|
||||
- [Phase ?]: [16-02]: Namenssperre fuer importierte Gruppen (D-03) ist eine Backend-Invariante in GroupsService.update() (BadRequestException), nicht nur ein UI-Disable
|
||||
- [Phase ?]: [16-02]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] — dieser Plan liefert nur den Backend-Teil, das Requirement schliesst erst mit Plan 16-05
|
||||
- [Phase ?]: [16-03]: syncBoundGroupsForTenant() als eigene, in Task 1 noch unverdrahtete Methode gebaut; Task 2 liefert ausschliesslich die Verdrahtung als Schritt 5a vor 5b samt Call-Order-Test — Reihenfolge ist die zentrale Korrektheitsbedingung der Phase
|
||||
- [Phase ?]: [16-03]: A1/A2-Live-Pruefung gegen ViCoTest nicht durchfuehrbar (kein erreichbares AD in dieser Sandbox) — als WINDOWS.md #4 (unrun-verify) festgehalten, negatives Ergebnis ist Stopp-Grund fuer die D-05-Loeschsemantik
|
||||
- [Phase ?]: [16-03]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] — schliesst erst mit Plan 16-05
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -306,7 +310,7 @@ Items acknowledged and carried forward from previous milestone close:
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-08-06T14:03:42.506Z
|
||||
Stopped at: Completed 16-02-PLAN.md
|
||||
Last session: 2026-08-06T14:24:13.279Z
|
||||
Stopped at: Completed 16-03-PLAN.md
|
||||
Resume file: None
|
||||
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
||||
|
||||
+16
-3
@@ -1,10 +1,10 @@
|
||||
---
|
||||
schema_version: 1
|
||||
open_count: 3
|
||||
open_count: 4
|
||||
waived_count: 0
|
||||
fixed_count: 0
|
||||
total_count: 3
|
||||
last_updated: 2026-08-04T17:50:13.320Z
|
||||
total_count: 4
|
||||
last_updated: 2026-08-06T14:21:59.987Z
|
||||
---
|
||||
|
||||
# Broken Windows Ledger
|
||||
@@ -18,6 +18,7 @@ last_updated: 2026-08-04T17:50:13.320Z
|
||||
| 1 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-06-PLAN.md | | 15-06 <verification>: manueller Browser-Durchklick (Anlegen/Umbenennen/Standardmarkierung/AD-Bindung/Mitglieder/Loeschdialog + Fehlerpfade bei abgeschalteter API) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar | open | | 2026-08-04T17:05:42.026Z | |
|
||||
| 2 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-PLAN.md | | Manueller Browser-Durchklick aus dem Plan-Verification-Block (Matrix-Freigabe setzen/entziehen, Aktivierungsdialog beide Wege, Direkt-Grant neben Gruppen-Grant, Fehlerfall bei gestoppter API, lange Namen) nicht ausgefuehrt -- kein Browser-Tool in dieser Session (15-07-SUMMARY.md D4) | open | | 2026-08-04T17:27:42.267Z | |
|
||||
| 3 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-08-PLAN.md | | Manueller Browser-Durchklick aus dem Plan-Verification-Block (USER ohne Freigabe: Sidebar/403-Seite/Marketplace-Badge+Toast/API-403 identisch; ADMIN: alle vier Ebenen zugaenglich) nicht ausgefuehrt - kein Browser-Tool in dieser Session verfuegbar. | open | | 2026-08-04T17:50:13.320Z | |
|
||||
| 4 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md | | 16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05). | open | | 2026-08-06T14:21:59.987Z | |
|
||||
|
||||
````json
|
||||
[
|
||||
@@ -56,6 +57,18 @@ last_updated: 2026-08-04T17:50:13.320Z
|
||||
"reason": "",
|
||||
"recorded_at": "2026-08-04T17:50:13.320Z",
|
||||
"resolved_at": null
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"kind": "unrun-verify",
|
||||
"phase": "16",
|
||||
"file": ".planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md",
|
||||
"line": null,
|
||||
"description": "16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05).",
|
||||
"status": "open",
|
||||
"reason": "",
|
||||
"recorded_at": "2026-08-06T14:21:59.987Z",
|
||||
"resolved_at": null
|
||||
}
|
||||
]
|
||||
````
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
---
|
||||
phase: 16-ad-gruppen-synchronisation
|
||||
plan: 03
|
||||
subsystem: auth
|
||||
tags: [nestjs, prisma, ldap, ldapts, groups, vitest, rls]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 16-ad-gruppen-synchronisation (Plan 16-01)
|
||||
provides: "Group.ldapObjectGuid (Migration angewendet), LdapService.escapeLdapFilterBuffer() (bislang ungenutzt)"
|
||||
- phase: 16-ad-gruppen-synchronisation (Plan 16-02)
|
||||
provides: "GroupsService.reassignDefaultBeforeDelete(tenantId, groupId), GroupsService.ensureDefaultGroup(tenantId), DEFAULT_GROUP_NAME"
|
||||
provides:
|
||||
- "LdapService.syncBoundGroupsForTenant(client, config, tenantId, result): Rename-Erkennung (SC-3), Verschwinden-Loeschung mit Default-Handoff (SC-4/D-05/D-06), Alt-Bindungs-GUID-Nachtrag (D-07), 32-Hex-Validierung vor jeder Filter-Interpolation (T-16-01)"
|
||||
- "LdapSyncResult erweitert um groupsAdopted, groupsRenamed, groupsDeleted, defaultMarkerMoved"
|
||||
- "syncUsersForTenant() Schritt 5a: syncBoundGroupsForTenant() laeuft nachweislich vor Schritt 5b (syncGroupMembershipsForTenant, D-21) — Kernkorrektheitsbedingung der Phase (RESEARCH.md Pitfall 1)"
|
||||
- "LdapService(prisma, userService, groupsService) — dritter Konstruktor-Parameter; LdapModule importiert GroupsModule"
|
||||
affects: [16-04-dialog-umbau, 16-05-anzeige-fallback-frontend]
|
||||
|
||||
# Actuals (#2632)
|
||||
actuals:
|
||||
tokens: 11091
|
||||
tasks: 2
|
||||
commits: 2
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Erster tatsaechlicher Aufrufer von escapeLdapFilterBuffer() (aus Plan 16-01) — der binaere Existenz-Sweep-Filter (objectGUID=...)"
|
||||
- "Reihenfolge-Kopplung zweier privater Sync-Schritte durch einen beobachtenden Test (Call-Order-Spy via vi.spyOn mit mockImplementation, das die Originalmethode aufruft) statt nur behaupteter Kommentar-Reihenfolge"
|
||||
- "32-Zeichen-Hex-Validierung als eigener Guard VOR jeder Buffer-Rueckwandlung — ein Wert, der die Regex nicht besteht, erzeugt eine Fehlerzeile statt einer Filter-Interpolation (T-16-01)"
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- apps/api/src/ldap/ldap.service.ts
|
||||
- apps/api/src/ldap/ldap.module.ts
|
||||
- apps/api/src/ldap/ldap.service.spec.ts
|
||||
|
||||
key-decisions:
|
||||
- "syncBoundGroupsForTenant() wird in Task 1 als eigenstaendige, noch nicht eingehaengte Methode gebaut und per direktem Cast ((service as any).syncBoundGroupsForTenant(...)) getestet — die Verdrahtung in syncUsersForTenant() ist bewusst Task 2 vorbehalten (Plan-Vorgabe), damit die Reihenfolge-Korrektheit als eigener, isoliert testbarer Schritt sichtbar bleibt."
|
||||
- "D-21-Testfixtures (Plan 15/vor Plan 16) hatten nie ein ldapObjectGuid-Feld gesetzt. Nach der Verdrahtung in Task 2 durchlaeuft jede dieser Gruppen jetzt zwingend Schritt 5a (Alt-Bindungs-Nachtrag). Statt jede der ueber zehn Test-Fixtures im D-21-Block einzeln um ein Feld zu erweitern, wurde der gemeinsame mockSearch so erweitert, dass er 5a's zwei Filterformen (Backfill-Probe, Existenz-Sweep) deterministisch als No-Op aufloest (DN-abgeleitete GUID, Identitaets-Treffer) — kleinerer, zentralerer Diff als 11 Einzelaenderungen."
|
||||
- "PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] (Pending) stehen — dieser Plan liefert Rekonziliation und Verdrahtung, das Requirement schliesst laut Plan-Vorgabe erst mit Plan 16-05."
|
||||
- "A1/A2-Live-Pruefung gegen ViCoTest nicht durchfuehrbar: kein erreichbares Active Directory in dieser Sandbox (Umgebungsvorgabe fuer diesen Lauf). Als offener Punkt im Broken-Windows-Ledger (WINDOWS.md #4) und unten dokumentiert, statt stillschweigend uebersprungen."
|
||||
|
||||
patterns-established:
|
||||
- "Reihenfolge-Kopplung zwischen zwei privaten Sync-Schritten wird durch einen beobachtenden Call-Order-Test abgesichert, nicht nur durch Kommentartext an der Aufrufstelle"
|
||||
|
||||
requirements-completed: [PERM-02] # Plan-Frontmatter-Vertrag; REQUIREMENTS.md-Checkbox bleibt bewusst [ ] (siehe Decisions) — schliesst erst mit Plan 16-05
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "syncBoundGroupsForTenant() erkennt Rename (cn/dn-Aenderung -> Group.name/ldapDn-Update, internalName bleibt unberuehrt) und Verschwinden (kein Treffer -> Loeschung) korrekt, ohne lokale Gruppen (ldapObjectGuid+ldapDn beide null) jemals abzufragen (SC-3/SC-4/SC-5, D-04)"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/ldap/ldap.service.spec.ts#LdapService.syncBoundGroupsForTenant — Rekonziliation gegen das Verzeichnis (SC-3/SC-4/SC-5, D-05/D-06) (14 it-Faelle)"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "grep -c internalName im Methodenkoerper (awk-Range) = 0"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Loesch-Zweig ruft reassignDefaultBeforeDelete() nachweislich VOR group.delete(); nach jeder Loeschung laeuft ensureDefaultGroup() einmal — kein Mandant bleibt ohne markierte Standardgruppe (D-06, RESEARCH.md Pitfall 5)"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/ldap/ldap.service.spec.ts — Faelle 'no hit, IS the default group...' und 'no hit, is the ONLY group of the tenant...'"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "awk-Range-Grep: reassignDefaultBeforeDelete-Zeile < group.delete-Zeile"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Alt-Bindungen (ldapDn gesetzt, ldapObjectGuid null) werden per Base-Scoped-Lookup einmalig nachgetragen (groupsAdopted) und im selben Lauf regulaer weiterverarbeitet; loest der DN nicht mehr auf, wird NICHT geloescht, nur eine Fehlerzeile erzeugt (D-07, T-16-11)"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/ldap/ldap.service.spec.ts — Faelle 'a legacy binding ... whose DN still resolves ...' und '... whose DN no longer resolves ...'"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "Der gespeicherte Hex-Wert wird vor jeder Filter-Interpolation auf exakt 32 [0-9a-f]-Zeichen validiert; ein ungueltiger Wert erzeugt eine Fehlerzeile statt eines Filters (T-16-01)"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/ldap/ldap.service.spec.ts#'an invalid stored ldapObjectGuid (not 32 [0-9a-f] chars) never reaches a filter'"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "syncBoundGroupsForTenant() laeuft in syncUsersForTenant() nachweislich VOR syncGroupMembershipsForTenant() (Schritt 5a vor 5b); ein im selben Lauf erkannter Rename fuehrt beobachtbar zu keinem memberOf-Filter mit der alten DN mehr (RESEARCH.md Pitfall 1)"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/api/src/ldap/ldap.service.spec.ts#LdapService.syncUsersForTenant — Schrittreihenfolge Gruppen vor Mitgliedschaften (3 it-Faelle: Call-Order-Spy, No-Op-Waechter, Regressions-Filter-Check)"
|
||||
status: pass
|
||||
- kind: other
|
||||
ref: "grep -n Aufrufzeilen: syncBoundGroupsForTenant-Zeile < syncGroupMembershipsForTenant-Zeile"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "RESEARCH.md-Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax vom Ziel-AD akzeptiert) read-only gegen ViCoTest verifiziert, bevor die Loeschsemantik (D-05) als produktionsreif gilt"
|
||||
requirement: "PERM-02"
|
||||
verification:
|
||||
- kind: other
|
||||
ref: "Kein erreichbares Active Directory in dieser Sandbox (Umgebungsvorgabe) — Live-Pruefung nicht durchgefuehrt"
|
||||
status: fail
|
||||
human_judgment: true
|
||||
rationale: "A1/A2 bleiben unverifizierte Annahmen aus RESEARCH.md. Als WINDOWS.md-Eintrag #4 (unrun-verify) festgehalten. Ein negatives Ergebnis bei A1 ODER A2 ist ein Stopp-Grund fuer die Loeschsemantik aus D-05 — vor produktivem Einsatz gegen ViCoTest (balios.ctl.local) nachzuholen."
|
||||
|
||||
duration: 12min
|
||||
completed: 2026-08-06
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 16 Plan 3: Gruppen-Rekonziliation gegen das Verzeichnis Summary
|
||||
|
||||
**syncBoundGroupsForTenant() unterscheidet AD-Umbenennung von AD-Verschwinden ueber den rename-stabilen ldapObjectGuid, laeuft nachweislich vor dem Mitgliedschafts-Abgleich, uebergibt die Standardmarkierung vor jeder Loeschung und traegt Alt-Bindungen aus Plan 15-06 nach — die A1/A2-Live-Pruefung gegen ein echtes Active Directory bleibt offen (kein AD in dieser Sandbox erreichbar).**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 12 min (Commit-Spanne 16:15:09 Uhr bis 16:21:10 Uhr; Lesen von PLAN.md, 16-01-/16-02-SUMMARY.md, PATTERNS.md, RESEARCH.md und der Zieldateien davor nicht mitgerechnet)
|
||||
- **Started:** 2026-08-06T14:15:09Z (erster Task-Commit)
|
||||
- **Completed:** 2026-08-06T14:21:10Z (zweiter Task-Commit)
|
||||
- **Tasks:** 2
|
||||
- **Files modified:** 3
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Neue private Methode `LdapService.syncBoundGroupsForTenant()`: fragt alle Gruppen mit gesetztem `ldapObjectGuid` ODER `ldapDn` ab (der D-07-Anschluss fuer Alt-Bindungen), tut fuer eine rein lokale Gruppe (beide Felder `null`) nichts und loest dafuer keine einzige LDAP-Suche aus
|
||||
- Rename-Erkennung (SC-3): ein AD-Treffer mit geaendertem `cn`/`dn` zieht `Group.name`/`Group.ldapDn` nach, `internalName` bleibt in jedem Codepfad der Methode unberuehrt (D-04); ein `P2002` aus einer Namenskollision wird abgefangen und als Fehlerzeile gemeldet, der Lauf geht weiter
|
||||
- Verschwinden-Loeschung (SC-4/D-05): kein Treffer im binaeren Existenz-Sweep loescht die Gruppe — aber immer erst NACH dem Standardmarkierungs-Handoff (`reassignDefaultBeforeDelete`, Plan 16-02), nachweislich in dieser Zeilenreihenfolge; nach jeder Loeschung laeuft `ensureDefaultGroup()` einmal, damit kein Mandant ohne Standardgruppe dasteht (D-06)
|
||||
- Alt-Bindungs-Nachtrag (D-07): eine Gruppe mit `ldapDn`, aber ohne `ldapObjectGuid`, bekommt den Identitaetsschluessel per Base-Scoped-Lookup einmalig nachgetragen (`groupsAdopted`) und wird im selben Lauf reguelaer weiterverarbeitet; loest der DN nicht mehr auf, wird **nicht** geloescht, nur eine Fehlerzeile erzeugt — ohne stabilen Schluessel waere eine Loeschung eine Vermutung
|
||||
- Der gespeicherte Hex-Wert wird vor jeder Filter-Interpolation auf exakt 32 `[0-9a-f]`-Zeichen validiert (T-16-01); ein ungueltiger Wert erzeugt eine Fehlerzeile statt einer Filter-Interpolation
|
||||
- Schritt 5a (`syncBoundGroupsForTenant`) laeuft in `syncUsersForTenant()` nachweislich VOR Schritt 5b (`syncGroupMembershipsForTenant`, D-21) — abgesichert durch einen beobachtenden Call-Order-Test (nicht nur durch Kommentartext), plus einen Regressionstest, der zeigt, dass ein im selben Lauf erkannter Rename zu keinem `memberOf`-Filter mit der alten DN mehr fuehrt
|
||||
- `LdapService`-Konstruktor nimmt neu `GroupsService` entgegen; `LdapModule` importiert `GroupsModule` (kein Zyklus, da `GroupsModule` weder `LdapModule` noch `UserModule` importiert)
|
||||
- 17 neue Testfaelle (14 fuer die neue Methode isoliert, 3 fuer die Verdrahtungsreihenfolge); vollstaendige API-Testsuite gruen: 40 Testdateien, 560 Tests
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Gruppen-Rekonziliation gegen das Verzeichnis (SC-3, SC-4, SC-5, D-05, D-06)** — `5222934` (feat, tdd)
|
||||
2. **Task 2: Einhaengen als Schritt 5a vor dem Mitgliedschafts-Abgleich (RESEARCH.md Pitfall 1)** — `68aca81` (feat, tdd)
|
||||
|
||||
**Plan metadata:** wird mit diesem Summary committet
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `apps/api/src/ldap/ldap.service.ts` — `LdapSyncResult` um `groupsAdopted`/`groupsRenamed`/`groupsDeleted`/`defaultMarkerMoved` erweitert; Konstruktor nimmt `GroupsService`; neue private Methode `syncBoundGroupsForTenant()`; Aufruf als Schritt 5a in `syncUsersForTenant()` vor Schritt 5b
|
||||
- `apps/api/src/ldap/ldap.module.ts` — `GroupsModule`-Import ergaenzt
|
||||
- `apps/api/src/ldap/ldap.service.spec.ts` — zwei neue `describe`-Bloecke (`syncBoundGroupsForTenant` mit 14 Faellen, `Schrittreihenfolge Gruppen vor Mitgliedschaften` mit 3 Faellen); alle acht `new LdapService(...)`-Konstruktionen um einen dritten Mock-Parameter ergaenzt; D-21-Block-Fixtures um eine 5a-No-Op-Aufloesung im gemeinsamen `mockSearch` erweitert, ohne jede der ueber zehn Einzel-Fixtures anzufassen
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- `syncBoundGroupsForTenant()` wurde in Task 1 bewusst noch NICHT in `syncUsersForTenant()` eingehaengt (Plan-Vorgabe) — Task 1 testet die Methode per direktem Cast auf die private Methode, Task 2 liefert ausschliesslich die Verdrahtung samt Reihenfolge-Test. Diese Trennung macht die zentrale Korrektheitsbedingung der Phase (Reihenfolge) als eigenen, isoliert nachvollziehbaren Schritt sichtbar.
|
||||
- Statt jede der ueber zehn D-21-Test-Fixtures (aus Plan 15, vor Plan 16 gebaut, nie mit `ldapObjectGuid` versehen) einzeln um ein Feld zu erweitern, wurde der gemeinsame `mockSearch` im D-21-Block um eine deterministische No-Op-Aufloesung fuer beide 5a-Filterformen erweitert (DN-abgeleitete GUID, Identitaets-Treffer bei unveraendertem `cn`/`dn`). Kleinerer, zentralerer Diff; die D-21-Tests bleiben inhaltlich unveraendert auf die Mitgliedschafts-Reconciliation fokussiert.
|
||||
- PERM-02 bleibt in `REQUIREMENTS.md` bewusst auf `[ ]` (Pending) — dieser Plan liefert Rekonziliation und Verdrahtung, das Requirement schliesst laut Plan-Vorgabe erst mit Plan 16-05.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Bestehender D-21-Test brach nach der additiven `LdapSyncResult`-Erweiterung (exaktes `toEqual`)**
|
||||
- **Found during:** Task 1
|
||||
- **Issue:** Der Test „creates nobody and deactivates nobody when the base DN is empty" verglich `result` per `toEqual` gegen ein Literal ohne die vier neuen Felder — nach der additiven Interface-Erweiterung schlug der Vergleich fehl, obwohl das Verhalten unveraendert korrekt war.
|
||||
- **Fix:** Die vier neuen Felder (`groupsAdopted: 0, groupsRenamed: 0, groupsDeleted: 0, defaultMarkerMoved: 0`) im erwarteten Literal ergaenzt.
|
||||
- **Files modified:** apps/api/src/ldap/ldap.service.spec.ts
|
||||
- **Verification:** `npx vitest run src/ldap/ldap.service.spec.ts` gruen
|
||||
- **Committed in:** 5222934
|
||||
|
||||
**2. [Rule 1 - Bug] Wiederverwendete GUID-Test-Fixture aus Plan 16-01 war tatsaechlich nur 31 Hex-Zeichen lang**
|
||||
- **Found during:** Task 1
|
||||
- **Issue:** Der bestehende `guidBuffer`-Fixture-String im "AD group import"-Block (`'0123456789abcdef0123456789abcde'`) ist bei genauer Zaehlung 31, nicht 32 Zeichen lang — `Buffer.from(..., 'hex')` schneidet das letzte ungerade Zeichen stumm ab und liefert 15 statt 16 Bytes zurueck. Das fiel dort nie auf, weil `importGroupsByDn()` die Laenge nie validiert; die neue 32-Hex-Validierung in `syncBoundGroupsForTenant()` (T-16-01) schlug mit demselben Wert sofort fehl.
|
||||
- **Fix:** Eigene, lokal im neuen `describe`-Block definierte, garantiert 32 Zeichen lange Fixture (`'0123456789abcdef'.repeat(2)`) statt den bestehenden (fehlerhaften) Fixture-String zu teilen. Der bestehende Fixture-String im 16-01-Block wurde NICHT angefasst (aussarhalb des Scopes dieses Plans, dortige Tests bleiben unveraendert gruen).
|
||||
- **Files modified:** apps/api/src/ldap/ldap.service.spec.ts
|
||||
- **Verification:** `npx vitest run src/ldap/ldap.service.spec.ts` gruen, neuer Kommentar dokumentiert den Unterschied explizit
|
||||
- **Committed in:** 5222934
|
||||
|
||||
**3. [Rule 1 - Bug] Verdrahtung in Task 2 brach vier bestehende D-21-Tests (Schritt 5a lief nun ungewollt gegen deren Fixtures)**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** Nach dem Einhaengen von Schritt 5a liefen die vorher isoliert getesteten D-21-Membership-Tests real durch `syncUsersForTenant()` — deren Gruppen-Fixtures (nur `ldapDn`, kein `ldapObjectGuid`, vor Plan 16 gebaut) loesten in jedem Lauf den Alt-Bindungs-Nachtragspfad aus und produzierten unerwuenschte Fehlerzeilen bzw. veraenderten `result.errors`.
|
||||
- **Fix:** Der gemeinsame `mockSearch` im D-21-`beforeEach` (und die eine Test-lokale Ueberschreibung im „records a search failure"-Test) um eine deterministische Aufloesung beider 5a-Filterformen erweitert (`resolve5aNoOp`-Helfer auf Describe-Ebene), die pro Fixture-Gruppe einen DN-abgeleiteten, immer gueltigen GUID annimmt und einen Identitaets-Treffer liefert — 5a wird dadurch fuer jede D-21-Fixture ein reiner No-Op, ohne die ueber zehn einzelnen `groups = [...]`-Literale anzufassen.
|
||||
- **Files modified:** apps/api/src/ldap/ldap.service.spec.ts
|
||||
- **Verification:** `npx vitest run src/ldap/ldap.service.spec.ts` — alle 55 Faelle gruen; `npx vitest run` — vollstaendige API-Suite (560 Tests) gruen
|
||||
- **Committed in:** 68aca81
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 3 auto-fixed (alle Rule 1, direkte Folgen der additiven Interface-Erweiterung bzw. der Verdrahtung — kein Scope Creep, kein Architekturwechsel)
|
||||
**Impact on plan:** Keine Auswirkung auf Funktionalitaet oder Scope der neuen Methode selbst — alle drei Korrekturen betrafen ausschliesslich bestehende Testinfrastruktur, die durch additive/verdrahtende Aenderungen aus diesem Plan beruehrt wurde.
|
||||
|
||||
## Outstanding Live Verification (A1/A2)
|
||||
|
||||
**RESEARCH.md fuehrt zwei Annahmen, die dieser Plan gegen ein echtes Active Directory pruefen sollte (Task 2 `<human-check>`), aber in dieser Sandbox nicht pruefen konnte — es ist kein Active-Directory-Server erreichbar (Umgebungsvorgabe fuer diesen Ausfuehrungslauf, siehe `<environment>` im Auftrag):**
|
||||
|
||||
1. **A1 — `objectGUID` uebersteht eine reine CN-Umbenennung unveraendert.** Unverifiziert. Die gesamte Rename-Erkennung (SC-3) haengt an dieser Annahme — ist sie falsch, wuerde jede echte AD-Umbenennung weiterhin wie ein Verschwinden+Neuanlage behandelt.
|
||||
2. **A2 — die byteweise `\XX`-Hex-Escaping-Syntax fuer den binaeren `(objectGUID=...)`-Filter wird vom Ziel-AD korrekt interpretiert.** Unverifiziert. Ist sie falsch, liefert der Existenz-Sweep fuer JEDE gebundene Gruppe null Treffer — SC-4 (Loeschung) wuerde dann faelschlich auf jede weiterhin existierende Gruppe angewendet.
|
||||
|
||||
**Beide Antworten stehen noch aus.** Sie sind als offener Eintrag im projektweiten Broken-Windows-Ledger festgehalten: `.planning/WINDOWS.md` Eintrag #4 (kind: `unrun-verify`, phase: 16). Ein negatives Ergebnis bei A1 ODER A2 ist ein **Stopp-Grund fuer die Loeschsemantik aus D-05** — die Implementierung darf in diesem Fall NICHT einfach "umgebogen" werden (so RESEARCH.md/PLAN.md ausdruecklich), sondern erfordert eine erneute Planungsentscheidung. Vor produktivem Einsatz der Loeschsemantik muss die read-only Pruefung gegen ViCoTest (`balios.ctl.local`) aus `16-VALIDATION.md` nachgeholt werden: (1) eine AD-Gruppe suchen, `objectGUID` notieren, im AD umbenennen, erneut suchen, GUID vergleichen; (2) eine Suche mit dem byteweise escaped `objectGUID`-Filter absetzen und pruefen, ob sie exakt diese Gruppe zurueckliefert.
|
||||
|
||||
Gemaess `workflow.human_verify_mode: end-of-phase` wurde dieser Plan trotz der offenen Pruefung bis zum Ende ausgefuehrt, statt mittendrin anzuhalten — die offene Verifikation ist hier klar dokumentiert und im Ledger sichtbar, nicht stillschweigend uebersprungen.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
Siehe Deviations oben — alle drei Punkte betrafen ausschliesslich bestehende Testinfrastruktur, die durch die additiven bzw. verdrahtenden Aenderungen dieses Plans beruehrt wurde, nicht die fachliche Logik der neuen Methode selbst.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None — keine externe Service-Konfiguration erforderlich. Der laufende Docker-Stack (lokal) wurde von diesem Plan nicht angefasst; die Aenderungen liegen im Quellcode und werden erst mit einem `--build`/`--force-recreate` durch den Nutzer wirksam. Kein Deploy auf den Testserver durch diesen Lauf (Projektregel).
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `syncBoundGroupsForTenant()` ist vollstaendig implementiert, verdrahtet und getestet — die Kernkorrektheitsbedingung der Phase (Reihenfolge vor dem Mitgliedschafts-Abgleich) ist durch einen beobachtenden Test abgesichert, nicht nur behauptet
|
||||
- Plan 16-04 (Dialog-Umbau) und 16-05 (Anzeige-Fallback Frontend, Requirement-Abschluss PERM-02) koennen auf dieser Grundlage aufsetzen
|
||||
- **Offener Punkt bleibt bestehen (WINDOWS.md #4):** A1/A2-Live-Pruefung gegen ein echtes Active Directory (ViCoTest) muss nachgeholt werden, bevor die Loeschsemantik aus D-05 als produktionsreif gilt — siehe Abschnitt „Outstanding Live Verification" oben
|
||||
|
||||
---
|
||||
*Phase: 16-ad-gruppen-synchronisation*
|
||||
*Completed: 2026-08-06*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Alle 3 in diesem Plan genannten Quelldateien sowie dieses Summary existieren auf der Festplatte; beide Task-Commit-Hashes (5222934, 68aca81) sind im lokalen Git-Log auffindbar.
|
||||
Reference in New Issue
Block a user