From 8b310cc951f4712f7b14ca523f3f856ee1774316 Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Mon, 07 May 2012 19:07:56 +0000
Subject: [PATCH] Part of the dev guide chapter on extended operations
---
opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm | 5
opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java | 203 +++++++++++++++++++++++++++++
opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java | 38 +++++
opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml | 154 ++++++++++++++++++----
opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml | 2
5 files changed, 370 insertions(+), 32 deletions(-)
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java
new file mode 100644
index 0000000..b0a3be0
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java
@@ -0,0 +1,203 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2012 ForgeRock AS
+ *
+ */
+
+package org.forgerock.opendj.examples;
+
+import java.util.Collection;
+
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.RootDSE;
+import org.forgerock.opendj.ldap.requests.PasswordModifyExtendedRequest;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.requests.WhoAmIExtendedRequest;
+import org.forgerock.opendj.ldap.responses.PasswordModifyExtendedResult;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.opendj.ldap.responses.WhoAmIExtendedResult;
+
+/**
+ * This command-line client demonstrates use of LDAP extended operations. The
+ * client takes as arguments the host and port for the directory server, and
+ * expects to find the entries and access control instructions as defined in <a
+ * href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>.
+ *
+ * This client connects as <code>cn=Directory Manager</code> with password
+ * <code>password</code>. Not a best practice; in real code use application
+ * specific credentials to connect, and ensure that your application has access
+ * to use the LDAP extended operations needed.
+ */
+public final class ExtendedOperations {
+
+ /**
+ * Connect to the server, and then try to use some LDAP extended operations.
+ *
+ * @param args
+ * The command line arguments: host, port
+ */
+ public static void main(final String[] args) {
+ if (args.length != 2) {
+ System.err.println("Usage: host port");
+ System.err.println("For example: localhost 1389");
+ System.exit(1);
+ }
+ final String host = args[0];
+ final int port = Integer.parseInt(args[1]);
+
+ final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
+ Connection connection = null;
+
+ try {
+ connection = factory.getConnection();
+ checkSupportedExtendedOperations(connection);
+
+ final String user = "cn=Directory Manager";
+ final char[] password = "password".toCharArray();
+ connection.bind(user, password);
+
+ // Uncomment a method to run one of the examples.
+
+ // For a Cancel Extended request, see the SearchAsync example.
+ //usePasswordModifyExtendedRequest(connection);
+ // For StartTLS, see the authentication examples.
+ useWhoAmIExtendedRequest(connection);
+
+ } catch (ErrorResultException e) {
+ System.err.println(e.getMessage());
+ System.exit(e.getResult().getResultCode().intValue());
+ return;
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ }
+
+ /**
+ * Use the password modify extended request.
+ *
+ * @param connection
+ * Active connection to LDAP server containing <a
+ * href="http://opendj.forgerock.org/Example.ldif"
+ * >Example.ldif</a> content.
+ * @throws ErrorResultException
+ * Operation failed.
+ */
+ static void usePasswordModifyExtendedRequest(Connection connection) throws ErrorResultException {
+ 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());
+ }
+ } else {
+ System.err.println("PasswordModifyExtendedRequest not supported");
+ }
+ }
+
+ /**
+ * Use the Who Am I? extended request.
+ *
+ * @param connection Active connection to LDAP server containing <a
+ * href="http://opendj.forgerock.org/Example.ldif"
+ * >Example.ldif</a> content.
+ * @throws ErrorResultException
+ * Operation failed.
+ */
+ static void useWhoAmIExtendedRequest(Connection connection) throws ErrorResultException {
+ 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());
+ }
+ }
+ } else {
+ System.err.println("WhoAmIExtendedRequest not supported");
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Constructor not used.
+ */
+ private ExtendedOperations() {
+ // Not used.
+ }
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
index ee6afe1..fc55a86 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
@@ -33,15 +33,18 @@
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.FutureResult;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.CancelExtendedRequest;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
@@ -77,7 +80,9 @@
// Bind succeeded: initiate search.
final SearchRequest request =
Requests.newSearchRequest(baseDN, scope, filter, attributes);
- connection.searchAsync(request, null, new SearchResultHandlerImpl());
+ final FutureResult<Result> futureResult =
+ connection.searchAsync(request, null, new SearchResultHandlerImpl());
+ requestID = futureResult.getRequestID();
}
}
@@ -117,8 +122,15 @@
@Override
public synchronized boolean handleEntry(final SearchResultEntry entry) {
try {
- WRITER.writeComment("Search result entry: " + entry.getName().toString());
- WRITER.writeEntry(entry);
+ if (entryCount < 10) {
+ WRITER.writeComment("Search result entry: " + entry.getName().toString());
+ WRITER.writeEntry(entry);
+ ++entryCount;
+ } else { // Cancel the search.
+ 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();
@@ -176,6 +188,26 @@
private static Connection connection = null;
private static int resultCode = 0;
+ static int requestID;
+ static int entryCount = 0;
+
+ private static final class CancelResultHandlerImpl implements ResultHandler<ExtendedResult> {
+
+ @Override
+ public void handleErrorResult(final ErrorResultException error) {
+ System.err.println(error.getMessage());
+ resultCode = error.getResult().getResultCode().intValue();
+ COMPLETION_LATCH.countDown();
+ }
+
+ @Override
+ public void handleResult(final ExtendedResult result) {
+ resultCode = result.getResultCode().intValue();
+ COMPLETION_LATCH.countDown();
+ }
+
+ }
+
/**
* Main method.
*
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
index 9129372..f89acd3 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
@@ -85,6 +85,11 @@
<a href="xref/org/forgerock/opendj/examples/Controls.html">Use LDAP Controls</a>
- illustrates how to use supported LDAP controls
</li>
+ <li>
+ <a href="xref/org/forgerock/opendj/examples/ExtendedOperations.html">Use
+ LDAP Extended Operations</a> - illustrates how to use supported LDAP extended
+ operations
+ </li>
</ul>
</section>
<section name="Get the OpenDJ LDAP SDK Examples">
diff --git a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml
index 94f4f41..ebe02f2 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml
@@ -103,7 +103,7 @@
</section>
<section xml:id="simple-auth-with-starttls-or-ssl">
- <title>Start TLS and SSL Authentication</title>
+ <title>Start TLS & SSL Authentication</title>
<para>Simple authentication involves sending a user name and password to
the directory server. To avoid sending the user name and password in
diff --git a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
index 979db6c..0de550c 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
@@ -64,54 +64,152 @@
supportedExtension: 1.3.6.1.4.1.4203.1.11.3
supportedExtension: 1.3.6.1.4.1.1466.20037</screen>
- <para>The following excerpt shows the Java equivalent of the preceding
- command.</para>
+ <para>The following excerpt shows code to check for supported extended
+ operations.</para>
<programlisting language="java">
-final LDAPConnectionFactory factory = new LDAPConnectionFactory(
- host, port);
-Connection connection = null;
+/**
+ * Controls supported by the LDAP server.
+ */
+private static Collection<String> extendedOperations;
-try
-{
- connection = factory.getConnection();
+/**
+ * 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();
+}
- // Perform an anonymous search on the root DSE.
- final SearchResultEntry entry = connection.searchSingleEntry(
- "", // DN is "" for root DSE.
- SearchScope.BASE_OBJECT, // Read only the root DSE.
- "objectclass=*", // Every object matches this filter.
- "supportedExtension"); // Check supported extended operations.
-
- final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
- writer.writeComment("Supported extended ops for server " + host + ":" + port);
- if (entry != null) writer.writeEntry(entry);
- writer.flush();
-}</programlisting>
+/**
+ * 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;
+}
+</programlisting>
</section>
<section xml:id="use-cancel-extended-operation">
<title>Cancel Extended Operation</title>
- <para>TODO</para>
+
+ <para>RFC 3909, <link xlink:href="http://tools.ietf.org/html/rfc3909"
+ xlink:show="new"><citetitle>LDAP Cancel Operation</citetitle></link>, defines
+ an extended operation that lets you cancel an operation in progress and get
+ an indication of the outcome.</para>
+
+ <para>This cancel extended requests uses the request ID of operation you
+ want to cancel, and so therefore works with asynchronous searches and
+ updates.</para>
+
+ <programlisting language="java">
+TODO
+</programlisting>
+
+ <para>OpenDJ directory server supports the cancel operation.</para>
+
+ <programlisting>TODO</programlisting>
</section>
-
+
<section xml:id="use-password-modify-extended-operation">
<title>Password Modify Extended Operation</title>
- <para>TODO</para>
+
+ <para>RFC 3062, <link xlink:href="http://tools.ietf.org/html/rfc3062"
+ xlink:show="new"><citetitle>LDAP Password Modify Extended
+ Operation</citetitle></link>, defines an extended operation for modifying
+ user passwords that does not depend on the authentication identity, nor on
+ the way passwords are stored.</para>
+
+ <programlisting language="java">
+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());
+ }
+}
+</programlisting>
+
+ <para>OpenDJ directory server supports the password modify operation.</para>
+
+ <programlisting>Changed password for u:scarter</programlisting>
</section>
-
+
<section xml:id="use-starttls-extended-operation">
<title>Start TLS Extended Operation</title>
- <para>TODO</para>
+
+ <para>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 <link
+ xlink:href="dev-guide#simple-auth-with-starttls-or-ssl"
+ xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Start TLS &
+ SSL Authentication</citetitle></link>.</para>
</section>
-
+
<section xml:id="use-who-am-i-extended-operation">
<title>Who am I? Extended Operation</title>
- <para>TODO</para>
+
+ <para>RFC 4532, <link xlink:href="http://tools.ietf.org/html/rfc4532"
+ xlink:show="new"><citetitle>LDAP "Who am I?" Operation</citetitle></link>,
+ defines an extended operation that lets your application determine the
+ current authorization ID.</para>
+
+ <programlisting language="java">
+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());
+ }
+ }
+}
+</programlisting>
+
+ <para>OpenDJ directory server supports the "Who am I?" operation.</para>
+
+ <programlisting
+ >Authz ID: dn:uid=bjensen,ou=People,dc=example,dc=com</programlisting>
</section>
-
+
<section xml:id="custom-extended-operation">
<title>Custom Extended Operations</title>
+
<para>TODO</para>
</section>
</chapter>
--
Gitblit v1.10.0