All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.tools.LDAPPasswordModify Maven / Gradle / Ivy

/*
 * Copyright 2020 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2020 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2020 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.tools;



import java.io.File;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPExtendedOperationException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.LDAPURL;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.RootDSE;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedRequest;
import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedResult;
import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedRequest;
import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedResult;
import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel;
import com.unboundid.ldap.sdk.unboundidds.controls.
            AssuredReplicationRemoteLevel;
import com.unboundid.ldap.sdk.unboundidds.controls.
            AssuredReplicationRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetAuthorizationEntryRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetUserResourceLimitsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            OperationPurposeRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            PasswordPolicyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            PasswordValidationDetailsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionPostConnectProcessor;
import com.unboundid.util.Debug;
import com.unboundid.util.LDAPCommandLineTool;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.PasswordReader;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.ControlArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.DurationArgument;
import com.unboundid.util.args.FileArgument;
import com.unboundid.util.args.IntegerArgument;
import com.unboundid.util.args.StringArgument;

import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;



/**
 * This class provides an implementation of an LDAP command-line tool that may
 * be used to change passwords in a directory server.  Three types of password
 * changes are supported:  the password modify extended operation (as described
 * in RFC 3062), a standard
 * LDAP modify operation that targets an attribute like userPassword, or an
 * Active Directory-specific password change that uses an LDAP modify operation
 * to replace the value of the unicodePwd attribute with a value that is the
 * password surrounded by quotation marks and encoded with UTF-16-LE.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class LDAPPasswordModify extends LDAPCommandLineTool implements UnsolicitedNotificationHandler { /** * The column at which output should be wrapped. */ private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; /** * The assured replication local level value that indicates no assurance is * needed. */ @NotNull private static final String ASSURED_REPLICATION_LOCAL_LEVEL_NONE = "none"; /** * The assured replication local level value that indicates the change should * be received by at least one other local server. */ @NotNull private static final String ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER = "received-any-server"; /** * The assured replication local level value that indicates the change should * be processed by all available local servers. */ @NotNull private static final String ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS = "processed-all-servers"; /** * The assured replication remote level value that indicates no assurance is * needed. */ @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_NONE = "none"; /** * The assured replication remote level value that indicates the change should * be received by at least one other remote server in at least one remote * location. */ @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION = "received-any-remote-location"; /** * The assured replication remote level value that indicates the change should * be received by at least one other remote server in every remote * location. */ @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS = "received-all-remote-locations"; /** * The assured replication remote level value that indicates the change should * be processed by all available remote servers in all locations. */ @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS = "processed-all-remote-servers"; /** * The password change method that will be used to indicate that the password * modify extended operation should be used. */ @NotNull private static final String PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP = "password-modify-extended-operation"; /** * The password change method that will be used to indicate that a regular * LDAP modify operation should be used. */ @NotNull private static final String PASSWORD_CHANGE_METHOD_LDAP_MOD = "ldap-modify"; /** * The password change method that will be used to indicate that an * Active Directory-specific operation should be used. */ @NotNull private static final String PASSWORD_CHANGE_METHOD_AD = "active-directory"; /** * The long identifier for the {@link LDAPCommandLineTool} argument used to * specify the bind DN to use when authenticating to the directory server. */ @NotNull private static final String BIND_DN_ARGUMENT_LONG_IDENTIFIER = "bindDN"; /** * The name of the default attribute that will be assumed to hold the password * in most directory servers. */ @NotNull private static final String DEFAULT_PASSWORD_ATTRIBUTE = "userPassword"; /** * The name of the attribute that Active Directory uses to hold the password. */ @NotNull private static final String AD_PASSWORD_ATTRIBUTE = "unicodePwd"; /** * The names of the attributes that will be used when searching for an entry * from its username in most directory servers. */ @NotNull private static final List DEFAULT_USERNAME_ATTRIBUTES = Collections.singletonList("uid"); /** * The names of the attributes that will be used when searching for an entry * from its username in an Active Directory server. */ @NotNull private static final List AD_USERNAME_ATTRIBUTES = Collections.unmodifiableList(Arrays.asList("samAccountName", "userPrincipalName")); /** * The OID base that has been assigned to Microsoft. */ @NotNull private static final String MICROSOFT_BASE_OBJECT_IDENTIFIER = "1.2.840.113556"; // A reference to the completion message to return for this tool. @NotNull private final AtomicReference completionMessage; // A reference to the argument parser for this tool. @Nullable private ArgumentParser argumentParser; // The supported command-line arguments. @Nullable private BooleanArgument followReferrals; @Nullable private BooleanArgument generateClientSideNewPassword; @Nullable private BooleanArgument getPasswordValidationDetails; @Nullable private BooleanArgument getUserResourceLimits; @Nullable private BooleanArgument noOperation; @Nullable private BooleanArgument promptForCurrentPassword; @Nullable private BooleanArgument promptForNewPassword; @Nullable private BooleanArgument provideBindDNAsUserIdentity; @Nullable private BooleanArgument purgeCurrentPassword; @Nullable private BooleanArgument retireCurrentPassword; @Nullable private BooleanArgument scriptFriendly; @Nullable private BooleanArgument useAdministrativeSession; @Nullable private BooleanArgument useAssuredReplication; @Nullable private BooleanArgument useAuthorizationIdentityControl; @Nullable private BooleanArgument usePasswordPolicyControlOnBind; @Nullable private BooleanArgument usePasswordPolicyControlOnUpdate; @Nullable private BooleanArgument verbose; @Nullable private ControlArgument bindControl; @Nullable private ControlArgument updateControl; @Nullable private DNArgument searchBaseDN; @Nullable private DurationArgument assuredReplicationTimeout; @Nullable private FileArgument currentPasswordFile; @Nullable private FileArgument newPasswordFile; @Nullable private IntegerArgument generatedPasswordLength; @Nullable private StringArgument assuredReplicationLocalLevel; @Nullable private StringArgument assuredReplicationRemoteLevel; @Nullable private StringArgument currentPassword; @Nullable private StringArgument generatedPasswordCharacterSet; @Nullable private StringArgument getAuthorizationEntryAttribute; @Nullable private StringArgument newPassword; @Nullable private StringArgument operationPurpose; @Nullable private StringArgument passwordAttribute; @Nullable private StringArgument passwordChangeMethod; @Nullable private StringArgument passwordUpdateBehavior; @Nullable private StringArgument userIdentity; @Nullable private StringArgument usernameAttribute; /** * Invokes this tool with the provided set of arguments. The default standard * output and error streams will be used. * * @param args The command-line arguments provided to this program. */ public static void main(@NotNull final String... args) { final ResultCode resultCode = main(System.out, System.err, args); if (resultCode != ResultCode.SUCCESS) { System.exit(resultCode.intValue()); } } /** * Invokes this tool with the provided set of arguments, and using the * provided streams for standard output and error. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. * @param args The command-line arguments provided to this program. * * @return The result code obtained when running the tool. Any result code * other than {@link ResultCode#SUCCESS} indicates an error. */ @NotNull() public static ResultCode main(@Nullable final OutputStream out, @Nullable final OutputStream err, @NotNull final String... args) { final LDAPPasswordModify tool = new LDAPPasswordModify(out, err); return tool.runTool(args); } /** * Creates a new instance of this tool with the provided output and error * streams. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. */ public LDAPPasswordModify(@Nullable final OutputStream out, @Nullable final OutputStream err) { super(out, err); completionMessage = new AtomicReference<>(); argumentParser = null; followReferrals = null; generateClientSideNewPassword = null; getPasswordValidationDetails = null; getUserResourceLimits = null; noOperation = null; promptForCurrentPassword = null; promptForNewPassword = null; provideBindDNAsUserIdentity = null; purgeCurrentPassword = null; retireCurrentPassword = null; scriptFriendly = null; useAdministrativeSession = null; useAssuredReplication = null; useAuthorizationIdentityControl = null; usePasswordPolicyControlOnBind = null; usePasswordPolicyControlOnUpdate = null; verbose = null; bindControl = null; updateControl = null; searchBaseDN = null; assuredReplicationTimeout = null; currentPasswordFile = null; newPasswordFile = null; generatedPasswordLength = null; assuredReplicationLocalLevel = null; assuredReplicationRemoteLevel = null; currentPassword = null; generatedPasswordCharacterSet = null; getAuthorizationEntryAttribute = null; newPassword = null; operationPurpose = null; passwordAttribute = null; passwordChangeMethod = null; passwordUpdateBehavior = null; userIdentity = null; usernameAttribute = null; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolName() { return "ldappasswordmodify"; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolDescription() { return INFO_PWMOD_TOOL_DESCRIPTION_1.get(); } /** * {@inheritDoc} */ @Override() @NotNull() public List getAdditionalDescriptionParagraphs() { return Collections.unmodifiableList(Arrays.asList( INFO_PWMOD_TOOL_DESCRIPTION_2.get(), INFO_PWMOD_TOOL_DESCRIPTION_3.get(), INFO_PWMOD_TOOL_DESCRIPTION_4.get())); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolVersion() { return Version.NUMERIC_VERSION_STRING; } /** * {@inheritDoc} */ @Override() public boolean supportsInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean defaultsToInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean supportsPropertiesFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsOutputFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsAuthentication() { return true; } /** * {@inheritDoc} */ @Override() protected boolean defaultToPromptForBindPassword() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsSASLHelp() { return true; } /** * {@inheritDoc} */ @Override() protected boolean includeAlternateLongIdentifiers() { return true; } /** * {@inheritDoc} */ @Override() @NotNull() protected List getBindControls() { final List bindControls = new ArrayList<>(10); if (bindControl.isPresent()) { bindControls.addAll(bindControl.getValues()); } if (useAuthorizationIdentityControl.isPresent()) { bindControls.add(new AuthorizationIdentityRequestControl(false)); } if (getAuthorizationEntryAttribute.isPresent()) { bindControls.add(new GetAuthorizationEntryRequestControl(true, true, getAuthorizationEntryAttribute.getValues())); } if (getUserResourceLimits.isPresent()) { bindControls.add(new GetUserResourceLimitsRequestControl()); } if (usePasswordPolicyControlOnBind.isPresent()) { bindControls.add(new PasswordPolicyRequestControl()); } return bindControls; } /** * {@inheritDoc} */ @Override() protected boolean supportsMultipleServers() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsSSLDebugging() { return true; } /** * {@inheritDoc} */ @Override() @NotNull() public LDAPConnectionOptions getConnectionOptions() { final LDAPConnectionOptions options = new LDAPConnectionOptions(); options.setUseSynchronousMode(true); options.setFollowReferrals(followReferrals.isPresent()); options.setUnsolicitedNotificationHandler(this); options.setResponseTimeoutMillis(0L); return options; } /** * {@inheritDoc} */ @Override() protected boolean logToolInvocationByDefault() { return true; } /** * {@inheritDoc} */ @Override() @Nullable() protected String getToolCompletionMessage() { return completionMessage.get(); } /** * {@inheritDoc} */ @Override() public void addNonLDAPArguments(@NotNull final ArgumentParser parser) throws ArgumentException { argumentParser = parser; // Authorization identity arguments. userIdentity = new StringArgument('a', "userIdentity", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_DN_OR_AUTHZID.get(), INFO_PWMOD_ARG_DESC_USER_IDENTITY.get()); userIdentity.addLongIdentifier("user-identity", true); userIdentity.addLongIdentifier("userDN", true); userIdentity.addLongIdentifier("user-dn", true); userIdentity.addLongIdentifier("authzID", true); userIdentity.addLongIdentifier("authz-id", true); userIdentity.addLongIdentifier("authorizationID", true); userIdentity.addLongIdentifier("authorization-id", true); userIdentity.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); parser.addArgument(userIdentity); provideBindDNAsUserIdentity = new BooleanArgument('A', "provideBindDNAsUserIdentity", 1, INFO_PWMOD_ARG_DESC_PROVIDE_BIND_DN_AS_USER_IDENTITY.get()); provideBindDNAsUserIdentity.addLongIdentifier( "provide-bind-dn-as-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier( "provideBindDNForUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier( "provide-bind-dn-for-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("provideDNAsUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier("provide-dn-as-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("provideDNForUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier( "provide-dn-for-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("useBindDNAsUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier( "use-bind-dn-as-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("useBindDNForUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier( "use-bind-dn-for-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("useDNAsUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier("use-dn-as-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("useDNForUserIdentity", true); provideBindDNAsUserIdentity.addLongIdentifier("use-dn-for-user-identity", true); provideBindDNAsUserIdentity.addLongIdentifier("useBindDNForAuthzID", true); provideBindDNAsUserIdentity.addLongIdentifier("use-bind-dn-for-authz-id", true); provideBindDNAsUserIdentity.addLongIdentifier("provideDNForAuthzID", true); provideBindDNAsUserIdentity.addLongIdentifier("provide-dn-for-authz-id", true); provideBindDNAsUserIdentity.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); parser.addArgument(provideBindDNAsUserIdentity); usernameAttribute = new StringArgument(null, "usernameAttribute", false, 0, INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), INFO_PWMOD_ARG_DESC_USERNAME_ATTRIBUTE.get()); usernameAttribute.addLongIdentifier("username-attribute", true); usernameAttribute.addLongIdentifier("usernameAttr", true); usernameAttribute.addLongIdentifier("username-attr", true); usernameAttribute.addLongIdentifier("userIDAttribute", true); usernameAttribute.addLongIdentifier("user-id-attribute", true); usernameAttribute.addLongIdentifier("userIDAttr", true); usernameAttribute.addLongIdentifier("user-id-attr", true); usernameAttribute.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); parser.addArgument(usernameAttribute); searchBaseDN = new DNArgument('b', "searchBaseDN", false, 0, null, INFO_PWMOD_ARG_DESC_SEARCH_BASE_DN.get(), DN.NULL_DN); searchBaseDN.addLongIdentifier("search-base-dn", true); searchBaseDN.addLongIdentifier("baseDN", true); searchBaseDN.addLongIdentifier("base-dn", true); searchBaseDN.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); parser.addArgument(searchBaseDN); // New password arguments. newPassword = new StringArgument('n', "newPassword", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_PASSWORD.get(), INFO_PWMOD_ARG_DESC_NEW_PASSWORD.get()); newPassword.addLongIdentifier("new-password", true); newPassword.addLongIdentifier("newPW", true); newPassword.addLongIdentifier("new-pw", true); newPassword.addLongIdentifier("new", true); newPassword.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(newPassword); newPasswordFile = new FileArgument('N', "newPasswordFile", false, 1, null, INFO_PWMOD_ARG_DESC_NEW_PASSWORD_FILE.get(), true, true, true, false); newPasswordFile.addLongIdentifier("new-password-file", true); newPasswordFile.addLongIdentifier("newPWFile", true); newPasswordFile.addLongIdentifier("new-pw-file", true); newPasswordFile.addLongIdentifier("newFile", true); newPasswordFile.addLongIdentifier("new-file", true); newPasswordFile.addLongIdentifier("newPasswordPath", true); newPasswordFile.addLongIdentifier("new-password-path", true); newPasswordFile.addLongIdentifier("newPWPath", true); newPasswordFile.addLongIdentifier("new-pw-path", true); newPasswordFile.addLongIdentifier("newPath", true); newPasswordFile.addLongIdentifier("new-path", true); newPasswordFile.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(newPasswordFile); promptForNewPassword = new BooleanArgument(null, "promptForNewPassword", 1, INFO_PWMOD_ARG_DESC_PROMPT_FOR_NEW_PASSWORD.get()); promptForNewPassword.addLongIdentifier("prompt-for-new-password", true); promptForNewPassword.addLongIdentifier("promptForNewPW", true); promptForNewPassword.addLongIdentifier("prompt-for-new-pw", true); promptForNewPassword.addLongIdentifier("promptForNew", true); promptForNewPassword.addLongIdentifier("prompt-for-new", true); promptForNewPassword.addLongIdentifier("promptNew", true); promptForNewPassword.addLongIdentifier("prompt-new", true); promptForNewPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(promptForNewPassword); generateClientSideNewPassword = new BooleanArgument(null, "generateClientSideNewPassword", 1, INFO_PWMOD_ARG_DESC_GENERATE_CLIENT_SIDE_NEW_PASSWORD.get()); generateClientSideNewPassword.addLongIdentifier( "generate-client-side-new-password", true); generateClientSideNewPassword.addLongIdentifier("generateClientSideNewPW", true); generateClientSideNewPassword.addLongIdentifier( "generate-client-side-new-pw", true); generateClientSideNewPassword.addLongIdentifier("generateNewPassword", true); generateClientSideNewPassword.addLongIdentifier("generate-new-password", true); generateClientSideNewPassword.addLongIdentifier("generateNewPW", true); generateClientSideNewPassword.addLongIdentifier("generate-new-pw", true); generateClientSideNewPassword.addLongIdentifier("generatePassword", true); generateClientSideNewPassword.addLongIdentifier("generate-password", true); generateClientSideNewPassword.addLongIdentifier("generatePW", true); generateClientSideNewPassword.addLongIdentifier("generate-pw", true); generateClientSideNewPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(generateClientSideNewPassword); generatedPasswordLength = new IntegerArgument(null, "generatedPasswordLength", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_LENGTH.get(), INFO_PWMOD_ARG_DESC_GENERATED_PASSWORD_LENGTH.get(), 1, Integer.MAX_VALUE, 12); generatedPasswordLength.addLongIdentifier("generated-password-length", true); generatedPasswordLength.addLongIdentifier("generatedPWLength", true); generatedPasswordLength.addLongIdentifier("generated-pw-length", true); generatedPasswordLength.addLongIdentifier("passwordLength", true); generatedPasswordLength.addLongIdentifier("password-length", true); generatedPasswordLength.addLongIdentifier("pwLength", true); generatedPasswordLength.addLongIdentifier("pw-length", true); generatedPasswordLength.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(generatedPasswordLength); generatedPasswordCharacterSet = new StringArgument(null, "generatedPasswordCharacterSet", false, 0, INFO_PWMOD_ARG_PLACEHOLDER_CHARS.get(), INFO_PWMOD_ARG_DESC_GENERATED_PASSWORD_CHARACTER_SET.get(), null, Collections.unmodifiableList(Arrays.asList( "abcdefghijmnopqrstuvwxyz", // Note that some letters and "ABCDEFGHJLMNPQRSTUVWXYZ", // digits are missing in an attempt "23456789", // to avoid ambiguous characters. "@#-_=+."))); generatedPasswordCharacterSet.addLongIdentifier( "generated-password-character-set", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharacterSet", true); generatedPasswordCharacterSet.addLongIdentifier( "generated-pw-character-set", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPasswordCharSet", true); generatedPasswordCharacterSet.addLongIdentifier( "generated-password-char-set", true); generatedPasswordCharacterSet.addLongIdentifier( "generated-password-charset", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharSet", true); generatedPasswordCharacterSet.addLongIdentifier("generated-pw-char-set", true); generatedPasswordCharacterSet.addLongIdentifier("generated-pw-charset", true); generatedPasswordCharacterSet.addLongIdentifier( "generatedPasswordCharacters", true); generatedPasswordCharacterSet.addLongIdentifier( "generated-password-characters", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharacters", true); generatedPasswordCharacterSet.addLongIdentifier("generated-pw-characters", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPasswordChars", true); generatedPasswordCharacterSet.addLongIdentifier("generated-password-chars", true); generatedPasswordCharacterSet.addLongIdentifier("generatedPWChars", true); generatedPasswordCharacterSet.addLongIdentifier("generated-pw-chars", true); generatedPasswordCharacterSet.addLongIdentifier("passwordCharacters", true); generatedPasswordCharacterSet.addLongIdentifier("password-characters", true); generatedPasswordCharacterSet.addLongIdentifier("pwCharacters", true); generatedPasswordCharacterSet.addLongIdentifier("pw-characters", true); generatedPasswordCharacterSet.addLongIdentifier("passwordCharacterSet", true); generatedPasswordCharacterSet.addLongIdentifier("password-character-set", true); generatedPasswordCharacterSet.addLongIdentifier("pwCharacterSet", true); generatedPasswordCharacterSet.addLongIdentifier("pw-character-set", true); generatedPasswordCharacterSet.addLongIdentifier("passwordCharSet", true); generatedPasswordCharacterSet.addLongIdentifier("password-charset", true); generatedPasswordCharacterSet.addLongIdentifier("password-char-set", true); generatedPasswordCharacterSet.addLongIdentifier("pwCharSet", true); generatedPasswordCharacterSet.addLongIdentifier("pw-charset", true); generatedPasswordCharacterSet.addLongIdentifier("pw-char-set", true); generatedPasswordCharacterSet.addLongIdentifier("passwordChars", true); generatedPasswordCharacterSet.addLongIdentifier("password-chars", true); generatedPasswordCharacterSet.addLongIdentifier("pw-chars", true); generatedPasswordCharacterSet.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); parser.addArgument(generatedPasswordCharacterSet); // Current password arguments. currentPassword = new StringArgument('c', "currentPassword", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_PASSWORD.get(), INFO_PWMOD_ARG_DESC_CURRENT_PASSWORD.get()); currentPassword.addLongIdentifier("current-password", true); currentPassword.addLongIdentifier("currentPW", true); currentPassword.addLongIdentifier("current-pw", true); currentPassword.addLongIdentifier("current", true); currentPassword.addLongIdentifier("oldPassword", true); currentPassword.addLongIdentifier("old-password", true); currentPassword.addLongIdentifier("oldPW", true); currentPassword.addLongIdentifier("old-pw", true); currentPassword.addLongIdentifier("old", true); currentPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); parser.addArgument(currentPassword); currentPasswordFile = new FileArgument('C', "currentPasswordFile", false, 1, null, INFO_PWMOD_ARG_DESC_CURRENT_PASSWORD_FILE.get(), true, true, true, false); currentPasswordFile.addLongIdentifier("current-password-file", true); currentPasswordFile.addLongIdentifier("currentPWFile", true); currentPasswordFile.addLongIdentifier("current-pw-file", true); currentPasswordFile.addLongIdentifier("currentFile", true); currentPasswordFile.addLongIdentifier("current-file", true); currentPasswordFile.addLongIdentifier("currentPasswordPath", true); currentPasswordFile.addLongIdentifier("current-password-path", true); currentPasswordFile.addLongIdentifier("currentPWPath", true); currentPasswordFile.addLongIdentifier("current-pw-path", true); currentPasswordFile.addLongIdentifier("currentPath", true); currentPasswordFile.addLongIdentifier("current-path", true); currentPasswordFile.addLongIdentifier("oldPasswordFile", true); currentPasswordFile.addLongIdentifier("old-password-file", true); currentPasswordFile.addLongIdentifier("oldPWFile", true); currentPasswordFile.addLongIdentifier("old-pw-file", true); currentPasswordFile.addLongIdentifier("oldFile", true); currentPasswordFile.addLongIdentifier("old-file", true); currentPasswordFile.addLongIdentifier("oldPasswordPath", true); currentPasswordFile.addLongIdentifier("old-password-path", true); currentPasswordFile.addLongIdentifier("oldPWPath", true); currentPasswordFile.addLongIdentifier("old-pw-path", true); currentPasswordFile.addLongIdentifier("oldPath", true); currentPasswordFile.addLongIdentifier("old-path", true); currentPasswordFile.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); parser.addArgument(currentPasswordFile); promptForCurrentPassword = new BooleanArgument(null, "promptForCurrentPassword", 1, INFO_PWMOD_ARG_DESC_PROMPT_FOR_CURRENT_PASSWORD.get()); promptForCurrentPassword.addLongIdentifier("prompt-for-current-password", true); promptForCurrentPassword.addLongIdentifier("promptForCurrentPW", true); promptForCurrentPassword.addLongIdentifier("prompt-for-current-pw", true); promptForCurrentPassword.addLongIdentifier("promptForCurrent", true); promptForCurrentPassword.addLongIdentifier("prompt-for-current", true); promptForCurrentPassword.addLongIdentifier("promptCurrent", true); promptForCurrentPassword.addLongIdentifier("prompt-current", true); promptForCurrentPassword.addLongIdentifier("promptForOldPassword", true); promptForCurrentPassword.addLongIdentifier("prompt-for-old-password", true); promptForCurrentPassword.addLongIdentifier("promptForOldPW", true); promptForCurrentPassword.addLongIdentifier("prompt-for-old-pw", true); promptForCurrentPassword.addLongIdentifier("promptForOld", true); promptForCurrentPassword.addLongIdentifier("prompt-for-old", true); promptForCurrentPassword.addLongIdentifier("promptOld", true); promptForCurrentPassword.addLongIdentifier("prompt-old", true); promptForCurrentPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); parser.addArgument(promptForCurrentPassword); // Bind control arguments. bindControl = new ControlArgument(null, "bindControl", false, 0, null, INFO_PWMOD_ARG_DESC_BIND_CONTROL.get()); bindControl.addLongIdentifier("bind-control", true); bindControl.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); parser.addArgument(bindControl); useAuthorizationIdentityControl = new BooleanArgument(null, "useAuthorizationIdentityControl", 1, INFO_PWMOD_ARG_DESC_USE_AUTHZ_ID_CONTROL.get()); useAuthorizationIdentityControl.addLongIdentifier( "use-authorization-identity-control", true); useAuthorizationIdentityControl.addLongIdentifier( "useAuthorizationID-control", true); useAuthorizationIdentityControl.addLongIdentifier( "use-authorization-id-control", true); useAuthorizationIdentityControl.addLongIdentifier( "authorizationIdentityControl", true); useAuthorizationIdentityControl.addLongIdentifier( "authorization-identity-control", true); useAuthorizationIdentityControl.addLongIdentifier("authorizationIDControl", true); useAuthorizationIdentityControl.addLongIdentifier( "authorization-id-control", true); useAuthorizationIdentityControl.addLongIdentifier("authzIDControl", true); useAuthorizationIdentityControl.addLongIdentifier("authz-id-control", true); useAuthorizationIdentityControl.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); parser.addArgument(useAuthorizationIdentityControl); usePasswordPolicyControlOnBind = new BooleanArgument(null, "usePasswordPolicyControlOnBind", 1, INFO_PWMOD_ARG_DESC_USE_PW_POLICY_CONTROL_ON_BIND.get()); usePasswordPolicyControlOnBind.addLongIdentifier( "use-password-policy-control-on-bind", true); usePasswordPolicyControlOnBind.addLongIdentifier("usePWPolicyControlOnBind", true); usePasswordPolicyControlOnBind.addLongIdentifier( "use-pw-policy-control-on-bind", true); usePasswordPolicyControlOnBind.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); parser.addArgument(usePasswordPolicyControlOnBind); getAuthorizationEntryAttribute = new StringArgument(null, "getAuthorizationEntryAttribute", false, 0, INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), INFO_PWMOD_ARG_DESC_GET_AUTHZ_ENTRY_ATTRIBUTE.get()); getAuthorizationEntryAttribute.addLongIdentifier( "get-authorization-entry-attribute", true); getAuthorizationEntryAttribute.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); parser.addArgument(getAuthorizationEntryAttribute); getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 1, INFO_PWMOD_ARG_DESC_GET_USER_RESOURCE_LIMITS.get()); getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); getUserResourceLimits.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); parser.addArgument(getUserResourceLimits); // Update control arguments. updateControl = new ControlArgument('J', "updateControl", false, 0, null, INFO_PWMOD_ARG_DESC_UPDATE_CONTROL.get()); updateControl.addLongIdentifier("update-control", true); updateControl.addLongIdentifier("control", true); updateControl.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(updateControl); usePasswordPolicyControlOnUpdate = new BooleanArgument(null, "usePasswordPolicyControlOnUpdate", 1, INFO_PWMOD_ARG_DESC_USE_PW_POLICY_CONTROL_ON_UPDATE.get()); usePasswordPolicyControlOnUpdate.addLongIdentifier( "use-password-policy-control-on-update", true); usePasswordPolicyControlOnUpdate.addLongIdentifier( "usePWPolicyControlOnUpdate", true); usePasswordPolicyControlOnUpdate.addLongIdentifier( "use-pw-policy-control-on-update", true); usePasswordPolicyControlOnUpdate.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(usePasswordPolicyControlOnUpdate); noOperation = new BooleanArgument(null, "noOperation", 1, INFO_PWMOD_ARG_DESC_NO_OPERATION.get()); noOperation.addLongIdentifier("no-operation", true); noOperation.addLongIdentifier("noOp", true); noOperation.addLongIdentifier("no-op", true); noOperation.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(noOperation); getPasswordValidationDetails = new BooleanArgument(null, "getPasswordValidationDetails", 1, INFO_PWMOD_ARG_DESC_GET_PW_VALIDATION_DETAILS.get()); getPasswordValidationDetails.addLongIdentifier( "get-password-validation-details", true); getPasswordValidationDetails.addLongIdentifier("getPWValidationDetails", true); getPasswordValidationDetails.addLongIdentifier("get-pw-validation-details", true); getPasswordValidationDetails.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(getPasswordValidationDetails); retireCurrentPassword = new BooleanArgument(null, "retireCurrentPassword", 1, INFO_PWMOD_ARG_DESC_RETIRE_CURRENT_PASSWORD.get()); retireCurrentPassword.addLongIdentifier("retire-current-password", true); retireCurrentPassword.addLongIdentifier("retireCurrentPW", true); retireCurrentPassword.addLongIdentifier("retire-current-pw", true); retireCurrentPassword.addLongIdentifier("retirePassword", true); retireCurrentPassword.addLongIdentifier("retire-password", true); retireCurrentPassword.addLongIdentifier("retirePW", true); retireCurrentPassword.addLongIdentifier("retire-pw", true); retireCurrentPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(retireCurrentPassword); purgeCurrentPassword = new BooleanArgument(null, "purgeCurrentPassword", 1, INFO_PWMOD_ARG_DESC_PURGE_CURRENT_PASSWORD.get()); purgeCurrentPassword.addLongIdentifier("purge-current-password", true); purgeCurrentPassword.addLongIdentifier("purgeCurrentPW", true); purgeCurrentPassword.addLongIdentifier("purge-current-pw", true); purgeCurrentPassword.addLongIdentifier("purgePassword", true); purgeCurrentPassword.addLongIdentifier("purge-password", true); purgeCurrentPassword.addLongIdentifier("purgePW", true); purgeCurrentPassword.addLongIdentifier("purge-pw", true); purgeCurrentPassword.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(purgeCurrentPassword); passwordUpdateBehavior = new StringArgument(null, "passwordUpdateBehavior", false, 0, INFO_PWMOD_ARG_PLACEHOLDER_NAME_VALUE.get(), INFO_PWMOD_ARG_DESC_PASSWORD_UPDATE_BEHAVIOR.get()); passwordUpdateBehavior.addLongIdentifier("password-update-behavior", true); passwordUpdateBehavior.addLongIdentifier("pwUpdateBehavior", true); passwordUpdateBehavior.addLongIdentifier("pw-update-behavior", true); passwordUpdateBehavior.addLongIdentifier("updateBehavior", true); passwordUpdateBehavior.addLongIdentifier("update-behavior", true); passwordUpdateBehavior.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(passwordUpdateBehavior); useAssuredReplication = new BooleanArgument(null, "useAssuredReplication", 1, INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION.get()); useAssuredReplication.addLongIdentifier("use-assured-replication", true); useAssuredReplication.addLongIdentifier("assuredReplication", true); useAssuredReplication.addLongIdentifier("assured-replication", true); useAssuredReplication.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(useAssuredReplication); assuredReplicationLocalLevel = new StringArgument(null, "assuredReplicationLocalLevel", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_LEVEL.get(), INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_LOCAL_LEVEL.get(), StaticUtils.setOf( ASSURED_REPLICATION_LOCAL_LEVEL_NONE, ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER, ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS)); assuredReplicationLocalLevel.addLongIdentifier( "assured-replication-local-level", true); assuredReplicationLocalLevel.addLongIdentifier("localLevel", true); assuredReplicationLocalLevel.addLongIdentifier("local-level", true); assuredReplicationLocalLevel.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(assuredReplicationLocalLevel); assuredReplicationRemoteLevel = new StringArgument(null, "assuredReplicationRemoteLevel", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_LEVEL.get(), INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_REMOTE_LEVEL.get(), StaticUtils.setOf( ASSURED_REPLICATION_REMOTE_LEVEL_NONE, ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION, ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS, ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS)); assuredReplicationRemoteLevel.addLongIdentifier( "assured-replication-remote-level", true); assuredReplicationRemoteLevel.addLongIdentifier("remoteLevel", true); assuredReplicationRemoteLevel.addLongIdentifier("remote-level", true); assuredReplicationRemoteLevel.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(assuredReplicationRemoteLevel); assuredReplicationTimeout = new DurationArgument(null, "assuredReplicationTimeout", false, INFO_PWMOD_ARG_PLACEHOLDER_TIMEOUT.get(), INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_TIMEOUT.get()); assuredReplicationTimeout.addLongIdentifier("assured-replication-timeout", true); assuredReplicationTimeout.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(assuredReplicationTimeout); operationPurpose = new StringArgument(null, "operationPurpose", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_PURPOSE.get(), INFO_PWMOD_ARG_DESC_OPERATION_PURPOSE.get()); operationPurpose.addLongIdentifier("operation-purpose", true); operationPurpose.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); parser.addArgument(operationPurpose); // Other arguments passwordAttribute = new StringArgument(null, "passwordAttribute", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), INFO_PWMOD_ARG_DESC_PASSWORD_ATTRIBUTE.get(), DEFAULT_PASSWORD_ATTRIBUTE); passwordAttribute.addLongIdentifier("password-attribute", true); passwordAttribute.addLongIdentifier("passwordAttr", true); passwordAttribute.addLongIdentifier("password-attr", true); passwordAttribute.addLongIdentifier("pwAttribute", true); passwordAttribute.addLongIdentifier("pw-attribute", true); passwordAttribute.addLongIdentifier("pwAttr", true); passwordAttribute.addLongIdentifier("pw-attr", true); passwordAttribute.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_OTHER.get()); passwordChangeMethod = new StringArgument(null, "passwordChangeMethod", false, 1, INFO_PWMOD_ARG_PLACEHOLDER_CHANGE_METHOD.get(), INFO_PWMOD_ARG_DESC_PASSWORD_CHANGE_METHOD.get(), StaticUtils.setOf( PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP, PASSWORD_CHANGE_METHOD_LDAP_MOD, PASSWORD_CHANGE_METHOD_AD)); passwordChangeMethod.addLongIdentifier("password-change-method", true); passwordChangeMethod.addLongIdentifier("pwChangeMethod", true); passwordChangeMethod.addLongIdentifier("pw-change-method", true); passwordChangeMethod.addLongIdentifier("changeMethod", true); passwordChangeMethod.addLongIdentifier("change-method", true); passwordChangeMethod.addLongIdentifier("method", true); passwordChangeMethod.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_OTHER.get()); parser.addArgument(passwordChangeMethod); followReferrals = new BooleanArgument(null, "followReferrals", 1, INFO_PWMOD_ARG_DESC_FOLLOW_REFERRALS.get()); followReferrals.addLongIdentifier("follow-referrals", true); followReferrals.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_OTHER.get()); parser.addArgument(followReferrals); useAdministrativeSession = new BooleanArgument(null, "useAdministrativeSession", 1, INFO_PWMOD_ARG_DESC_USE_ADMIN_SESSION.get()); useAdministrativeSession.addLongIdentifier("use-administrative-session", true); useAdministrativeSession.addLongIdentifier("useAdminSession", true); useAdministrativeSession.addLongIdentifier("use-admin-session", true); useAdministrativeSession.addLongIdentifier("administrativeSession", true); useAdministrativeSession.addLongIdentifier("administrative-session", true); useAdministrativeSession.addLongIdentifier("adminSession", true); useAdministrativeSession.addLongIdentifier("admin-session", true); useAdministrativeSession.setArgumentGroupName( INFO_PWMOD_ARG_GROUP_OTHER.get()); parser.addArgument(useAdministrativeSession); verbose = new BooleanArgument('v', "verbose", 1, INFO_PWMOD_ARG_DESC_VERBOSE.get()); verbose.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_OTHER.get()); parser.addArgument(verbose); // This argument isn't actually used, but provides command-line backward // compatibility with an existing implementation. scriptFriendly = new BooleanArgument(null, "script-friendly", 1, INFO_PWMOD_ARG_DESC_SCRIPT_FRIENDLY.get()); scriptFriendly.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_OTHER.get()); scriptFriendly.setHidden(true); parser.addArgument(scriptFriendly); // Argument constraints. parser.addExclusiveArgumentSet(userIdentity, provideBindDNAsUserIdentity); final DNArgument bindDNArgument = parser.getDNArgument(BIND_DN_ARGUMENT_LONG_IDENTIFIER); parser.addDependentArgumentSet(provideBindDNAsUserIdentity, bindDNArgument); parser.addExclusiveArgumentSet(newPassword, newPasswordFile, promptForNewPassword, generateClientSideNewPassword); parser.addDependentArgumentSet(generatedPasswordLength, generateClientSideNewPassword); parser.addDependentArgumentSet(generatedPasswordCharacterSet, generateClientSideNewPassword); parser.addExclusiveArgumentSet(currentPassword, currentPasswordFile, promptForCurrentPassword); parser.addDependentArgumentSet(assuredReplicationLocalLevel, useAssuredReplication); parser.addDependentArgumentSet(assuredReplicationRemoteLevel, useAssuredReplication); parser.addDependentArgumentSet(assuredReplicationTimeout, useAssuredReplication); parser.addExclusiveArgumentSet(retireCurrentPassword, purgeCurrentPassword); } /** * {@inheritDoc} */ @Override() @NotNull() protected Set getSuppressedShortIdentifiers() { return StaticUtils.setOf('N'); } /** * {@inheritDoc} */ @Override() public void doExtendedNonLDAPArgumentValidation() throws ArgumentException { // Make sure that if any generate password character sets were provided, // they must all be non-empty. if (generatedPasswordCharacterSet.isPresent()) { for (final String charSet : generatedPasswordCharacterSet.getValues()) { if (charSet.isEmpty()) { throw new ArgumentException(ERR_PWMOD_CHAR_SET_EMPTY.get( generatedPasswordCharacterSet.getIdentifierString())); } } } } /** * {@inheritDoc} */ @Override() @NotNull() public ResultCode doToolProcessing() { LDAPConnectionPool pool = null; try { // Create a connection pool that will be used to communicate with the // directory server. If we should use an administrative session, then // create a connect processor that will be used to start the session // before performing the bind. try { final StartAdministrativeSessionPostConnectProcessor p; if (useAdministrativeSession.isPresent()) { p = new StartAdministrativeSessionPostConnectProcessor( new StartAdministrativeSessionExtendedRequest(getToolName(), true)); } else { p = null; } pool = getConnectionPool(1, 2, 0, p, null, true, new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, verbose.isPresent())); // Figure out the method to use to update the password. final String updateMethod; try { updateMethod = getPasswordUpdateMethod(pool); } catch (final LDAPException e) { Debug.debugException(e); logCompletionMessage(true, e.getMessage()); return e.getResultCode(); } switch (updateMethod) { case PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP: return doPasswordModifyExtendedOperation(pool); case PASSWORD_CHANGE_METHOD_AD: return doLDAPModifyPasswordUpdate(pool, true); case PASSWORD_CHANGE_METHOD_LDAP_MOD: default: return doLDAPModifyPasswordUpdate(pool, false); } } catch (final LDAPException le) { Debug.debugException(le); // Unable to create the connection pool, which means that either the // connection could not be established or the attempt to authenticate // the connection failed. If the bind failed, then the report bind // result health check should have already reported the bind failure. // If the failure was something else, then display that failure result. if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) { for (final String line : ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) { err(line); } } return le.getResultCode(); } } finally { if (pool != null) { pool.close(); } } } /** * Determines the method that should be used to update the password. * * @param pool The connection pool to use to communicate with the * directory server, if appropriate. * * @return The method that should be used to update the password. The value * returned will be one of * {@link #PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP}, * {@link #PASSWORD_CHANGE_METHOD_LDAP_MOD}, or * {@link #PASSWORD_CHANGE_METHOD_AD}. * * @throws LDAPException If a problem occurs while attempting to make the * determination. */ @NotNull() private String getPasswordUpdateMethod(@NotNull final LDAPConnectionPool pool) throws LDAPException { if (passwordChangeMethod.isPresent()) { switch (StaticUtils.toLowerCase(passwordChangeMethod.getValue())) { case PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP: return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; case PASSWORD_CHANGE_METHOD_LDAP_MOD: return PASSWORD_CHANGE_METHOD_LDAP_MOD; case PASSWORD_CHANGE_METHOD_AD: return PASSWORD_CHANGE_METHOD_AD; } } // Retrieve the root DSE from the directory server. If we can't get the // root DSE, then default to the password modify extended operation. final RootDSE rootDSE; try { rootDSE = pool.getRootDSE(); } catch (final LDAPException e) { Debug.debugException(e); return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; } if (rootDSE == null) { return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; } // If the root DSE claims support for the password modify extended // operation, then use that method. if (rootDSE.supportsExtendedOperation( PasswordModifyExtendedRequest.PASSWORD_MODIFY_REQUEST_OID)) { if (verbose.isPresent()) { wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SELECTING_PW_MOD_EXTOP_METHOD.get()); } return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; } // We need to differentiate between Active Directory and other types of // servers. Unfortunately, Active Directory doesn't seem to provide // vendorName or vendorVersion attributes in its root DSE, so we'll need to // use some other means of detecting it. Let's assume that if the server // advertises support for at least twenty supported controls in Microsoft's // OID range (starting with 1.2.840.113556), then it's an Active Directory // instance. At the time this was written, two different AD versions each // advertised support for nearly double that number. int numMicrosoftControlsSupported = 0; for (final String oid : rootDSE.getSupportedControlOIDs()) { if (oid.startsWith(MICROSOFT_BASE_OBJECT_IDENTIFIER + '.')) { numMicrosoftControlsSupported++; } } if (numMicrosoftControlsSupported >= 20) { if (verbose.isPresent()) { wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SELECTING_AD_METHOD_CONTROL_COUNT.get( numMicrosoftControlsSupported, MICROSOFT_BASE_OBJECT_IDENTIFIER)); } return PASSWORD_CHANGE_METHOD_AD; } // Fall back to a default of a regular LDAP modify operation. if (verbose.isPresent()) { wrapOut(0, WRAP_COLUMN, INFO_PWMOD_DEFAULTING_TO_LDAP_MOD.get()); } return PASSWORD_CHANGE_METHOD_LDAP_MOD; } /** * Attempts a password modify extended operation to change the target user's * password. * * @param pool A connection pool to use to communicate with the directory * server. * * @return A result code that indicates whether the password update was * successful. */ @NotNull() private ResultCode doPasswordModifyExtendedOperation( @NotNull final LDAPConnectionPool pool) { // Create the password modify extended request to be processed. final String identity; final byte[] currentPW; final byte[] newPW; final Control[] controls; try { identity = getUserIdentity(null, false); currentPW = getCurrentPassword(); newPW = getNewPassword(); controls = getUpdateControls(); } catch (final LDAPException e) { Debug.debugException(e); logCompletionMessage(true, e.getMessage()); return e.getResultCode(); } final PasswordModifyExtendedRequest passwordModifyRequest = new PasswordModifyExtendedRequest(identity, currentPW, newPW, controls); // Send the request and interpret the response, including special handling // for any referral that might have been returned. LDAPConnection connection = null; try { connection = pool.getConnection(); final PasswordModifyExtendedResult passwordModifyResult = (PasswordModifyExtendedResult) connection.processExtendedOperation(passwordModifyRequest); out(); out(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); for (final String line : ResultUtils.formatResult(passwordModifyResult, true, 0, WRAP_COLUMN)) { out(line); } out(); final String generatedPassword = passwordModifyResult.getGeneratedPassword(); if (passwordModifyResult.getResultCode() == ResultCode.SUCCESS) { logCompletionMessage(false, INFO_PWMOD_EXTOP_SUCCESSFUL.get()); if (generatedPassword != null) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); } return ResultCode.SUCCESS; } else if (passwordModifyResult.getResultCode() == ResultCode.NO_OPERATION) { logCompletionMessage(false, INFO_PWMOD_EXTOP_NO_OP.get()); if (generatedPassword != null) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); } return ResultCode.SUCCESS; } else if ((passwordModifyResult.getResultCode() == ResultCode.REFERRAL) && followReferrals.isPresent() && (passwordModifyResult.getReferralURLs().length > 0)) { // The LDAP SDK doesn't support automatic referral following for // extended operations. If appropriate, try to follow it ourselves. return followPasswordModifyReferral(passwordModifyRequest, passwordModifyResult, connection, 1); } else { logCompletionMessage(true, ERR_PWMOD_EXTOP_FAILED.get( String.valueOf(passwordModifyResult.getResultCode()), passwordModifyResult.getDiagnosticMessage())); return passwordModifyResult.getResultCode(); } } catch (final LDAPException e) { Debug.debugException(e); err(); err(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); for (final String line : ResultUtils.formatResult(e, true, 0, WRAP_COLUMN)) { err(line); } err(); if (connection != null) { pool.releaseDefunctConnection(connection); connection = null; } logCompletionMessage(true, ERR_PWMOD_EXTOP_ERROR.get(String.valueOf(e.getResultCode()), e.getMessage())); return e.getResultCode(); } finally { if (connection != null) { pool.releaseConnection(connection); } } } /** * Attempts to follow a referral that was returned in response to a password * modify extended request. * * @param request The extended request that was sent. * @param result The extended result that was received, * including the referral details. * @param receivedOnConnection The LDAP connection on which the referral * result was received. * @param referralCount The number of referrals that have been * returned so far. If this is too high, then * subsequent referrals will not be followed. * * @return A result code that indicates whether the password update was * successful. */ @NotNull() private ResultCode followPasswordModifyReferral( @NotNull final PasswordModifyExtendedRequest request, @NotNull final PasswordModifyExtendedResult result, @NotNull final LDAPConnection receivedOnConnection, final int referralCount) { final List referralURLs = new ArrayList<>(); for (final String urlString : result.getReferralURLs()) { try { referralURLs.add(new LDAPURL(urlString)); } catch (final LDAPException e) { Debug.debugException(e); } } if (referralURLs.isEmpty()) { logCompletionMessage(true, ERR_PWMOD_EXTOP_NO_VALID_REFERRAL_URLS.get(String.valueOf(result))); return ResultCode.REFERRAL; } LDAPException firstException = null; for (final LDAPURL url : referralURLs) { try (LDAPConnection referralConnection = receivedOnConnection.getReferralConnection(url, receivedOnConnection)) { final String referredUserIdentity; if (url.getBaseDN().isNullDN()) { referredUserIdentity = request.getUserIdentity(); } else { referredUserIdentity = url.getBaseDN().toString(); } final PasswordModifyExtendedRequest referralRequest = new PasswordModifyExtendedRequest(referredUserIdentity, request.getOldPassword(), request.getNewPassword(), request.getControls()); final PasswordModifyExtendedResult referralResult = (PasswordModifyExtendedResult) referralConnection.processExtendedOperation(referralRequest); out(); out(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); for (final String line : ResultUtils.formatResult(referralResult, true, 0, WRAP_COLUMN)) { out(line); } out(); final String generatedPassword = referralResult.getGeneratedPassword(); if (referralResult.getResultCode() == ResultCode.SUCCESS) { logCompletionMessage(false, INFO_PWMOD_EXTOP_SUCCESSFUL.get()); if (generatedPassword != null) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); } return ResultCode.SUCCESS; } else if (referralResult.getResultCode() == ResultCode.NO_OPERATION) { logCompletionMessage(false, INFO_PWMOD_EXTOP_NO_OP.get()); if (generatedPassword != null) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); } return ResultCode.SUCCESS; } else if (referralResult.getResultCode() == ResultCode.REFERRAL) { final int maxReferralCount = receivedOnConnection. getConnectionOptions().getReferralHopLimit(); if (referralCount > maxReferralCount) { logCompletionMessage(true, ERR_PWMOD_TOO_MANY_REFERRALS.get()); return ResultCode.REFERRAL_LIMIT_EXCEEDED; } else { return followPasswordModifyReferral(referralRequest, referralResult, referralConnection, (referralCount + 1)); } } else { if (firstException == null) { firstException = new LDAPExtendedOperationException(referralResult); } } } catch (final LDAPException e) { Debug.debugException(e); if (firstException == null) { firstException = e; } } } logCompletionMessage(true, ERR_PWMOD_FOLLOW_REFERRAL_FAILED.get( String.valueOf(firstException.getResultCode()), firstException.getDiagnosticMessage())); return firstException.getResultCode(); } /** * Attempts a regular LDAP modify operation to change the target user's * password. * * @param pool A connection pool to use to communicate with the * directory server. * @param isActiveDirectory Indicates whether the target directory server * is believed to be an Active Directory instance. * * @return A result code that indicates whether the password update was * successful. */ @NotNull() private ResultCode doLDAPModifyPasswordUpdate( @NotNull final LDAPConnectionPool pool, final boolean isActiveDirectory) { // Get the information to include in the password modify extended request. byte[] currentPW; byte[] newPW; final String identity; final Control[] controls; try { identity = getUserIdentity(pool, isActiveDirectory); currentPW = getCurrentPassword(); newPW = getNewPassword(); controls = getUpdateControls(); } catch (final LDAPException e) { Debug.debugException(e); logCompletionMessage(true, e.getMessage()); return e.getResultCode(); } // If there is no new password, then fail. if (newPW == null) { logCompletionMessage(true, ERR_PWMOD_NO_NEW_PW_FOR_MODIFY.get(newPassword.getIdentifierString(), newPasswordFile.getIdentifierString(), promptForNewPassword.getIdentifierString(), generateClientSideNewPassword.getIdentifierString())); return ResultCode.PARAM_ERROR; } // Determine the name of the attribute to modify. final String passwordAttr; if (isActiveDirectory) { passwordAttr = AD_PASSWORD_ATTRIBUTE; currentPW = encodePasswordForActiveDirectory(currentPW); newPW = encodePasswordForActiveDirectory(newPW); } else { passwordAttr = passwordAttribute.getValue(); } // Construct the modify request to send to the server. final ModifyRequest modifyRequest; if (currentPW == null) { modifyRequest = new ModifyRequest(identity, new Modification(ModificationType.REPLACE, passwordAttr, newPW)); } else { modifyRequest = new ModifyRequest(identity, new Modification(ModificationType.DELETE, passwordAttr, currentPW), new Modification(ModificationType.ADD, passwordAttr, newPW)); } modifyRequest.setControls(controls); // Send the modify request and read the result. LDAPResult modifyResult; try { modifyResult = pool.modify(modifyRequest); } catch (final LDAPException e) { Debug.debugException(e); modifyResult = e.toLDAPResult(); } out(); out(INFO_PWMOD_MODIFY_RESULT_HEADER.get()); for (final String line : ResultUtils.formatResult(modifyResult, true, 0, WRAP_COLUMN)) { out(line); } out(); if (modifyResult.getResultCode() == ResultCode.SUCCESS) { logCompletionMessage(false, INFO_PWMOD_MODIFY_SUCCESSFUL.get()); return ResultCode.SUCCESS; } else if (modifyResult.getResultCode() == ResultCode.NO_OPERATION) { logCompletionMessage(false, INFO_PWMOD_MODIFY_NO_OP.get()); return ResultCode.SUCCESS; } else { logCompletionMessage(true, ERR_PWMOD_MODIFY_FAILED.get( String.valueOf(modifyResult.getResultCode()), modifyResult.getDiagnosticMessage())); return modifyResult.getResultCode(); } } /** * Encodes the provided password in the form that is needed when changing a * password in Active Directory. The password must be surrounded in * quotation marks and encoded as UTF-16 with little-Endian ordering. * * @param pw The password to be encoded. It may optionally be {@code null}. * * @return The encoded password. */ @Nullable() static byte[] encodePasswordForActiveDirectory(@Nullable final byte[] pw) { if (pw == null) { return null; } final String quotedPassword = '"' + StaticUtils.toUTF8String(pw) + '"'; return quotedPassword.getBytes(StandardCharsets.UTF_16LE); } /** * Retrieves the user identity for whom to update the password. * * @param pool A connection pool to use to communicate with the * directory server, if necessary. This may be * {@code null} if only an explicitly provided user * identity should be used. If it is * non-{@code null}, then an attempt will be made * to infer the correct value, and the value * returned will be a DN. * @param isActiveDirectory Indicates whether the target directory server * is believed to be an Active Directory instance. * * @return The user identity for whom to update the password. * * @throws LDAPException If a problem occurs while attempting to obtain the * user identity. */ @NotNull() private String getUserIdentity(@NotNull final LDAPConnectionPool pool, final boolean isActiveDirectory) throws LDAPException { String identity = null; final DNArgument bindDNArgument = argumentParser.getDNArgument(BIND_DN_ARGUMENT_LONG_IDENTIFIER); if (userIdentity.isPresent()) { identity = userIdentity.getValue(); } else if (provideBindDNAsUserIdentity.isPresent()) { identity = bindDNArgument.getStringValue(); if ((pool == null) && verbose.isPresent()) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_USING_USER_IDENTITY_FROM_DN_FOR_EXTOP.get(identity)); } } else { if ((pool == null) && verbose.isPresent()) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_OMITTING_USER_IDENTITY_FROM_EXTOP.get()); } } if (pool == null) { return identity; } if (identity == null) { if (bindDNArgument.isPresent()) { final DN bindDN = bindDNArgument.getValue(); if (! bindDN.isNullDN()) { return bindDN.toString(); } } final WhoAmIExtendedRequest whoAmIRequest = new WhoAmIExtendedRequest(); try { final WhoAmIExtendedResult whoAmIResult = (WhoAmIExtendedResult) pool.processExtendedOperation(whoAmIRequest); if (whoAmIResult.getResultCode() == ResultCode.SUCCESS) { identity = whoAmIResult.getAuthorizationID(); } } catch (final LDAPException e) { Debug.debugException(e); } } if (identity == null) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_PWMOD_CANNOT_DETERMINE_USER_IDENTITY.get( userIdentity.getIdentifierString())); } final String userDN; final String lowerIdentity = StaticUtils.toLowerCase(identity); if (lowerIdentity.startsWith("dn:")) { userDN = identity.substring(3).trim(); } else if (lowerIdentity.startsWith("u:")) { final String username = identity.substring(2).trim(); if (username.isEmpty()) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_PWMOD_USER_IDENTITY_EMPTY_USERNAME.get( userIdentity.getIdentifierString())); } userDN = searchForUser(pool, username, isActiveDirectory); } else { userDN = identity; } final DN parsedUserDN; try { parsedUserDN = new DN(userDN); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(ResultCode.PARAM_ERROR, ERR_PWMOD_USER_IDENTITY_NOT_VALID_DN.get(userDN, userIdentity.getIdentifierString()), e); } if (parsedUserDN.isNullDN()) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_PWMOD_USER_IDENTITY_EMPTY_DN.get( userIdentity.getIdentifierString())); } if (verbose.isPresent()) { out(); INFO_PWMOD_USER_IDENTITY_DN_FOR_MOD.get(userDN); } return userDN; } /** * Performs a search to determine the DN for the user with the given username. * * @param pool A connection pool to use to communicate with the * directory server. It must not be {@code null}. * @param username The username for the target user. It must not * be {@code null}. * @param isActiveDirectory Indicates whether the target directory server * is believed to be an Active Directory instance. * * @return The DN for the user with the given username. * * @throws LDAPException If a problem occurs while searching for the user, * or if the search does not match exactly one user. */ @NotNull() private String searchForUser(@NotNull final LDAPConnectionPool pool, @NotNull final String username, final boolean isActiveDirectory) throws LDAPException { // Construct the filter to use for the search. final List filterAttributeNames; if (usernameAttribute.isPresent()) { filterAttributeNames = usernameAttribute.getValues(); } else if (isActiveDirectory) { filterAttributeNames = AD_USERNAME_ATTRIBUTES; } else { filterAttributeNames = DEFAULT_USERNAME_ATTRIBUTES; } final Filter filter; if (filterAttributeNames.size() == 1) { filter = Filter.createEqualityFilter(filterAttributeNames.get(0), username); } else { final List orComponents = new ArrayList<>(filterAttributeNames.size()); for (final String attrName : filterAttributeNames) { orComponents.add(Filter.createEqualityFilter(attrName, username)); } filter = Filter.createORFilter(orComponents); } // Create the search request to use to find the target user entry. final SearchRequest searchRequest = new SearchRequest( searchBaseDN.getStringValue(), SearchScope.SUB, filter, SearchRequest.NO_ATTRIBUTES); searchRequest.setSizeLimit(1); if (verbose.isPresent()) { out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_ISSUING_SEARCH_FOR_USER.get( String.valueOf(searchRequest), username)); } // Issue the search and get the results. SearchResult searchResult; LDAPException searchException = null; try { searchResult = pool.search(searchRequest); } catch (final LDAPException e) { Debug.debugException(e); searchException = e; searchResult = new SearchResult(e); } if (verbose.isPresent()) { out(); for (final String line : ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) { out(line); } } if (searchResult.getResultCode() == ResultCode.SUCCESS) { if (searchResult.getEntryCount() == 1) { return searchResult.getSearchEntries().get(0).getDN(); } else { throw new LDAPException(ResultCode.NO_RESULTS_RETURNED, ERR_PWMOD_SEARCH_FOR_USER_NO_MATCHES.get(username)); } } else if (searchResult.getResultCode() == ResultCode.SIZE_LIMIT_EXCEEDED) { throw new LDAPException(ResultCode.SIZE_LIMIT_EXCEEDED, ERR_PWMOD_SEARCH_FOR_USER_MULTIPLE_MATCHES.get(username), searchException); } else { throw new LDAPException(searchResult.getResultCode(), ERR_PWMOD_SEARCH_FOR_USER_FAILED.get(username, String.valueOf(searchResult.getResultCode()), searchResult.getDiagnosticMessage()), searchException); } } /** * Retrieves the bytes that comprise the current password for the user, if one * should be provided in the password update request. * * @return The bytes that comprise the current password for the user, or * {@code null} if none should be provided in the password update * request. * * @throws LDAPException If a problem occurs while trying to obtain the * current password. */ @Nullable() private byte[] getCurrentPassword() throws LDAPException { if (currentPassword.isPresent()) { return StaticUtils.getBytes(currentPassword.getValue()); } else if (currentPasswordFile.isPresent()) { final File f = currentPasswordFile.getValue(); try { final char[] currentPasswordChars = getPasswordFileReader().readPassword(f); return StaticUtils.getBytes(new String(currentPasswordChars)); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(e.getResultCode(), ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), e.getMessage()), e); } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } else if (promptForCurrentPassword.isPresent()) { while (true) { getOut().print(INFO_PWMOD_PROMPT_CURRENT_PW.get()); try { final byte[] pwBytes = PasswordReader.readPassword(); if ((pwBytes == null) || (pwBytes.length == 0)) { err(); wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); err(); continue; } return pwBytes; } catch (final Exception e) { throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_PWMOD_CANNOT_PROMPT_FOR_CURRENT_PW.get( StaticUtils.getExceptionMessage(e)), e); } } } else { return null; } } /** * Retrieves the bytes that comprise the new password for the user, if one * should be provided in the password update request. * * @return The bytes that comprise the new password for the user, or * {@code null} if none should be provided in the password update * request. * * @throws LDAPException If a problem occurs while trying to obtain the new * password. */ @Nullable() private byte[] getNewPassword() throws LDAPException { if (newPassword.isPresent()) { return StaticUtils.getBytes(newPassword.getValue()); } else if (newPasswordFile.isPresent()) { final File f = newPasswordFile.getValue(); try { final char[] newPasswordChars = getPasswordFileReader().readPassword(f); return StaticUtils.getBytes(new String(newPasswordChars)); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(e.getResultCode(), ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), e.getMessage()), e); } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } else if (promptForNewPassword.isPresent()) { while (true) { getOut().print(INFO_PWMOD_PROMPT_NEW_PW.get()); final byte[] pwBytes; try { pwBytes = PasswordReader.readPassword(); if ((pwBytes == null) || (pwBytes.length == 0)) { err(); wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); err(); continue; } getOut().print(INFO_PWMOD_CONFIRM_NEW_PW.get()); final byte[] confirmBytes = PasswordReader.readPassword(); if ((confirmBytes == null) || (! Arrays.equals(pwBytes, confirmBytes))) { Arrays.fill(pwBytes, (byte) 0x00); Arrays.fill(confirmBytes, (byte) 0x00); err(); wrapErr(0, WRAP_COLUMN, ERR_PWMOD_NEW_PW_MISMATCH.get()); err(); continue; } Arrays.fill(confirmBytes, (byte) 0x00); return pwBytes; } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_PWMOD_CANNOT_PROMPT_FOR_NEW_PW.get( StaticUtils.getExceptionMessage(e)), e); } } } else if (generateClientSideNewPassword.isPresent()) { return generatePassword(); } else { return null; } } /** * Generates a new password for the user. * * @return The new password that was generated. */ @NotNull() private byte[] generatePassword() { final int length = generatedPasswordLength.getValue(); final StringBuilder generatedPassword = new StringBuilder(length); final SecureRandom random = new SecureRandom(); final StringBuilder allPasswordCharacters = new StringBuilder(); for (final String charSet : generatedPasswordCharacterSet.getValues()) { allPasswordCharacters.append(charSet); // Pick one character at random from the provided set to include in the // password. generatedPassword.append( charSet.charAt(random.nextInt(charSet.length()))); } // Choose as many additional characters (across all of the sets) as needed // to reach the desired length. while (generatedPassword.length() < length) { generatedPassword.append(allPasswordCharacters.charAt( random.nextInt(allPasswordCharacters.length()))); } // Scramble the generated password. final StringBuilder scrambledPassword = new StringBuilder(generatedPassword.length()); while (true) { if (generatedPassword.length() == 1) { scrambledPassword.append(generatedPassword.charAt(0)); break; } else { final int pos = random.nextInt(generatedPassword.length()); scrambledPassword.append(generatedPassword.charAt(pos)); generatedPassword.deleteCharAt(pos); } } final String scrambledPasswordString = scrambledPassword.toString(); out(); wrapOut(0, WRAP_COLUMN, INFO_PWMOD_CLIENT_SIDE_GEN_PW.get(getToolName(), scrambledPasswordString)); return StaticUtils.getBytes(scrambledPasswordString); } /** * Retrieves the controls that should be included in the password update * request. * * @return The controls that should be included in the password update * request, or an empty array if no controls should be included. * * @throws LDAPException If a problem occurs while trying to create any of * the controls. */ @NotNull() private Control[] getUpdateControls() throws LDAPException { final List controls = new ArrayList<>(); if (updateControl.isPresent()) { controls.addAll(updateControl.getValues()); } if (usePasswordPolicyControlOnUpdate.isPresent()) { controls.add(new PasswordPolicyRequestControl()); } if (noOperation.isPresent()) { controls.add(new NoOpRequestControl()); } if (getPasswordValidationDetails.isPresent()) { controls.add(new PasswordValidationDetailsRequestControl()); } if (retireCurrentPassword.isPresent()) { controls.add(new RetirePasswordRequestControl(false)); } if (purgeCurrentPassword.isPresent()) { controls.add(new PurgePasswordRequestControl(false)); } if (passwordUpdateBehavior.isPresent()) { controls.add(LDAPModify.createPasswordUpdateBehaviorRequestControl( passwordUpdateBehavior.getIdentifierString(), passwordUpdateBehavior.getValues())); } if (operationPurpose.isPresent()) { controls.add(new OperationPurposeRequestControl(false, getToolName(), getToolVersion(), LDAPPasswordModify.class.getName() + ".getUpdateControls", operationPurpose.getValue())); } if (useAssuredReplication.isPresent()) { AssuredReplicationLocalLevel localLevel = null; if (assuredReplicationLocalLevel.isPresent()) { final String level = assuredReplicationLocalLevel.getValue(); if (level.equalsIgnoreCase(ASSURED_REPLICATION_LOCAL_LEVEL_NONE)) { localLevel = AssuredReplicationLocalLevel.NONE; } else if (level.equalsIgnoreCase( ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER)) { localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER; } else if (level.equalsIgnoreCase( ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS)) { localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS; } } AssuredReplicationRemoteLevel remoteLevel = null; if (assuredReplicationRemoteLevel.isPresent()) { final String level = assuredReplicationRemoteLevel.getValue(); if (level.equalsIgnoreCase(ASSURED_REPLICATION_REMOTE_LEVEL_NONE)) { remoteLevel = AssuredReplicationRemoteLevel.NONE; } else if (level.equalsIgnoreCase( ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION)) { remoteLevel = AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION; } else if (level.equalsIgnoreCase( ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS)) { remoteLevel = AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS; } else if (level.equalsIgnoreCase( ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS)) { remoteLevel = AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS; } } Long timeoutMillis = null; if (assuredReplicationTimeout.isPresent()) { timeoutMillis = assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS); } controls.add(new AssuredReplicationRequestControl(true, localLevel, localLevel, remoteLevel, remoteLevel, timeoutMillis, false)); } return controls.toArray(StaticUtils.NO_CONTROLS); } /** * Writes the provided message and sets it as the completion message. * * @param isError Indicates whether the message should be written to * standard error rather than standard output. * @param message The message to be written. */ private void logCompletionMessage(final boolean isError, @NotNull final String message) { completionMessage.compareAndSet(null, message); if (isError) { wrapErr(0, WRAP_COLUMN, message); } else { wrapOut(0, WRAP_COLUMN, message); } } /** * {@inheritDoc} */ @Override() public void handleUnsolicitedNotification( @NotNull final LDAPConnection connection, @NotNull final ExtendedResult notification) { final ArrayList lines = new ArrayList<>(10); ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, WRAP_COLUMN); for (final String line : lines) { err(line); } err(); } /** * {@inheritDoc} */ @Override() @NotNull() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap<>(); examples.put( new String[] { "--hostname", "ds.example.com", "--port", "636", "--useSSL", "--userIdentity", "u:jdoe", "--promptForCurrentPassword", "--promptForNewPassword" }, INFO_PWMOD_EXAMPLE_1.get()); examples.put( new String[] { "--hostname", "ds.example.com", "--port", "636", "--useSSL", "--bindDN", "uid=admin,dc=example,dc=com", "--bindPasswordFile", "admin-password.txt", "--userIdentity", "uid=jdoe,ou=People,dc=example,dc=com", "--generateClientSideNewPassword", "--passwordChangeMethod", "ldap-modify" }, INFO_PWMOD_EXAMPLE_2.get()); return examples; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy