From 67aef9c5ffaacb59cd8d6e2d5b12d19a1592afd7 Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Thu, 20 Aug 2026 14:34:28 +0500 Subject: [PATCH] ldap2 --- lib/ldapClient.js | 22 ++++++++++++++++++++-- lib/patch-ldap-dn.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 lib/patch-ldap-dn.js diff --git a/lib/ldapClient.js b/lib/ldapClient.js index b80c550..527a4b3 100644 --- a/lib/ldapClient.js +++ b/lib/ldapClient.js @@ -1,6 +1,7 @@ 'use strict'; const fs = require('fs'); +require('./patch-ldap-dn'); const ldap = require('ldapjs'); const config = require('./config'); @@ -208,13 +209,30 @@ function entryToObject(entry) { function addEntry(client, dn, attributes) { return new Promise((resolve, reject) => { - client.add(dn, attributes, (err) => (err ? reject(mapLdapError(err)) : resolve())); + const entry = {}; + for (const a of attributes || []) { + entry[a.attr] = a.vals; + } + client.add(dn, entry, (err) => (err ? reject(mapLdapError(err)) : resolve())); }); } +function normalizeChange(change) { + const mod = change.modification || {}; + const key = Object.keys(mod)[0]; + const values = mod[key]; + return { + operation: change.operation, + modification: { + type: key, + values: Array.isArray(values) ? values : [values] + } + }; +} + function modifyEntry(client, dn, changes) { return new Promise((resolve, reject) => { - client.modify(dn, changes, (err) => (err ? reject(mapLdapError(err)) : resolve())); + client.modify(dn, (changes || []).map(normalizeChange), (err) => (err ? reject(mapLdapError(err)) : resolve())); }); } diff --git a/lib/patch-ldap-dn.js b/lib/patch-ldap-dn.js new file mode 100644 index 0000000..64a7fdc --- /dev/null +++ b/lib/patch-ldap-dn.js @@ -0,0 +1,44 @@ +'use strict'; + +// Workaround for ldapjs 3.x / @ldapjs/dn: its escapeValue hex-escapes every +// non-ASCII UTF-8 byte in DN values (\d0\a2...). Active Directory's LDAP add +// parser decodes those hex escapes as Latin-1 characters, so Cyrillic DNs get +// double-encoded (mojibake) on the server and group membership lookups fail +// with LDAP 32. Keep non-ASCII characters raw so writeString sends real UTF-8. +// Must be loaded BEFORE ldapjs/@ldapjs/dn are required. +// +// eslint-disable-next-line global-require +const modulePath = require.resolve('@ldapjs/dn/lib/utils/escape-value'); +// eslint-disable-next-line global-require +require(modulePath); // ensure it is in the require cache +require.cache[modulePath].exports = function escapeValue(value) { + if (typeof value !== 'string') { + throw new Error('value must be a string'); + } + let result = ''; + for (let i = 0; i < value.length; i += 1) { + const c = value.charCodeAt(i); + if (c === 0x5c) { + result += '\\\\'; + continue; + } + if (c === 0x2c || c === 0x2b || c === 0x22 || c === 0x3c || c === 0x3e || c === 0x3b || c === 0x3d) { + result += '\\' + value[i]; + continue; + } + if (c <= 31) { + result += '\\' + c.toString(16).padStart(2, '0'); + continue; + } + if ((i === 0 || i === value.length - 1) && c === 0x20) { + result += '\\20'; + continue; + } + if (i === 0 && c === 0x23) { + result += '\\23'; + continue; + } + result += value[i]; + } + return result; +}; \ No newline at end of file