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:
@@ -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 },
|
||||
|
||||
Reference in New Issue
Block a user