Extended operations allow directory servers to perform processing which isn't defined as part of the core LDAP specification. They provide a means of allowing directory servers to provide enhanced functionality.
The cancel extended operation is defined in RFC 3909, and it is similar to the standard LDAP abandon operation in that it can be used to request that the server stop processing another operation. However, whereas the abandon request does not have a response, and an operation which gets abandoned may never receive a response from the server, the cancel extended operation does have a response, which allows it to receive the result of the cancel operation and also ensures that any operation which is canceled will be sent a result indicating that it has been canceled.
Whenever the server processes a cancel request, it should include one of the following result codes in the response:
Note that cancel requests are only available for use with operations that are being processed asynchronously. See the Processing Asynchronous Operations page for more information.
The following example initiates an asynchronous modify operation and then attempts to cancel it:
Modification mod = new Modification(ModificationType.REPLACE, "description", "This is the new description."); ModifyRequest modifyRequest = new ModifyRequest("dc=example,dc=com", mod); AsyncRequestID asyncRequestID = connection.asyncModify(modifyRequest, myAsyncResultListener); // Assume that we've waited a reasonable amount of time but the modify // hasn't completed yet so we'll try to cancel it. ExtendedResult cancelResult; try { cancelResult = connection.processExtendedOperation( new CancelExtendedRequest(asyncRequestID)); // This doesn't necessarily mean that the operation was successful, since // some kinds of extended operations (like cancel) return non-success // results under normal conditions. } catch (LDAPException le) { // For an extended operation, this generally means that a problem was // encountered while trying to send the request or read the result. cancelResult = new ExtendedResult(le.toLDAPResult()); } switch (cancelResult.getResultCode().intValue()) { case ResultCode.CANCELED_INT_VALUE: // The modify operation was successfully canceled. break; case ResultCode.CANNOT_CANCEL_INT_VALUE: // This indicates that the server isn't capable of canceling that // type of operation. This probably won't happen for this kind of // modify operation, but it could happen for other kinds of operations. break; case ResultCode.TOO_LATE_INT_VALUE: // This indicates that the cancel request was received too late and the // server is intending to process the operation. break; case ResultCode.NO_SUCH_OPERATION_INT_VALUE: // This indicates that the server doesn't know anything about the // operation, most likely because it has already completed. break; default: // This suggests that the operation failed for some other reason. break; }
The password modify extended operation is defined in RFC 3062 and can be used to change the password for a given user. It provides the ability to specify the user's current password for validation purposes, and also provides the ability to request (by not including a new password value) that the server generate a new password on the user's behalf (in which case the new password will be made available through the getGeneratedPassword, getGeneratedPasswordBytes, and getRawGeneratedPassword methods of the corresponding PasswordModifyExtendedResult object).
The password modify extended request also provides a mechanism to identify the user for which to change the password. If this is not provided, then it will change the password for the currently-authenticated user. If it is provided, then the specification is a little unclear as to the format of the value (in particular, it says that the userIdentity field may or may not be an LDAP DN), but server implementations generally support specifying the target userIdentity value as a DN, and many also support specifying it as an authorization ID.
The following example demonstrates the use of the password modify extended operation to change the password for user "uid=test.user,ou=People,dc=example,dc=com" and have the server generate a new password for that user.
PasswordModifyExtendedRequest passwordModifyRequest = new PasswordModifyExtendedRequest( "uid=test.user,ou=People,dc=example,dc=com", // The user to update (String) null, // The current password for the user. (String) null); // The new password. null = server will generate PasswordModifyExtendedResult passwordModifyResult; try { passwordModifyResult = (PasswordModifyExtendedResult) connection.processExtendedOperation(passwordModifyRequest); // This doesn't necessarily mean that the operation was successful, since // some kinds of extended operations return non-success results under // normal conditions. } catch (LDAPException le) { // For an extended operation, this generally means that a problem was // encountered while trying to send the request or read the result. passwordModifyResult = new PasswordModifyExtendedResult( new ExtendedResult(le.toLDAPResult())); } LDAPTestUtils.assertResultCodeEquals(passwordModifyResult, ResultCode.SUCCESS); String serverGeneratedNewPassword = passwordModifyResult.getGeneratedPassword();
The StartTLS extended operation is defined in RFC 4511 and may be used to initiate a secure communication channel over a previously unencrypted connection. That is, it provides the option of allowing the server to perform both clear-text and secure communication over a single port so that it is not necessary to expose separate ports for clear-text and encrypted communication.
The requirements for communicating with a directory server using a StartTLS-encrypted channel are essentially the same as those for establishing an SSL-based connection as described in the Creating and Using LDAP Connections section. You can either use the default SSL context provided by the JVM or specify your own context. In most cases, you will want to use the latter, as it provides greater flexibility and more options for determining whether to trust the server certificate. And as with creating SSL-based connections, the com.unboundid.util.TrustAllSSLSocketFactory can be used to request that the client blindly trust whatever certificate the server presents.
The following code demonstrates the process for using the StartTLS extended operation, using a trust store file to determine whether to trust the certificate presented by the server:
// Create an SSLContext that will be used to perform the cryptographic // processing. SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath)); SSLContext sslContext = sslUtil.createSSLContext(); // Create and process the extended request to secure a connection. StartTLSExtendedRequest startTLSRequest = new StartTLSExtendedRequest(sslContext); ExtendedResult startTLSResult; try { startTLSResult = connection.processExtendedOperation(startTLSRequest); // This doesn't necessarily mean that the operation was successful, since // some kinds of extended operations return non-success results under // normal conditions. } catch (LDAPException le) { // For an extended operation, this generally means that a problem was // encountered while trying to send the request or read the result. startTLSResult = new ExtendedResult(le.toLDAPResult()); } // Make sure that we can use the connection to interact with the server. RootDSE rootDSE = connection.getRootDSE();
The "Who Am I?" extended operation is defined in RFC 4532, and is similar to the authorization identity controls in that it can be used to obtain the current authorization identity for a client connection. The "Who Am I?" request, however, can be used at any time whereas the authorization identity controls can only be used in conjunction with a bind operation.
The following code illustrates the use of the "Who Am I?" extended operation:
// Use the "Who Am I?" extended request to determine the identity of the // currently-authenticated user. WhoAmIExtendedResult whoAmIResult; try { whoAmIResult = (WhoAmIExtendedResult) connection.processExtendedOperation(new WhoAmIExtendedRequest()); // This doesn't necessarily mean that the operation was successful, since // some kinds of extended operations return non-success results under // normal conditions. } catch (LDAPException le) { // For an extended operation, this generally means that a problem was // encountered while trying to send the request or read the result. whoAmIResult = new WhoAmIExtendedResult(new ExtendedResult(le.toLDAPResult())); } LDAPTestUtils.assertResultCodeEquals(whoAmIResult, ResultCode.SUCCESS); String authzID = whoAmIResult.getAuthorizationID(); if (authzID.equals("") || authzID.equals("dn:")) { // The user is authenticated anonymously. } else if (authzID.startsWith("dn:")) { // The DN of the authenticated user should be authzID.substring(3) } else if (authzID.startsWith("u:")) { // The username of the authenticated user should be authzID.substring(2) } else { // The authorization ID isn't in any recognizable format. Perhaps it's // a raw DN or a username? }