0

I must connect to a LDAP server and find user based on a specific ID.

I've decided to use ldapjs module.

I managed to create my client and bind him so I can search for an user with success.

My problem is, as I only use async/await, that I don't understant how to handle error in callbacks... For example with this simple code from ldapjs library :

public static async search(searchOptions: SearchOptions) {
    LdapService.bind()

    LdapService.getClient()?.search('ou=*****,ou=***,dc=****,dc=****', searchOptions, function (err, res) {
      ifError(err)

      res.on('searchEntry', function (entry) {
        // ----------------------
        // an error came from here
        throw new Error('test') 
        // ----------------------

        console.log('entry: ' + JSON.stringify(entry.object));
      });

      res.on('searchReference', function (referral) {
        console.log('referral: ' + referral.uris.join());
      });

      res.on('error', function (err) {
        console.error('error: ' + err.message);
      });

      res.on('end', function (result) {
        console.log('status: ' + result?.status);
      });
    })
  }

LdapService.getClient() is a singleton method that return the result of createClient -> works fine

LdapService.bind() is a method that just bind with the server with correct credentials -> works fine

I just can't manage to handle my error "test"... How am I supposed to handle it?

Is the search method really async?

Can I do it the async/await way? :P

PS: the DN string ("ou=,ou=,dc=,dc=") is hidden due to security reasons and the code works great without throwing an error ;)

spMaax
  • 365
  • 2
  • 11

2 Answers2

0

For anyone passing here and struggling with callback like me, here the "fix" I found :

public static async search(searchOptions: SearchOptions) {
    // wrapping the all thing in a Promise that can be in a try/catch -> error will be handled there
    return await new Promise((resolve, reject) => {
      LdapService.getClient()?.search('ou=****,ou=****,dc=****,dc=****', searchOptions, function (err, res) {
        if (err) {
          // handle connection error I guess ?
          reject(err)
        }

        res.on('searchEntry', function (entry) {
          try {
            // -------------
            // Same test as before
            throw new Error('test')
            // -------------

            resolve(JSON.stringify(entry.object))
          } catch (err) {
            // error must be catched to call reject method !
            reject(err)
          }
        });

        res.on('error', function (err) {
          // handle API error when looking for the specific ID I guess ?
          reject(err)
        });
      })
    })
  }

What solved my problem? Wrapping the all thing in a Promise.

All the "res.on" methods before were something like Promise listeners. So the all method search (the one inside mine) was a sort of asynchronous call.

Now I can call resolve/reject methods to return data and error.

Also, I can call my static search method the async/await way.

When you're not familiar with callbacks... ^^"

spMaax
  • 365
  • 2
  • 11
  • I also remove all unused "res.on" methods! Maybe there is a shorter way but this one works ;) – spMaax Nov 02 '20 at 14:19
0

There is another way to do this. Since the Client object in ldapjs is en EventEmitter, you can turn on the 'captureRejections' boolean.

details: https://nodejs.org/api/events.html#error-events

Example from docs:

import { EventEmitter } from 'node:events';

EventEmitter.captureRejections = true;
const ee1 = new EventEmitter();
ee1.on('something', async (value) => {
  throw new Error('kaboom');
});

ee1.on('error', console.log);

when building the client, you can pass this as an option, and then the .on handlers behave a lot better. Once you add it to the client, all the bind and search functions use it as well.

async buildLdapClient() {
    return new Promise((resolve, reject) => {

        const options: ClientOptions = {
            url: ldaps://something.here.not.there.com:636,
            connectTimeout: 5000,
            timeout: 5000,
            reconnect: false,
            captureRejections: true
        }

        let client = ldapjs.createClient(options);

        client.on('timeout', (error) => {
            reject(error);
        });

        client.on('connectTimeout', (error) => {
            reject(error);
        });

        client.on('timeLimit', (error) => {
            reject(error);
        });

        client.on('error', (error) => {
            reject(error);
        });

        resolve(client);

    });
}

`