From 110395c2a024a5481bc51c0f0d8cb8ff65be5710 Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Wed, 04 Sep 2013 15:53:52 +0000
Subject: [PATCH] CR-2258 Fix for OPENDJ-1113: Provide an example for resetting passwords in AD
---
opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm | 5 +
opendj3/src/main/docbkx/dev-guide/chap-writing.xml | 21 +++++
opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml | 3
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java | 180 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 209 insertions(+), 0 deletions(-)
diff --git a/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java
new file mode 100644
index 0000000..6b5b353
--- /dev/null
+++ b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java
@@ -0,0 +1,180 @@
+/*
+ * 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 2013 ForgeRock AS
+ *
+ */
+
+package org.forgerock.opendj.examples;
+
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.LDAPOptions;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SSLContextBuilder;
+import org.forgerock.opendj.ldap.TrustManagers;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.Requests;
+
+import javax.net.ssl.SSLContext;
+import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
+
+/**
+ * This command-line client demonstrates how to reset a user password in
+ * Microsoft Active Directory.
+ * <p>
+ * The client takes as arguments the host and port of the Active Directory
+ * server, a flag indicating whether this is a self-reset (user changing own
+ * password) or an administrative reset (administrator changing a password),
+ * the DN and password of the user performing the reset, and target user DN
+ * and new user password.
+ */
+public final class PasswordResetForAD {
+
+ /**
+ * Reset a user password in Microsoft Active Directory.
+ * <p>
+ * The connection should be LDAPS, not LDAP, in order to perform the
+ * modification.
+ *
+ * @param args The command line arguments: host, port, "admin"|"self",
+ * DN, password, targetDN, newPassword
+ */
+ public static void main(final String[] args) {
+ // --- JCite main ---
+ if (args.length != 7) {
+ System.err.println("Usage: host port \"admin\"|\"self\" DN "
+ + "password targetDN newPassword");
+ System.err.println("For example: ad.example.com 636 admin "
+ + "cn=administrator,cn=Users,DC=ad,DC=example,DC=com "
+ + "Secret123 cn=testuser,cn=Users,DC=ad,DC=example,DC=com "
+ + "NewP4s5w0rd");
+ System.exit(1);
+ }
+ final String host = args[0];
+ final int port = Integer.parseInt(args[1]);
+ final String mode = args[2];
+ final String bindDN = args[3];
+ final String bindPassword = args[4];
+ final String targetDN = args[5];
+ final String newPassword = args[6];
+
+ Connection connection = null;
+ try {
+ final LDAPConnectionFactory factory =
+ new LDAPConnectionFactory(host, port, getTrustAllOptions());
+ connection = factory.getConnection();
+ connection.bind(bindDN, bindPassword.toCharArray());
+
+ ModifyRequest request =
+ Requests.newModifyRequest(DN.valueOf(targetDN));
+ String passwordAttribute = "unicodePwd";
+
+ if (mode.equalsIgnoreCase("admin")) {
+ // Request modify, replacing the password with the new.
+
+ request.addModification(
+ ModificationType.REPLACE,
+ passwordAttribute,
+ encodePassword(newPassword)
+ );
+ } else if (mode.equalsIgnoreCase("self")) {
+ // Request modify, deleting the old password, adding the new.
+
+ // The default password policy for Active Directory domain
+ // controller systems sets minimum password age to 1 (day).
+ // If you get a constraint violation error when trying this
+ // example, set this minimum password age to 0 by executing
+ // cmd.exe as Administrator and entering the following
+ // command at the prompt:
+ //
+ // net accounts /MINPWAGE:0
+
+ request.addModification(
+ ModificationType.DELETE,
+ passwordAttribute,
+ encodePassword(bindPassword)
+ );
+ request.addModification(
+ ModificationType.ADD,
+ passwordAttribute,
+ encodePassword(newPassword)
+ );
+ } else {
+ System.err.println("Mode must be admin or self, not " + mode);
+ System.exit(1);
+ }
+
+ connection.modify(request);
+
+ System.out.println("Successfully changed password for "
+ + targetDN + " to " + newPassword + ".");
+ } catch (final ErrorResultException e) {
+ System.err.println(e.getMessage());
+ System.exit(e.getResult().getResultCode().intValue());
+ } catch (final GeneralSecurityException e) {
+ System.err.println(e.getMessage());
+ System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ // --- JCite main ---
+ }
+
+ // --- JCite encodePassword ---
+ /**
+ * Encode new password in UTF-16LE format for use with Active Directory.
+ *
+ * @param password String representation of the password
+ * @return Byte array containing encoded password
+ */
+ public static byte[] encodePassword(final String password) {
+ return ("\"" + password + "\"").getBytes(Charset.forName("UTF-16LE"));
+ }
+ // --- JCite encodePassword ---
+
+ /**
+ * For SSL the connection factory needs SSL context options. This
+ * implementation simply trusts all server certificates.
+ */
+ private static LDAPOptions getTrustAllOptions() throws GeneralSecurityException {
+ LDAPOptions lo = new LDAPOptions();
+ SSLContext sslContext =
+ new SSLContextBuilder().setTrustManager(TrustManagers.trustAll())
+ .getSSLContext();
+ lo.setSSLContext(sslContext);
+ return lo;
+ }
+
+ /**
+ * Constructor not used.
+ */
+ private PasswordResetForAD() {
+ // Not used.
+ }
+}
diff --git a/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm b/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
index eee539b..090ad7c 100644
--- a/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
+++ b/opendj3/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
@@ -106,6 +106,11 @@
<a href="xref/org/forgerock/opendj/examples/GetADChangeNotifications.html">Use <code>GenericControl</code></a>
- illustrates how to use <code>GenericControl</code> to get change notifications from Active Directory
</li>
+ <li>
+ <a href="xref/org/forgerock/opendj/examples/PasswordResetForAD.html">Reset AD user password</a>
+ - illustrates how to reset a user password in Active Directory as Administrator,
+ or change the password as the user
+ </li>
</ul>
</section>
<section name="Get the OpenDJ LDAP SDK Examples">
diff --git a/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml b/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
index 1d00f76..629dc8c 100644
--- a/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
+++ b/opendj3/src/main/docbkx/dev-guide/chap-extended-ops.xml
@@ -171,6 +171,9 @@
<primary>Modifications</primary>
<secondary>Password modify</secondary>
</indexterm>
+ <indexterm>
+ <primary>Passwords</primary>
+ </indexterm>
<para>RFC 3062, <link xlink:href="http://tools.ietf.org/html/rfc3062"
xlink:show="new"><citetitle>LDAP Password Modify Extended
diff --git a/opendj3/src/main/docbkx/dev-guide/chap-writing.xml b/opendj3/src/main/docbkx/dev-guide/chap-writing.xml
index 47ba251..edc8154 100644
--- a/opendj3/src/main/docbkx/dev-guide/chap-writing.xml
+++ b/opendj3/src/main/docbkx/dev-guide/chap-writing.xml
@@ -164,6 +164,27 @@
entry, but instead by specifying individual changes. See a demonstration
of this technique in <xref linkend="updating-static-groups" />.</para>
+ <indexterm>
+ <primary>Passwords</primary>
+ </indexterm>
+
+ <para>You can also construct a <literal>ModifyRequest</literal> for example
+ to change a user password in Active Directory, as demonstrated in the
+ following excerpt. When working with OpenDJ directory server, consider using
+ the LDAP Password Modify extended operation instead as shown in the section,
+ <link xlink:show="new" xlink:href="dev-guide#use-password-modify-extended-operation"
+ xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Password Modify
+ Extended Operation</citetitle></link>.</para>
+
+ <programlisting language="java"
+ >[jcp:org.forgerock.opendj.examples.PasswordResetForAD:--- JCite main ---]</programlisting>
+
+ <para>To make the modification, the example connects to Active Directory over
+ LDAPS, and provides the password value in UTF-16LE format.</para>
+
+ <programlisting language="java"
+ >[jcp:org.forgerock.opendj.examples.PasswordResetForAD:--- JCite encodePassword ---]</programlisting>
+
<para>If the modifications are easier to construct in LDIF, you can write the
LDIF to the directory server as shown in the chapter, <link xlink:show="new"
xlink:href="dev-guide#chap-ldif" xlink:role="http://docbook.org/xlink/role/olink"
--
Gitblit v1.10.0