mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Mark Craig
07.07.2012 8420391bb4f07e6559952ca74c4f17dc0b3af986
Part of the dev guide chapter on extended operations
1 files added
4 files modified
388 ■■■■■ changed files
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java 203 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java 32 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm 5 ●●●●● patch | view | raw | blame | history
opendj3/src/main/docbkx/dev-guide/chap-authenticating.xml 2 ●●● patch | view | raw | blame | history
opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml 146 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ExtendedOperations.java
New file
@@ -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.
    }
}
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);
            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 {
                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.
     *
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">
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 &amp; 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
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&lt;String&gt; 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 &amp;&amp; !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 &amp;
  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>