View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions Copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2013-2015 ForgeRock AS.
15   */
16  
17  package org.forgerock.opendj.examples;
18  
19  import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
20  
21  import org.forgerock.opendj.ldap.Connection;
22  import org.forgerock.opendj.ldap.DN;
23  import org.forgerock.opendj.ldap.LdapException;
24  import org.forgerock.opendj.ldap.LDAPConnectionFactory;
25  import org.forgerock.opendj.ldap.ModificationType;
26  import org.forgerock.opendj.ldap.ResultCode;
27  import org.forgerock.opendj.ldap.SSLContextBuilder;
28  import org.forgerock.opendj.ldap.TrustManagers;
29  import org.forgerock.opendj.ldap.requests.ModifyRequest;
30  import org.forgerock.opendj.ldap.requests.Requests;
31  import org.forgerock.util.Options;
32  
33  import javax.net.ssl.SSLContext;
34  import java.nio.charset.Charset;
35  import java.security.GeneralSecurityException;
36  
37  /**
38   * This command-line client demonstrates how to reset a user password in
39   * Microsoft Active Directory.
40   * <br>
41   * The client takes as arguments the host and port of the Active Directory
42   * server, a flag indicating whether this is a self-reset (user changing own
43   * password) or an administrative reset (administrator changing a password),
44   * the DN and password of the user performing the reset, and target user DN
45   * and new user password.
46   */
47  public final class PasswordResetForAD {
48  
49      /**
50       * Reset a user password in Microsoft Active Directory.
51       * <br>
52       * The connection should be LDAPS, not LDAP, in order to perform the
53       * modification.
54       *
55       * @param args The command line arguments: host, port, "admin"|"self",
56       *             DN, password, targetDN, newPassword
57       */
58      public static void main(final String[] args) {
59          // --- JCite main ---
60          if (args.length != 7) {
61              System.err.println("Usage: host port \"admin\"|\"self\" DN "
62                      + "password targetDN newPassword");
63              System.err.println("For example: ad.example.com 636 admin "
64                      + "cn=administrator,cn=Users,DC=ad,DC=example,DC=com "
65                      + "Secret123 cn=testuser,cn=Users,DC=ad,DC=example,DC=com "
66                      + "NewP4s5w0rd");
67              System.exit(1);
68          }
69          final String host = args[0];
70          final int port = Integer.parseInt(args[1]);
71          final String mode = args[2];
72          final String bindDN = args[3];
73          final String bindPassword = args[4];
74          final String targetDN = args[5];
75          final String newPassword = args[6];
76  
77          Connection connection = null;
78          try {
79              final LDAPConnectionFactory factory =
80                      new LDAPConnectionFactory(host, port, getTrustAllOptions());
81              connection = factory.getConnection();
82              connection.bind(bindDN, bindPassword.toCharArray());
83  
84              ModifyRequest request =
85                      Requests.newModifyRequest(DN.valueOf(targetDN));
86              String passwordAttribute = "unicodePwd";
87  
88              if ("admin".equalsIgnoreCase(mode)) {
89                  // Request modify, replacing the password with the new.
90  
91                  request.addModification(
92                          ModificationType.REPLACE,
93                          passwordAttribute,
94                          encodePassword(newPassword)
95                  );
96              } else if ("self".equalsIgnoreCase(mode)) {
97                  // Request modify, deleting the old password, adding the new.
98  
99                  // The default password policy for Active Directory domain
100                 // controller systems sets minimum password age to 1 (day).
101                 // If you get a constraint violation error when trying this
102                 // example, set this minimum password age to 0 by executing
103                 // cmd.exe as Administrator and entering the following
104                 // command at the prompt:
105                 //
106                 // net accounts /MINPWAGE:0
107 
108                 request.addModification(
109                         ModificationType.DELETE,
110                         passwordAttribute,
111                         encodePassword(bindPassword)
112                 );
113                 request.addModification(
114                         ModificationType.ADD,
115                         passwordAttribute,
116                         encodePassword(newPassword)
117                 );
118             } else {
119                 System.err.println("Mode must be admin or self, not " + mode);
120                 System.exit(1);
121             }
122 
123             connection.modify(request);
124 
125             System.out.println("Successfully changed password for "
126                     + targetDN + " to " + newPassword + ".");
127         } catch (final LdapException e) {
128             System.err.println(e.getMessage());
129             System.exit(e.getResult().getResultCode().intValue());
130         } catch (final GeneralSecurityException e) {
131             System.err.println(e.getMessage());
132             System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
133         } finally {
134             if (connection != null) {
135                 connection.close();
136             }
137         }
138         // --- JCite main ---
139     }
140 
141     // --- JCite encodePassword ---
142     /**
143      * Encode new password in UTF-16LE format for use with Active Directory.
144      *
145      * @param password String representation of the password
146      * @return Byte array containing encoded password
147      */
148     public static byte[] encodePassword(final String password) {
149         return ("\"" + password + "\"").getBytes(Charset.forName("UTF-16LE"));
150     }
151     // --- JCite encodePassword ---
152 
153     /**
154      * For SSL the connection factory needs SSL context options. This
155      * implementation simply trusts all server certificates.
156      */
157     private static Options getTrustAllOptions() throws GeneralSecurityException {
158         Options options = Options.defaultOptions();
159         SSLContext sslContext = new SSLContextBuilder()
160               .setTrustManager(TrustManagers.trustAll()).getSSLContext();
161         options.set(SSL_CONTEXT, sslContext);
162         return options;
163     }
164 
165     /**
166      * Constructor not used.
167      */
168     private PasswordResetForAD() {
169         // Not used.
170     }
171 }