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

Mark Craig
04.53.2013 110395c2a024a5481bc51c0f0d8cb8ff65be5710
CR-2258 Fix for OPENDJ-1113: Provide an example for resetting passwords in AD
1 files added
3 files modified
209 ■■■■■ changed files
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java 180 ●●●●● 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-extended-ops.xml 3 ●●●●● patch | view | raw | blame | history
opendj3/src/main/docbkx/dev-guide/chap-writing.xml 21 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java
New file
@@ -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.
    }
}
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">
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
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"