Menu

Authenticate LDAP users in asynchronous mode with UnboundID LDAP SDK

2016-02-07
2016-03-17
  • Malinda Dilhara

    Malinda Dilhara - 2016-02-07

    Since I am kind of new this would be a basic question.
    I need to Authenticate LDAP users in asynchronous mode. According to this source we can not use simple bind request in asynchronous mode. If then, How can I authenticate a user by providing DN and password.

     

    Last edit: Malinda Dilhara 2016-02-07
  • Neil Wilson

    Neil Wilson - 2016-02-08

    LDAP is an inherently asynchronous protocol, meaning that, in most cases, you can issue multiple concurrent requests over the same connection and the client will be able to correlate the responses with their appropriate requests.

    However, the LDAP protocol specification (RFC 4511 section 4.2.1) states that bind operations cannot be processed on a connection that has any other outstanding operations. In particular "Before processing a BindRequest, all uncompleted operations MUST either complete or be abandoned" and "After sending a BindRequest, clients MUST NOT send further LDAP PDUs until receiving the BindResponse." This is because a bind operation is used to change the authentication state of a connection (and in some cases may also include negotiating a communication security layer). It is dangerous to have other types of operations in progress on the connection while a bind is being processed because the bind processing may change the nature of the response to the client.

    The UnboundID LDAP SDK for Java is inherently asynchronous by default, meaning that it operates in a mode that allows multiple operations to be processed concurrently on a single connection. There are two ways to do this:

    1. Simply use the same connection concurrently across multiple threads. For example, if you have two threads that are sharing the same connection, you could call LDAPConnection.search on one thread while LDAPConnection.modify is being called on another. In this mode of operation, whenever you send an LDAP request on a thread, that thread will block until it gets the response (or until the response timeout is reached, or until it detects that the connection has been terminated), but other threads can use the same connection for other operations.
    2. Use the asynchronous API to allow a single thread to interact with multiple operations simultaneously. For example, if you call LDAPConnection.modify, then the LDAP SDK will send the request and wait until it gets the response, but if you call LDAPConnection.asyncModify, then the LDAP SDK will send the request and then immediately return control to the thread so that it can do something else. It will provide you with an AsyncRequestID that you can use to determine whether the operation has completed (and if so to get that response) whenever you want to check on it.

    But as per the restriction around bind operations, the LDAP SDK does not provide an LDAPConnection.asyncBind because your application should not ever attempt to process a bind operation on a connection that might have one or more other operations in progress. The LDAP SDK won't explicitly prevent you from calling LDAPConnection.bind simultaneously on the same connection from two separate threads, but you need to be careful to not do that in your own code.

    In general, I would recommend that you avoid using asynchronous operations altogether. There is rarely a good reason to use them, and it will likely make your application less reliable. If your application needs to be able to issue multiple LDAP requests simultaneously, then it is far better to use separate connections for those requests, and the best way to do that is to use an LDAPConnectionPool to manage those connections.

    The LDAPConnectionPool class is specifically designed to make this as convenient and reliable as possible. While you can check out a connection, use it to perform an operation, and then release that connection to the pool, it is better to just invoke the connection against the pool and have it do the connection management for you. For example, if you want to perform a search operation on a pooled connection, instead of:

    LDAPConnection connection = pool.getConnection();
    SearchResult searchResult = connection.search(searchRequest);
    pool.releaseConnection(connection);
    

    you should simply do the following:

    SearchResult searchResult = pool.search(searchRequest);
    

    Not only is this more convenient, but it has several advantages that make your application more reliable, including:

    • Your application doesn't have to worry about making sure that the connection gets returned to the pool once the operation is complete, nor to try to determine whether the connection is still usable or not. Applications that check out and release their own connections have a tendency to encounter circumstances that can cause a connection to get checked out but not released (which means that connections get leaked and over time may consume all available file descriptors on the directory server). It can also lead to cases in which an application mistakenly puts a no-longer-valid connection back into the pool where it can cause other operations to fail.
    • The LDAP SDK connection pooling mechanism provides support for establishing connections to multiple servers in support of high availability and load balancing. You don't need to worry about this in your application, nor do you need to have any kind of hardware load balancer (and the LDAP SDK is able to do it better than a hardware load balancer anyway).
    • The LDAP SDK connection pool can be configured to automatically retry operations that fail in a way that suggests that the connection is no longer valid. If the connection pool detects such a failure, then it will automatically create a new connection (possibly to a different server than the one to which the former connection was established) and send the same request over the new connection. This is completely transparent to your application.
    • The LDAP SDK provides the ability to periodically check the health of the connections it maintains so that your application is less likely to encounter a connection that is no longer available. It can also close and re-establish connections after some amount of time to work around cases in which the directory server (or commonly, some poorly-designed piece of network hardware) will drop a connection after it has been established for too long.
     
  • Malinda Dilhara

    Malinda Dilhara - 2016-02-08

    Since synchronous mode is a blocking call, current thread will have to wait for the response and that will effect to the performance (Let's say number of authentication requests per second). But, using asynchronous mode we can get rid of that impact and increase the performance. This is the reason for choosing asynchronous mode rather than using synchronous mode. How can I solve this problem using Unbound ID LDAP SDK ? Is there any technique that can be used to improve performance of synchronous mode than asynchronous mode? Or else is there any way to authenticate a user rather than using bind request in asynchronous mode?

     
  • Neil Wilson

    Neil Wilson - 2016-02-08

    As I said in my initial response, as per RFC 4511 section 4.2.1, the LDAP specification states that while a bind operation is in progress on a connection, nothing else (including other bind operations) is permitted on that connection. This is not a limitation of the UnboundID LDAP SDK for Java—it is a fundamental restriction of the LDAP protocol, and for good reason.

    As I also said before, this only applies to concurrent operations on the same connection. There is nothing to prevent simultaneous bind operations from being processed on separate connections. Your application can process as many concurrent bind operations as is necessary, as long as each is processed on a separate connection. This is the way that LDAP-enabled applications behave, and it does not adversely affect performance (in fact, processing simultaneous operations on the same connection generally yields somewhat lower performance than processing those operations on separate connections because of contention around the shared connection). For example, on adequate hardware, a single instance of the UnboundID Directory Server can process hundreds of thousands of simultaneous bind operations.

     
  • Malinda Dilhara

    Malinda Dilhara - 2016-02-28

    Thank you for the explanation. I am proceeding with the implementation according to your suggestions on asynchronous mode and synchronous mode. Considering the huge load that I am getting for authentication I have decided to work on multiple threads and connection pool. Further, I have one question about my design. I have two design options on my mind

    1. Pass Connectionpool to individual threads as argument and get a connection to do the bind request from ConnectionPool
    2. Pass connection to threads rather than passing Connectionpool to threads and do bind request

    Which Design would you prefer most and what are the reasons for them?

     
  • Neil Wilson

    Neil Wilson - 2016-02-28

    I would recommend that you make the LDAPConnectionPool available to the threads doing the bind rather than giving them an LDAPConnection. However, instead of having the thread check out a connection and use it to perform the bind, you have the pool itself do the bind. That is, instead of writing something like:

    public BindResult doBind(LDAPConnectionPool pool,
                             BindRequest bindRequest)
                 throws LDAPException
    {
      LDAPConnection connection = pool.getConnection();
    
      try
      {
        BindResult bindResult = connection.bind(bindRequest);
        pool.releaseConnection(connection);
        return bindResult;
      }
      catch (LDAPException le)
      {
        if (le.getResultCode().isConnectionUsable())
        {
          pool.releaseConnection(connection);
        }
        else
        {
          pool.releaseDefunctConnection(connection);
        }
    
        throw le;
      }
    }
    

    I would recommend that you just use:

    public BindResult doBind(LDAPConnectionPool pool, 
                             BindRequest bindRequest)
                 throws LDAPException
    {
      return pool.bind(bindRequest);
    }
    

    Not only is this much simpler, because the connection pool does all of the error checking and connection management for you, but it can also make your application more reliable. You can configure the connection pool so that, if the operation fails in a way that indicates that the connection is no longer usable, the pool will automatically create a new connection (possibly to a different server, if you created the connection pool with a ServerSet) and re-try the same operation on that newly-created connection. This means that if a server goes down or a connection gets reset, your application doesn't even have to know about it as long as it's possible to create a new connection to one of the servers that you've configured.

     
  • Malinda Dilhara

    Malinda Dilhara - 2016-03-01

    Thank you for the explanation. This is seem to be more user friendly.
    I have several questions further.
    According to guide

    Performing a bind operation using a connection from the pool will invalidate any previous authentication on that connection, and if that connection is released back to the pool without first being re-authenticated as the original user, then subsequent operation attempts may fail or be processed in an incorrect manner. Bind operations should only be performed in a connection pool if the pool is to be used exclusively for processing binds, if the bind request is specially crafted so that it will not change the identity of the associated connection (e.g., by including the retain identity request control in the bind request if using the Commercial Edition of the LDAP SDK with an UnboundID Directory Server), or if the code using the connection pool makes sure to re-authenticate the connection as the appropriate user whenever its identity has been changed.

    According to this I do not need to re-authenticate the connection before releasing if I am using commercial edition. How can I include the retain identity request?

    I have another question regarding maximum number of connection of a pool. In the guide they have mentioned this is the maximum number of unused connection at a given time. Is this explanation correct? Shouldn't it maximum number of all currently using and not using connections? If this is maximum number of unused connections at a given time then connection pool should keep growing when there is a heavy load? Isn’t it?

     

    Last edit: Malinda Dilhara 2016-03-01
  • Neil Wilson

    Neil Wilson - 2016-03-01

    The retain identity request control is a proprietary feature that is only available in the UnboundID Directory Server. If you're not using the UnboundID Directory Server, then you can't use it.

    If you want to use connection pools, and if you want to use pooled connections for both bind and non-bind operations, then you have two basic options:

    • Maintain two separate connection pools. One connection pool will be used exclusively for binds, and the other for everything else. In this case, the bind operations in one pool won't affect the authentication state for connections in the other pool.
    • Use a single connection pool, but instead of calling LDAPConnectionPool.bind, use LDAPConnectionPool.bindAndRevertAuthentication instead. This method will do all the work of getting a connection performing the bind, and releasing the connection back to the pool, but before it releases that connection, it will perform a second bind to undo the effects of the bind operation that you just processed.

    For your second question about the number of connections, it is important to note that the connection pool only keeps track of the number of connections that are not currently being used. If you release a connection back to the pool and the number of currently-available connections is already at the maximum, then the connection you are releasing will be closed instead of being put back in the pool.

    What is important to note, however, is how the pool behaves when you try to get a connection and there isn't one immediately available. The connection pool offers four possible modes of operation:

    • Immediately throw an exception to indicate that no connections are available.
    • Immediately create a new connection to use.
    • Wait for up to a specified period of time for an existing connection to be released back to the pool. If it's not possible to get an existing connection within that period of time, then throw an exception to indicate that no connections are available.
    • Wait for up to a specified period of time for an existing connection to be released back to the pool. If it's not possible to get an existing connection within that period of time, then create a new connection to use.

    Which mode the connection pool will use in this case is controlled by two settings. The setMaxWaitTimeMillis method is used to specify how long to wait for an existing connection to become available (by default, there is no wait at all), and the setCreateIfNecessary method is used to indicate whether the pool can create a new connection if one is needed but none are available after waiting the specified period of time (by default, the pool will create a new connection instead of throwing an exception).

    This does mean that under heavy load, you can end up with a case in which there are more connections in use than the specified maximum. However, when that load dies down, then any extra connections that had been created to handle the spike in traffic will be closed, so that you won't end up with more than the maximum number of connections. However, if you want, you can configure the connection pool so that it won't ever have more than the maximum allowed number of connections, and you can either make your clients wait for an existing connection to be released or handle the possibility that no connections are available.

     
  • Malinda Dilhara

    Malinda Dilhara - 2016-03-09

    Thank you for your detailed description. I have another prolem with creation of the connection pool . When connection pool is created using server set we have to provide SimpleBindRequest as a argument. What is the purpose of this? Is that mean we can not create a connection pool with server set without valid credentials ?

     

    Last edit: Malinda Dilhara 2016-03-09
  • Neil Wilson

    Neil Wilson - 2016-03-09

    It doesn't necessarily have to be a SimpleBindRequest. It can be any kind of BindRequest, in case you wanted to perform a SASL bind rather than a simple bind. It can also be null in case you don't want to bind at all.

     
  • Malinda Dilhara

    Malinda Dilhara - 2016-03-13

    Thank you the help again. Further, I am searching for user attributes such as surname . given name for a particular DN. As an example, I want to get surname of the DN o=mojo,ou=users,cn=malinda. Can I know whether this is fine?

    Filter filter = Filter.createEqualityFilter(cn,malinda);
                    SearchRequest searchRequest =
                            new SearchRequest(o=mojo,ou=users, SearchScope.BASE, filter,
                                    "sn");
    
     
  • Neil Wilson

    Neil Wilson - 2016-03-14

    Your code probably won't do what you want for a couple of reasons:

    • It looks like your search base DN might be backwards. LDAP DNs are ordered with the root of the tree (often called the "naming context" or "suffix") is on the right side, so that the leftmost element is the furthest from the root. In your example, it seems much more likely that "o=mojo" is the root of the tree and that "ou=users" is a child of "o=mojo". In that case, the correct base DN would be "ou=users,o=mojo" rather than "o=mojo,o=users". If you specify the base DN for an entry that doesn't exist, then the server will likely return search result with a "no such object" (integer value 32), which will cause the LDAP SDK to throw an LDAPSearchException.
    • You probably want to use SearchScope.SUB instead of SearchScope.BASE. A scope of BASE means that you only want the server to look in the entry specified as the base DN and no other entries. A scope of SUB means that you want the server to look at the entry specified as the base DN and all of its subordinates to any depth. The other defined scopes are ONE (which means that you only want the server to search the entries that are immediately subordinate to the base DN, but not any of their subordinates, and not the base entry itself), and SUBORDINATE_SUBTREE (which means that you want the server to look at all entries below the base DN, but not the base entry itself).
     
  • Malinda Dilhara

    Malinda Dilhara - 2016-03-14

    Thank you for the help. Yes of course my root is o=mojo and I have confused to put in reverse order. It should be "cn=malinda,ou=users,o=mojo" But, I need to search in exsactly same DN but not in ubordinates. My requarement is to get attribute value in the given DN. Can I achive this by putting SearchScope.BASE ? Is my filter good for this?

     

    Last edit: Malinda Dilhara 2016-03-14
  • Neil Wilson

    Neil Wilson - 2016-03-14

    Yes, your filter should be fine. And if you only want to search one specific entry and none of its subordinates, then the way to accomplish that is with a base DN that is the DN for the entry you want to examine and a scope of SearchScope.BASE.

     

    Last edit: Neil Wilson 2016-03-14
  • Malinda Dilhara

    Malinda Dilhara - 2016-03-17

    I would be really grateful about you support. I have another requirement to change password of DNs. In reference I saw there are two ways to do this by modify operation and PasswordModifyExtendedRequest. Since my application should work with all the types of AD servers, How can I achieve my requirement?. What is the best option? Is there any other ways to do this?

     

Log in to post a comment.