fix(16): WR-04 normalize the rename-vs-unchanged comparison

syncBoundGroupsForTenant() compared cn/dn for byte equality, so any
casing difference AD returns between two runs (e.g. after a
domain-controller switch) would look like a rename and re-write
name/ldapDn every single sync — violating the 'sync twice over an
unchanged AD state = no-op' idempotency guarantee. The comparison used
to DECIDE 'is this a rename' is now case-insensitive; the value written
on an actual rename is still stored byte-for-byte as the directory
reports it, per D-03.
This commit is contained in:
2026-08-06 16:58:29 +02:00
parent 19717954d6
commit 2779d42e6c
2 changed files with 50 additions and 1 deletions
+20 -1
View File
@@ -1308,8 +1308,27 @@ export class LdapService {
: hit.dn;
const dn = hit.dn;
if (name !== group.name || dn !== group.ldapDn) {
// WR-04 (16-REVIEW.md): the CHANGE CHECK is case-insensitive —
// only the DECISION "is this a rename" is normalized, never the
// value written below. AD returning the same cn/dn with different
// casing between two runs (e.g. after a domain-controller switch)
// must not look like a rename: that would break the idempotency
// guarantee (Priority Check 7 — sync twice over an unchanged AD
// state = no-op) and increment groupsRenamed / issue an update on
// every subsequent run. This is a plain lowercase compare, not
// full RFC 4514 DN canonicalization (per-attribute-type
// case-sensitivity rules, escaped-character normalization, etc.)
// — a pragmatic simplification, same uncertainty class as A1/A2 in
// RESEARCH.md, not verified against a real AD.
const nameChanged = name.toLowerCase() !== (group.name ?? '').toLowerCase();
const dnChanged =
dn.toLowerCase() !== (group.ldapDn ?? '').toLowerCase();
if (nameChanged || dnChanged) {
try {
// The write below stores name/dn EXACTLY as the directory
// reports them, byte-for-byte — never the lowercased
// comparison values above. Group.name stays AD's, per D-03.
await tenantPrisma.group.update({
where: { id: group.id },
data: { name, ldapDn: dn },