This chapter demonstrates how to use LDAP extended operations.
For complete examples corresponding to the excerpts shown below, see ExtendedOperations.java, one of the OpenDJ LDAP SDK examples.
Extended operations allow additional operations to be defined for services not already available in the protocol
For OpenDJ, the extended operations supported are listed in the
Administration Guide appendix, LDAP Extended
Operations. You can access the list of OIDs for
supported LDAP controls by reading the supportedExtension
attribute of the root DSE.
$ ldapsearch --baseDN "" --searchScope base --port 1389 "(objectclass=*)" supportedExtension dn: supportedExtension: 1.3.6.1.1.8 supportedExtension: 1.3.6.1.4.1.26027.1.6.1 supportedExtension: 1.3.6.1.4.1.26027.1.6.2 supportedExtension: 1.3.6.1.4.1.26027.1.6.3 supportedExtension: 1.3.6.1.4.1.4203.1.11.1 supportedExtension: 1.3.6.1.4.1.4203.1.11.3 supportedExtension: 1.3.6.1.4.1.1466.20037
The following excerpt shows code to check for supported extended operations.
/** * Controls supported by the LDAP server. */ private static Collection<String> extendedOperations; /** * Populate the list of supported LDAP extended operation OIDs. * * @param connection * Active connection to the LDAP server. * @throws ErrorResultException * Failed to get list of extended operations. */ static void checkSupportedExtendedOperations(Connection connection) throws ErrorResultException { extendedOperations = RootDSE.readRootDSE(connection) .getSupportedExtendedOperations(); } /** * Check whether an extended operation is supported. Call * {@code checkSupportedExtendedOperations} first. * * @param extendedOperation * Check support for this extended operation, provided by OID. * @return True if the control is supported. */ static boolean isSupported(final String extendedOperation) { if (extendedOperations != null && !extendedOperations.isEmpty()) { return extendedOperations.contains(extendedOperation); } return false; }
RFC 3909, LDAP Cancel Operation, defines an extended operation that lets you cancel an operation in progress and get an indication of the outcome.
The Cancel extended request uses the request ID of operation you want to cancel, and so therefore works with asynchronous searches and updates. Depending on the delay between your application sending the Cancel request and the directory server receiving the request, the server might have already finished processing the original request before it receives your Cancel request.
You can add a Cancel extended request for example to stop handling entries returned from a search if the directory server returns more entries than you want.
private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1); private static final CountDownLatch CANCEL_LATCH = new CountDownLatch(1); private static final LDIFEntryWriter WRITER = new LDIFEntryWriter(System.out); static int requestID; static int entryCount = 0; // The requestID is obtained from the future result of the asynchronous search. // For more context see the example, SearchAsync.java. private static final class SearchResultHandlerImpl implements SearchResultHandler { @Override public synchronized boolean handleEntry(final SearchResultEntry entry) { try { // Cancel the search if it returns too many results. if (entryCount < 10) { WRITER.writeComment("Search result entry: " + entry.getName().toString()); WRITER.writeEntry(entry); ++entryCount; } else { CancelExtendedRequest request = Requests.newCancelExtendedRequest(requestID); connection.extendedRequestAsync( request, null, new CancelResultHandlerImpl()); return false; } } catch (final IOException e) { System.err.println(e.getMessage()); resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue(); COMPLETION_LATCH.countDown(); return false; } return true; } ... } private static final class CancelResultHandlerImpl implements ResultHandler<ExtendedResult> { @Override public void handleErrorResult(final ErrorResultException error) { System.err.println("Cancel request failed with result code: " + error.getResult().getResultCode().intValue()); CANCEL_LATCH.countDown(); } @Override public void handleResult(final ExtendedResult result) { System.err.println("Cancel request succeeded"); CANCEL_LATCH.countDown(); } }
OpenDJ directory server supports the cancel operation. If OpenDJ
directory server manages to return all entries in
Example.ldif
before it receives the Cancel extended
request, you can see the Cancel request fail because the request ID refers
to the search, which is no longer in progress. Try adding a new base DN using
OpenDJ control panel and adding the default 2000 generated entries to ensure
more search results. For example if dc=example,dc=org
contains 2000 generated entries, and the SearchAsync
example is run with the arguments sub objectclass=* cn
for scope, filter, and attributes respectively, then the example produces
something like the following output:
Canceled: Processing on this operation was terminated as a result of receiving a cancel request (message ID 3) # Search result entry: dc=example,dc=org dn: dc=example,dc=org # Search result entry: ou=People,dc=example,dc=org dn: ou=People,dc=example,dc=org # Search result entry: uid=user.0,ou=People,dc=example,dc=org dn: uid=user.0,ou=People,dc=example,dc=org cn: Aaccf Amar ... Cancel request succeeded
RFC 3062, LDAP Password Modify Extended Operation, defines an extended operation for modifying user passwords that does not depend on the authentication identity, nor on the way passwords are stored.
if (isSupported(PasswordModifyExtendedRequest.OID)) { final String userIdentity = "u:scarter"; final char[] oldPassword = "sprain".toCharArray(); final char[] newPassword = "secret12".toCharArray(); final PasswordModifyExtendedRequest request = Requests.newPasswordModifyExtendedRequest() .setUserIdentity(userIdentity) .setOldPassword(oldPassword) .setNewPassword(newPassword); final PasswordModifyExtendedResult result = connection.extendedRequest(request); if (result.isSuccess()) { System.out.println("Changed password for " + userIdentity); } else { System.err.println(result.getDiagnosticMessage()); } }
OpenDJ directory server supports the password modify operation.
Changed password for u:scarter
Use Start TLS when setting up your connection to protect what your application sends to and receives from the directory server. For an example, read the section on Start TLS & SSL Authentication.
RFC 4532, LDAP "Who am I?" Operation, defines an extended operation that lets your application determine the current authorization ID.
if (isSupported(WhoAmIExtendedRequest.OID)) { final String name = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] password = "hifalutin".toCharArray(); final Result result = connection.bind(name, password); if (result.isSuccess()) { final WhoAmIExtendedRequest request = Requests.newWhoAmIExtendedRequest(); final WhoAmIExtendedResult extResult = connection.extendedRequest(request); if (extResult.isSuccess()) { System.out.println("Authz ID: " + extResult.getAuthorizationID()); } } }
OpenDJ directory server supports the "Who am I?" operation.
Authz ID: dn:uid=bjensen,ou=People,dc=example,dc=com