UnboundID LDAP SDK for Java

Ping Identity
Product Information

Using Additional UnboundID-Specific Extended Operations

In addition to the standard extended operations described on the Using Standard Extended Operations page of the Getting Started Guide, the UnboundID LDAP SDK for Java also provides support for an additional set of extended operations that allow for enhanced functionality when interacting with the Ping Identity, UnboundID, and Nokia/Alcatel-Lucent 8661 Directory Server. These extended operations will likely not work with any other directory, but can help provide an enhanced experience when using the Ping Identity, UnboundID, and Nokia/Alcatel-Lucent 8661 Directory Server.

Clear Missed Notification Changes

The clear missed notification changes operation may be used to clear a server alarm condition about missed change notifications.

Delete Notification Destination

The delete notification destination operation may be used to remove a notification destination, and all of its associated subscriptions, from the server.

Delete Notification Subscription

The delete notification subscription operation may be used to remove a notification subscription from the server.

Deliver and Consume Single-Use Token

The deliver single-use token operation may be used to generate a time-limited, single-use token and deliver it to a user through some out-of-band mechanism (e.g., email, SMS, voice call, etc.). The consume single-use token operation maye be used to indicate that the user has received that token. These operations may be useful for things like verifying that a user can receive email at a given address or SMS messages at a given mobile number.

Deliver One-Time Password

The deliver one-time password operation may be used to generate a time-limited, single-use password and deliver it to a user through some out-of-band mechanism (e.g., email, SMS, voice call, etc.) after verifying that a provided static password is valid for that user. This one-time password can be used to bind to the server via the UNBOUNDID-DELIVERED-OTP SASL mechanism as a means of performing two-factor authentication.

Deliver Password Reset Token

The deliver password reset token operation may be used to generate a time-limited, single-use token and deliver it to a user through some out-of-band mechanism (e.g., email, SMS, voice call, etc.). This token can be provided to the password modify operation in lieu of the user's current password in order to give that user the ability to choose a new password. This can be used to help automate the process of resetting a password that the end user had forgotten or allowed to expire.

Get Backup Compatibility Descriptor and Identify Backup Compatibility Problems

The get backup compatibility descriptor operation may be used to obtain a set of properties that a backend will use when creating a backup. The backup compatibility descriptor will automatically be embedded in any backup performed by the server, but this operation may be used to get that descriptor without actually performing a backup. The identify backup compatibility problems operation may be used to determine whether a backup with a given backup compatibility descriptor may be restored to that backend. This set of operations is particularly useful when attempting to restore a backup from one server into another server, or when performing online replica initialization between instances, to ensure that the target instance will be able to properly handle the data contained in the backup.

Get Changelog Batch

The get changelog batch operation may be used to retrieve information about a set of changes processed by the server. This operation can be used via polling or by blocking for changes to become available, can be used to retrieve only a subset of changes matching a given set of criteria (e.g., only certain change types, only changes to certain attributes, etc.), and can safely be used across all replicas in the environment, even if their changelogs may contain a slightly different operation order as a result of the difference between changes being processed locally versus those received from replication.

Get and List Configurations

The get configuration operation may be used to retrieve information about the server configuration. This may be the current configuration, a known baseline configuration, or a configuration that the server may have held in the past. The list configurations operation may be used to retrieve information about the set of configurations that can be retrieved using the get configuration operation. These operations may be used to help identify configuration differences between server instances, as well as to determine what configuration changes have been applied to a server over time.

Get Connection ID

The get connection ID extended operation provides a mechanism that allows the client to request the connection ID that the Ping Identity, UnboundID, or Nokia/Alcatel-Lucent 8861 Directory Server has assigned to that client connection. This can be helpful because it allows the client to include that in logging or debugging information that makes it easier to correlate requests from the client with operations in the server's access log.

The following code demonstrates the use of the get connection ID extended operation:

GetConnectionIDExtendedResult result =
     (GetConnectionIDExtendedResult) connection.processExtendedOperation(
          new GetConnectionIDExtendedRequest());

// NOTE:  The processExtendedOperation method will only throw an exception
// if a problem occurs while trying to send the request or read the
// response.  It will not throw an exception because of a non-success
// response.

if (result.getResultCode() == ResultCode.SUCCESS)
{
  System.out.println("The directory server's connection ID for this " +
                     "connection is " + result.getConnectionID());
}

Get Password Quality Requirements

The get password quality requirements may be used to retrieve information about the constraints that will be enforced for passwords for a given user in a specified operation. This may be used to allow an application to provide guidelines when allowing a user to change his or her password, and may also provide information that an application may be able to leverage in order to perform some client-side validation for a proposed password.

Get and Set Subtree Accessibility

The set subtree accessibility operation may be used to alter the accessibility of one or more subtrees in the DIT. This operation can be used to make subtrees fully accessible, read-only, or hidden. The get subtree accessibility operation can be used to retrieve information about subtree accessibility restrictions that are currently in place in the server.

Get Supported OTP Delivery Mechanisms

The get supported OTP delivery mechanisms operation may be used to obtain information about the set of mechanisms that the server offers for conveying one-time passwords, password reset tokens, and single-use tokens to end users, and which of those mechanisms may be available for use by a specified user.

List Notification Subscriptions

The list notification subscriptions operation may be used to obtain information about the set of notification destinations that have been defined, along with their associated subscriptions.

Multi-Update

The multi-update operation provides a mechanism for sending information about multiple add, delete, modify, modify DN, and/or password modify operations that should be processed in a single request. The client can indicate whether all of the changes should be processed as a single atomic unit, or whether they should be processed as if they had been separate requests.

Password Policy State

The password policy state extended operation provides a powerful mechanism that can be used to retrieve and alter a number of properties related to a user's current password policy state. The properties that are available are defined in the PasswordPolicyStateOperation object and include:

Most of these operations depend on the server's password policy configuration, and many of them may not apply in all configurations. For example, if password expiration is not enabled, then password policy state operations associated with password expiration may not be meaningful. Nevertheless, this can be a powerful capability for administrators, particularly given that the server may not allow many of these values to be altered in any other way.

The password policy state request needs to include the DN of the target user and a set of zero or more password policy state operations to process for that user. If no password policy state operations are included in the request, then it will be interpreted as a "get all" request, in which the server will return the values of all password policy state properties listed above but will not alter anything. If a specified set of password policy state operations is provided, then the server will only process those. For any password policy state changes that are made, the server will also return the updated values for the associated properties in the response.

The following example demonstrates the use of the password policy state operation in order to administratively disable a user's account:

PasswordPolicyStateOperation disableOp =
     PasswordPolicyStateOperation.createSetAccountDisabledStateOperation(
          true);
PasswordPolicyStateExtendedRequest pwpStateRequest =
     new PasswordPolicyStateExtendedRequest(
              "uid=john.doe,ou=People,dc=example,dc=com", disableOp);
PasswordPolicyStateExtendedResult pwpStateResult =
     (PasswordPolicyStateExtendedResult)
     connection.processExtendedOperation(pwpStateRequest);

// NOTE:  The processExtendedOperation method will only throw an exception
// if a problem occurs while trying to send the request or read the
// response.  It will not throw an exception because of a non-success
// response.

if (pwpStateResult.getResultCode() == ResultCode.SUCCESS)
{
  boolean isDisabled = pwpStateResult.getBooleanValue(
       PasswordPolicyStateOperation.OP_TYPE_GET_ACCOUNT_DISABLED_STATE);
  if (isDisabled)
  {
    System.out.println("The user account has been disabled.");
  }
  else
  {
    System.out.println("The user account is not disabled.");
  }
}

Set Notification Destination

The set notification destination operation may be used to create or update a notification destination in the server.

Set Notification Subscription

The set notification destination operation may be used to create or update a notification subscription in the server.

Start and End Administrative Session

The start administrative session operation may be used to indicate that the client is about to perform a sequence of operations that are intended for administrative use. It may optionally request that subsequent operations in that session be processed in a separate, dedicated pool of worker threads that are not used for processing normal operations and may be used to help an administrator diagnose a problem or take corrective action even if all normal worker threads are currently busy processing expensive operations. The end administrative session operation may be used to indicate that any following requests should not be considered part of the administrative session.

Start and End Batched Transaction

The start batched transaction and end batched transaction extended operations, and the associated batched transaction specification request control, are based on the specification in draft-zeilenga-ldap-txn. They can be used to create a batched transaction in which multiple write operations are performed as a single atomic unit. First, a start batched transaction request is sent to the server, which will return a start batched transaction result containing a transaction ID. Each operation that should be performed as part of that transaction should then include the batched transaction specification control containing that transaction ID. Finally, the end batched transaction request may be sent to the server to either commit or abort the transaction. If any of the operations performed as part of the transaction included response controls, then they will also be made available in the end transaction result.

Note that transactions processed in this manner can only contain add, delete, modify, and modify DN operations, as well as the password modify extended operation. It is not possible to include abandon, bind, compare, search, unbind, or other types of extended operations as part of transaction. However, it is possible to use the LDAP assertions control to ensure that an entry being updated matches an expected set of criteria, and it is also possible to use the LDAP pre-read and/or post-read control to retrieve copies of the entry immediately before and/or after a given write was performed.

The following example illustrates the use of the start and end batched transaction operations and the batched transaction specification request control to modify two entries as a single atomic update:

// Send the start transaction operation and get the transaction ID.
StartBatchedTransactionExtendedRequest startTxnRequest =
     new StartBatchedTransactionExtendedRequest();
StartBatchedTransactionExtendedResult startTxnResult =
     (StartBatchedTransactionExtendedResult)
     connection.processExtendedOperation(startTxnRequest);
if (startTxnResult.getResultCode() != ResultCode.SUCCESS)
{
  throw new LDAPException(startTxnResult);
}
ASN1OctetString txnID = startTxnResult.getTransactionID();

// At this point, we have a transaction available for use.  If any error
// occurs, we will want to make sure that the transaction is aborted, so
// use a try/finally block to handle that.
boolean shouldAbort = true;
try
{
  // Create and send the first modify request as part of the transaction.
  // Make sure to include the batched transaction specification control and
  // the post-read request control in the modify request.
  ModifyRequest modifyRequest1 = new ModifyRequest(
       "cn=first,dc=example,dc=com",
       new Modification(ModificationType.REPLACE, "description", "first"));
  modifyRequest1.addControl(
       new BatchedTransactionSpecificationRequestControl(txnID));
  modifyRequest1.addControl(new PostReadRequestControl());
  LDAPResult modifyResult1 = connection.modify(modifyRequest1);

  // Create and send the second modify request as part of the transaction.
  // Again, make sure to include the appropriate controls in the request.
  ModifyRequest modifyRequest2 = new ModifyRequest(
       "cn=second,dc=example,dc=com",
       new Modification(ModificationType.REPLACE, "description", "second"));
  modifyRequest2.addControl(
       new BatchedTransactionSpecificationRequestControl(txnID));
  modifyRequest2.addControl(new PostReadRequestControl());
  LDAPResult modifyResult2 = connection.modify(modifyRequest1);

  // Now we're ready to commit, which we can do with the end batched
  // transaction request with the commit flag set to true.
  EndBatchedTransactionExtendedRequest commitRequest =
       new EndBatchedTransactionExtendedRequest(txnID, true);
  EndBatchedTransactionExtendedResult commitResult =
       (EndBatchedTransactionExtendedResult)
       connection.processExtendedOperation(commitRequest);
  if (commitResult.getResultCode() == ResultCode.SUCCESS)
  {
    System.out.println("The transaction was committed successfully.");

    // Everything was successful, so we don't need to abort anything.
    shouldAbort = false;

    // Get the post-read response control for the first modify operation.
    // It's the same process for the second, but this example is already
    // long enough so we'll skip it.
    Control[] controls = commitResult.getOperationResponseControls(
         modifyResult1.getMessageID());
    if (controls != null)
    {
      for (Control c : controls)
      {
        if (c instanceof PostReadResponseControl)
        {
          PostReadResponseControl postReadResponse =
               (PostReadResponseControl) c;
          System.out.println("First entry after the modification:");
          System.out.println(postReadResponse.getEntry().toLDIFString());
        }
      }
    }
  }
  else
  {
    // The transaction failed for some reason.  The response should tell us
    // whether it failed because of one of the operations.
    int failedOpMessageID = commitResult.getFailedOpMessageID();
    if (failedOpMessageID == modifyResult1.getMessageID())
    {
      System.err.println("The transaction failed because of a failure " +
           "encountered while processing the first modification.");
    }
    else if (failedOpMessageID == modifyResult2.getMessageID())
    {
      System.err.println("The transaction failed because of a failure " +
           "encountered while processing the second modification.");
    }
    else
    {
      System.err.println("The transaction failed for some reason other " +
           "than either of the modify operations.");
    }

    throw new LDAPException(commitResult);
  }
}
finally
{
  if (shouldAbort)
  {
    // Setting the commit flag to false in the end batched transaction
    // request will cause the transaction to be aborted rather than
    // committed.
    EndBatchedTransactionExtendedRequest abortRequest =
          new EndBatchedTransactionExtendedRequest(txnID, false);
    connection.processExtendedOperation(abortRequest);
  }
}

Start and End Interactive Transaction

The start interactive transaction and end interactive transaction extended operations, and the associated interactive transaction specification request control, can be used to create an interactive transaction in which multiple operations are performed as a single atomic unit. First, a start interactive transaction request is sent to the server, which will return a start interactive transaction result containing a transaction ID. Each operation that should be performed as part of that transaction should then include the interactive transaction specification control containing that transaction ID. Finally, the end interactive transaction request may be sent to the server to either commit or abort the transaction.

Interactive transactions differ from batched transactions in a couple of key ways. First, interactive transactions may include read operations (e.g., search and compare) as well as write operations (e.g., add, delete, modify, and modify DN), whereas batched transactions only allow for write operations. Second, the operations which are part of an interactive transaction are processed immediately by the server rather than being batched and processed later, which means that there is no need for special handling with regard to response controls.

The following example illustrates the use of the start and end interactive transaction operations and the interactive transaction specification request control to perform a search and then modify all entries that match the associated search criteria:

// Start the interactive transaction and get the transaction ID.
StartInteractiveTransactionExtendedRequest startTxnRequest =
     new StartInteractiveTransactionExtendedRequest("dc=example,dc=com");
StartInteractiveTransactionExtendedResult startTxnResult =
     (StartInteractiveTransactionExtendedResult)
     connection.processExtendedOperation(startTxnRequest);
if (startTxnResult.getResultCode() != ResultCode.SUCCESS)
{
  throw new LDAPException(startTxnResult);
}
ASN1OctetString txnID = startTxnResult.getTransactionID();

// At this point, we have a valid transaction.  We want to ensure that the
// transaction is aborted if any failure occurs, so do that in a
// try-finally block.
boolean txnFailed = true;
try
{
  // Perform a search to find all users in the "Sales" department.
  SearchRequest searchRequest =
       new SearchRequest("dc=example,dc=com", SearchScope.SUB, "(ou=Sales)");
  searchRequest.addControl(
       new InteractiveTransactionSpecificationRequestControl(txnID, true,
                                                           true));
  SearchResult searchResult = connection.search(searchRequest);
  if (searchResult.getResultCode() != ResultCode.SUCCESS)
  {
    throw new LDAPException(searchResult);
  }

  // Iterate through all of the users and assign a new fax number to each
  // of them.
  for (SearchResultEntry e : searchResult.getSearchEntries())
  {
    ModifyRequest modifyRequest = new ModifyRequest(e.getDN(),
         new Modification(ModificationType.REPLACE, "facsimileTelephoneNumber",
                          "+1 123 456 7890"));
    modifyRequest.addControl(
         new InteractiveTransactionSpecificationRequestControl(txnID, true,
                                                               true));
    connection.modify(modifyRequest);
  }

  // Commit the transaction.
  ExtendedResult endTxnResult = connection.processExtendedOperation(
       new EndInteractiveTransactionExtendedRequest(txnID, true));
  if (endTxnResult.getResultCode() == ResultCode.SUCCESS)
  {
    txnFailed = false;
  }
}
finally
{
  if (txnFailed)
  {
    connection.processExtendedOperation(
         new EndInteractiveTransactionExtendedRequest(txnID, false));
  }
}

Stream Directory Values

The stream directory values operation may be used to request that a Directory Server retrieve information about all unique entry DNs and/or all unique values for a specified attribute or set of indexed attributes. Although it is primarily used by the Directory Proxy Server for populating the entry-balancing global index, it may also be useful for other purposes (e.g., it provides a very efficient way to retrieve a list of the DNs of all entries in a specified portion of the DIT).