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); 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. * 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 & 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<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>