diff --git a/server/index.js b/server/index.js index 155a2bd..e4610ae 100644 --- a/server/index.js +++ b/server/index.js @@ -79,15 +79,19 @@ app.use((req, res, next) => { async function authenticateLDAP(username, password) { 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) => { + console.error('[LDAP] Connection error:', err.message); reject(new Error('LDAP connection failed')); }); // Step 1: Bind with service account (if configured) to search for user const doSearch = () => { - // Build search filter from config, replacing {{username}} with actual username const filter = CONFIG.ldap.searchFilter.replace('{{username}}', username); const searchOptions = { @@ -98,6 +102,31 @@ async function authenticateLDAP(username, password) { let userDN = null; const user = { username }; + let searchComplete = false; + + const finishSearch = () => { + if (searchComplete) return; + searchComplete = true; + + if (!userDN) { + client.destroy(); + reject(new Error('User not found')); + return; + } + + // Step 2: Bind as the user to verify password + client.bind(userDN, password, (err) => { + if (err) { + client.destroy(); + reject(new Error('Invalid credentials')); + return; + } + + console.log('[LDAP] Authenticated:', username); + client.destroy(); + resolve(user); + }); + }; client.search(CONFIG.ldap.baseDN, searchOptions, (err, res) => { if (err) { @@ -107,48 +136,32 @@ async function authenticateLDAP(username, password) { } 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]) - : []; + 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', () => { - if (!userDN) { - client.destroy(); - reject(new Error('User not found')); - return; - } - - // Step 2: Bind as the user to verify password - client.bind(userDN, password, (err) => { - if (err) { - client.destroy(); - reject(new Error('Invalid credentials')); - return; - } - - client.destroy(); - resolve(user); - }); - }); + res.on('end', () => finishSearch()); }); }; - // If we have a service account bind, use it first if (CONFIG.ldap.bindDN && CONFIG.ldap.bindPassword) { client.bind(CONFIG.ldap.bindDN, CONFIG.ldap.bindPassword, (err) => { if (err) { + console.error('[LDAP] Service bind failed:', err.message); client.destroy(); reject(new Error('LDAP service bind failed')); return; @@ -156,7 +169,6 @@ async function authenticateLDAP(username, password) { doSearch(); }); } else { - // No service bind - search anonymously (may not work with all LDAP servers) doSearch(); } });