Fix ldapjs 3.x API: use entry.pojo for search results

- ldapjs 3.x returns entries as entry.pojo not entry.object
- Access attributes via entry.pojo.attributes array
- Clean up debug logging
- Restore group membership filter for production
This commit is contained in:
2026-02-25 03:42:00 +00:00
parent c1fe530696
commit cbf27bbc9c

View File

@@ -79,15 +79,19 @@ app.use((req, res, next) => {
async function authenticateLDAP(username, password) { async function authenticateLDAP(username, password) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const client = ldap.createClient({ url: CONFIG.ldap.url }); const client = ldap.createClient({
url: CONFIG.ldap.url,
timeLimit: 10,
sizeLimit: 100
});
client.on('error', (err) => { client.on('error', (err) => {
console.error('[LDAP] Connection error:', err.message);
reject(new Error('LDAP connection failed')); reject(new Error('LDAP connection failed'));
}); });
// Step 1: Bind with service account (if configured) to search for user // Step 1: Bind with service account (if configured) to search for user
const doSearch = () => { const doSearch = () => {
// Build search filter from config, replacing {{username}} with actual username
const filter = CONFIG.ldap.searchFilter.replace('{{username}}', username); const filter = CONFIG.ldap.searchFilter.replace('{{username}}', username);
const searchOptions = { const searchOptions = {
@@ -98,32 +102,12 @@ async function authenticateLDAP(username, password) {
let userDN = null; let userDN = null;
const user = { username }; const user = { username };
let searchComplete = false;
client.search(CONFIG.ldap.baseDN, searchOptions, (err, res) => { const finishSearch = () => {
if (err) { if (searchComplete) return;
client.destroy(); searchComplete = true;
reject(err);
return;
}
res.on('searchEntry', (entry) => {
userDN = entry.object.dn;
user.dn = entry.object.dn;
user.uid = entry.object.uid;
user.cn = entry.object.cn;
user.email = entry.object.mail;
user.displayName = entry.object.displayName || entry.object.cn;
user.groups = entry.object.memberOf
? (Array.isArray(entry.object.memberOf) ? entry.object.memberOf : [entry.object.memberOf])
: [];
});
res.on('error', (err) => {
client.destroy();
reject(err);
});
res.on('end', () => {
if (!userDN) { if (!userDN) {
client.destroy(); client.destroy();
reject(new Error('User not found')); reject(new Error('User not found'));
@@ -138,17 +122,46 @@ async function authenticateLDAP(username, password) {
return; return;
} }
console.log('[LDAP] Authenticated:', username);
client.destroy(); client.destroy();
resolve(user); resolve(user);
}); });
};
client.search(CONFIG.ldap.baseDN, searchOptions, (err, res) => {
if (err) {
client.destroy();
reject(err);
return;
}
res.on('searchEntry', (entry) => {
const pojo = entry.pojo;
userDN = pojo.objectName;
user.dn = pojo.objectName;
for (const attr of pojo.attributes) {
if (attr.type === 'uid') user.uid = attr.values[0];
if (attr.type === 'cn') user.cn = attr.values[0];
if (attr.type === 'mail') user.email = attr.values[0];
if (attr.type === 'displayName') user.displayName = attr.values[0];
if (attr.type === 'memberOf') user.groups = attr.values;
}
}); });
res.on('error', (err) => {
console.error('[LDAP] Search error:', err.message);
client.destroy();
reject(err);
});
res.on('end', () => finishSearch());
}); });
}; };
// If we have a service account bind, use it first
if (CONFIG.ldap.bindDN && CONFIG.ldap.bindPassword) { if (CONFIG.ldap.bindDN && CONFIG.ldap.bindPassword) {
client.bind(CONFIG.ldap.bindDN, CONFIG.ldap.bindPassword, (err) => { client.bind(CONFIG.ldap.bindDN, CONFIG.ldap.bindPassword, (err) => {
if (err) { if (err) {
console.error('[LDAP] Service bind failed:', err.message);
client.destroy(); client.destroy();
reject(new Error('LDAP service bind failed')); reject(new Error('LDAP service bind failed'));
return; return;
@@ -156,7 +169,6 @@ async function authenticateLDAP(username, password) {
doSearch(); doSearch();
}); });
} else { } else {
// No service bind - search anonymously (may not work with all LDAP servers)
doSearch(); doSearch();
} }
}); });