Node-ldapjs: search.on('end') fires before the entry is found

Created on 4 Jun 2016  路  10Comments  路  Source: ldapjs/node-ldapjs

I'm using ldapjs client libraries to connect to an Active Directory server, everything works if the login succeeds and the user is found, but only if there's no search.on('end') specified. If I ad the 'end' listener, then it gets called BEFORE the user is found, so when I try to return the response, it crashes the app.

THIS DOESN'T WORK

            client.search('ou=users, dc=company, dc=com', opts, function(error, search) {
                    console.log('Searching.....');

                    search.on('searchEntry', function(entry) {
                        if (entry.object) {
                            userClient = ldap.createClient({url: config.ldap.url, timeout: 5000, connectTimeout: 10000});
                            userClient.bind(entry.object.dn, password, function(err) {
                                if (err == null) {
                                    res.send({status: "OK", sessionID: sessionID});
                                } else {
                                    res.send("FAIL - BAD LOGIN");
                                }
                            });
                        }
                    });

                    search.on('end', function(){
                        res.send("FAIL - not found");
                    });

THIS WORKS, BUT ONLY IF THE USER IS FOUND, IF NOT, APP CRASHES.

            client.search('ou=users, dc=company, dc=com', opts, function(error, search) {
                    console.log('Searching.....');

                    search.on('searchEntry', function(entry) {
                        if (entry.object) {
                            userClient = ldap.createClient({url: config.ldap.url, timeout: 5000, connectTimeout: 10000});
                            userClient.bind(entry.object.dn, password, function(err) {
                                if (err == null) {
                                    res.send({status: "OK", sessionID: sessionID});
                                } else {
                                    res.send("FAIL - BAD LOGIN");
                                }
                            });
                        }
                    });

All I want to do is return a failed login if the user does not exist on the server. And also determine if the login attempt failed by the password.

Any help would be greatly appreciated.

question

Most helpful comment

here is how I did it. I have a login process as follow: search the LDAP and bind it. So either way I need the userObject or a null value to decide whether bind/search has failed:

let userObject = {};
    client.on('connect', function (e) {
        console.log('LDAP connected:');
        client.search(baseDN, opts, function(err, res) {
            res.on('searchEntry', function(entry) {               
                userObject = {
                    username: entry.object.uid,
                    firstname: entry.object.givenName,
                    lastname: entry.object.sn,
                    name: entry.object.cn,
                    email: entry.object.mail,
                    description: entry.object.description,
                    uidNumber: entry.object.uidNumber
                };
                client.bind(entry.object.dn, password, function(err, res) {
                    if(err){
                        // Bind failed, return null
                        callback(null);
                    }else{
                        // Bind was successful, return userObject                      
                        callback(userObject);
                    }
                });
            });
            res.on('searchReference', function(referral) {
                console.log('referral: ' + referral.uris.join());
            });
            res.on('error', function(err) {
                // client/TCP errors, return null
                callback(null);
            });
            res.on('end', function(result) {
                if(userObject.hasOwnProperty("username")){
                   // search was successful, will return null/objectUser in  client.bind
                  // no action needed here
                    console.log("finished success");
                }else{
                    // search failed, return null
                    callback(null);
                }
            });
        });
    });

I am using the latest version and res.end always returns null if it can't find anything. I don't really care if the search was successful as you can see. But the failed part always happens when the search fails.
It's unfortunate that in "search" the error is not the same as other functions:

Note that the error event will only be for client/TCP errors, not LDAP error codes like the other APIs

Hope this helps.

All 10 comments

have you tried

search.on('error', function() {
    res.send("FAIL - not found");
});

I have tried it, error never fires, end fires before anything.

i think when end fires before searchEntry that means the search from client.search(... didn't have any results

I thought the same thing, but end fires before searchEntry even when there is a record found. I haven't figured out a way to retrieve an login error with this library.

I have the exact same issue. If there's a workaround then I'm all ears :)

Have you tried using the compare function?
I would suggest something like this:

client.compare('uid='+uid+',ou=users,dc=company,dc=com', 'uid', ''+uid+'', function(err, matched) {
    if (err) {
        res.send("FAIL - not found");
    }
    else {
        userClient = ldap.createClient({url: config.ldap.url, timeout: 5000, connectTimeout: 10000});
        userClient.bind('uid='+uid+',ou=users,dc=company,dc=com', password, function(err) {
            if (err == null) {
                res.send({status: "OK", sessionID: sessionID});
            } else {
                res.send("FAIL - BAD LOGIN");
            }
        });
    }
});

here is how I did it. I have a login process as follow: search the LDAP and bind it. So either way I need the userObject or a null value to decide whether bind/search has failed:

let userObject = {};
    client.on('connect', function (e) {
        console.log('LDAP connected:');
        client.search(baseDN, opts, function(err, res) {
            res.on('searchEntry', function(entry) {               
                userObject = {
                    username: entry.object.uid,
                    firstname: entry.object.givenName,
                    lastname: entry.object.sn,
                    name: entry.object.cn,
                    email: entry.object.mail,
                    description: entry.object.description,
                    uidNumber: entry.object.uidNumber
                };
                client.bind(entry.object.dn, password, function(err, res) {
                    if(err){
                        // Bind failed, return null
                        callback(null);
                    }else{
                        // Bind was successful, return userObject                      
                        callback(userObject);
                    }
                });
            });
            res.on('searchReference', function(referral) {
                console.log('referral: ' + referral.uris.join());
            });
            res.on('error', function(err) {
                // client/TCP errors, return null
                callback(null);
            });
            res.on('end', function(result) {
                if(userObject.hasOwnProperty("username")){
                   // search was successful, will return null/objectUser in  client.bind
                  // no action needed here
                    console.log("finished success");
                }else{
                    // search failed, return null
                    callback(null);
                }
            });
        });
    });

I am using the latest version and res.end always returns null if it can't find anything. I don't really care if the search was successful as you can see. But the failed part always happens when the search fails.
It's unfortunate that in "search" the error is not the same as other functions:

Note that the error event will only be for client/TCP errors, not LDAP error codes like the other APIs

Hope this helps.

I think this has been sufficiently answered. If not feel free to re-open.

It seems I am facing the same issue now. In my log file I seem to see the SearchEntry to be present, but the 'end' is fired immediately showing this status.

My code is very simple:

const ldap = require('ldapjs');
var client = ldap.createClient({
    url: 'ldap://70.70.70.70:389',
    log,
});
client.bind('CN=vagrant,CN=Users,DC=perficientads,DC=com' /*'[email protected]'*/, 'vagrant', function(err) {
    if (err) {
        return console.log('Error:', err);
    }
    client.search('DC=perficientads,DC=com',
        {
            //filter:'(&(|(objectClass=user)(objectClass=person))(!(objectClass=computer))(!(objectClass=group))(cn=*vagrant*))',
            filter: '(sAMAccountName=vagrant)',
            //filter: '(&(|(objectClass=user)(objectClass=person))(!(objectClass=computer))(!(objectClass=group)))',
            attributes: [
                'dn', 'sn', 'cn',
                "mail",
            ],
            scope: 'sub',
        },
        function(err, res) {
            res.on('searchEntry', function(entry) {
                console.log('entry: ' + JSON.stringify(entry.object));
                resolve(null);
            });
            res.on('searchReference', function(referral) {
                console.log('referral: ' + referral.uris.join());
                resolve(null);
            });
            res.on('error', function(err) {
                console.error('error: ' + err.message);
                resolve(null);

            });
            res.on('end', function(result) {
                console.log('result status: ' + JSON.stringify(result));
                resolve(null);
            });
        }
    );
});

I am using ldapjs: latest, but upgrading to 2.0.0-pre.2 did not solve the issue.
I feel like I am missing something very simple. Please help, thanks.

P.S.: None of the filters included work.

@davidpodhola are you querying against a Microsoft Active Directory server? If so, I suspect there is some issue with referrals. Libraries like https://www.npmjs.com/package/activedirectory2 allow disabling referrals.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SeanPlusPlus picture SeanPlusPlus  路  5Comments

jjayaraman picture jjayaraman  路  5Comments

JohnPolansky picture JohnPolansky  路  3Comments

moehlone picture moehlone  路  3Comments

UziTech picture UziTech  路  4Comments