001/* 002 * Copyright 2020-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2020-2022 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2020-2022 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.File; 041import java.io.OutputStream; 042import java.nio.charset.StandardCharsets; 043import java.security.SecureRandom; 044import java.util.ArrayList; 045import java.util.Arrays; 046import java.util.Collections; 047import java.util.LinkedHashMap; 048import java.util.List; 049import java.util.Set; 050import java.util.concurrent.TimeUnit; 051import java.util.concurrent.atomic.AtomicReference; 052 053import com.unboundid.ldap.sdk.Control; 054import com.unboundid.ldap.sdk.DN; 055import com.unboundid.ldap.sdk.ExtendedResult; 056import com.unboundid.ldap.sdk.Filter; 057import com.unboundid.ldap.sdk.LDAPConnection; 058import com.unboundid.ldap.sdk.LDAPConnectionOptions; 059import com.unboundid.ldap.sdk.LDAPConnectionPool; 060import com.unboundid.ldap.sdk.LDAPException; 061import com.unboundid.ldap.sdk.LDAPExtendedOperationException; 062import com.unboundid.ldap.sdk.LDAPResult; 063import com.unboundid.ldap.sdk.LDAPURL; 064import com.unboundid.ldap.sdk.Modification; 065import com.unboundid.ldap.sdk.ModificationType; 066import com.unboundid.ldap.sdk.ModifyRequest; 067import com.unboundid.ldap.sdk.ResultCode; 068import com.unboundid.ldap.sdk.RootDSE; 069import com.unboundid.ldap.sdk.SearchRequest; 070import com.unboundid.ldap.sdk.SearchResult; 071import com.unboundid.ldap.sdk.SearchScope; 072import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler; 073import com.unboundid.ldap.sdk.Version; 074import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 075import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedRequest; 076import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedResult; 077import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedRequest; 078import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedResult; 079import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel; 080import com.unboundid.ldap.sdk.unboundidds.controls. 081 AssuredReplicationRemoteLevel; 082import com.unboundid.ldap.sdk.unboundidds.controls. 083 AssuredReplicationRequestControl; 084import com.unboundid.ldap.sdk.unboundidds.controls. 085 GetAuthorizationEntryRequestControl; 086import com.unboundid.ldap.sdk.unboundidds.controls. 087 GetUserResourceLimitsRequestControl; 088import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl; 089import com.unboundid.ldap.sdk.unboundidds.controls. 090 OperationPurposeRequestControl; 091import com.unboundid.ldap.sdk.unboundidds.controls. 092 PasswordPolicyRequestControl; 093import com.unboundid.ldap.sdk.unboundidds.controls. 094 PasswordValidationDetailsRequestControl; 095import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl; 096import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl; 097import com.unboundid.ldap.sdk.unboundidds.extensions. 098 StartAdministrativeSessionExtendedRequest; 099import com.unboundid.ldap.sdk.unboundidds.extensions. 100 StartAdministrativeSessionPostConnectProcessor; 101import com.unboundid.util.Debug; 102import com.unboundid.util.LDAPCommandLineTool; 103import com.unboundid.util.NotNull; 104import com.unboundid.util.Nullable; 105import com.unboundid.util.PasswordReader; 106import com.unboundid.util.StaticUtils; 107import com.unboundid.util.ThreadLocalSecureRandom; 108import com.unboundid.util.ThreadSafety; 109import com.unboundid.util.ThreadSafetyLevel; 110import com.unboundid.util.args.ArgumentException; 111import com.unboundid.util.args.ArgumentParser; 112import com.unboundid.util.args.BooleanArgument; 113import com.unboundid.util.args.ControlArgument; 114import com.unboundid.util.args.DNArgument; 115import com.unboundid.util.args.DurationArgument; 116import com.unboundid.util.args.FileArgument; 117import com.unboundid.util.args.IntegerArgument; 118import com.unboundid.util.args.StringArgument; 119 120import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 121 122 123 124/** 125 * This class provides an implementation of an LDAP command-line tool that may 126 * be used to change passwords in a directory server. Three types of password 127 * changes are supported: the password modify extended operation (as described 128 * in <A HREF="http://www.ietf.org/rfc/rfc3062.txt">RFC 3062</A>), a standard 129 * LDAP modify operation that targets an attribute like userPassword, or an 130 * Active Directory-specific password change that uses an LDAP modify operation 131 * to replace the value of the unicodePwd attribute with a value that is the 132 * password surrounded by quotation marks and encoded with UTF-16-LE. 133 * <BR> 134 * <BLOCKQUOTE> 135 * <B>NOTE:</B> This class, and other classes within the 136 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 137 * supported for use against Ping Identity, UnboundID, and 138 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 139 * for proprietary functionality or for external specifications that are not 140 * considered stable or mature enough to be guaranteed to work in an 141 * interoperable way with other types of LDAP servers. 142 * </BLOCKQUOTE> 143 */ 144@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 145public final class LDAPPasswordModify 146 extends LDAPCommandLineTool 147 implements UnsolicitedNotificationHandler 148{ 149 /** 150 * The column at which output should be wrapped. 151 */ 152 private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 153 154 155 156 /** 157 * The assured replication local level value that indicates no assurance is 158 * needed. 159 */ 160 @NotNull private static final String ASSURED_REPLICATION_LOCAL_LEVEL_NONE = 161 "none"; 162 163 164 165 /** 166 * The assured replication local level value that indicates the change should 167 * be received by at least one other local server. 168 */ 169 @NotNull private static final String 170 ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER = 171 "received-any-server"; 172 173 174 175 /** 176 * The assured replication local level value that indicates the change should 177 * be processed by all available local servers. 178 */ 179 @NotNull private static final String 180 ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS = 181 "processed-all-servers"; 182 183 184 185 /** 186 * The assured replication remote level value that indicates no assurance is 187 * needed. 188 */ 189 @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_NONE = 190 "none"; 191 192 193 194 /** 195 * The assured replication remote level value that indicates the change should 196 * be received by at least one other remote server in at least one remote 197 * location. 198 */ 199 @NotNull private static final String 200 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION = 201 "received-any-remote-location"; 202 203 204 205 /** 206 * The assured replication remote level value that indicates the change should 207 * be received by at least one other remote server in every remote 208 * location. 209 */ 210 @NotNull private static final String 211 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS = 212 "received-all-remote-locations"; 213 214 215 216 /** 217 * The assured replication remote level value that indicates the change should 218 * be processed by all available remote servers in all locations. 219 */ 220 @NotNull private static final String 221 ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS = 222 "processed-all-remote-servers"; 223 224 225 226 /** 227 * The password change method that will be used to indicate that the password 228 * modify extended operation should be used. 229 */ 230 @NotNull private static final String PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP = 231 "password-modify-extended-operation"; 232 233 234 235 /** 236 * The password change method that will be used to indicate that a regular 237 * LDAP modify operation should be used. 238 */ 239 @NotNull private static final String PASSWORD_CHANGE_METHOD_LDAP_MOD = 240 "ldap-modify"; 241 242 243 244 /** 245 * The password change method that will be used to indicate that an 246 * Active Directory-specific operation should be used. 247 */ 248 @NotNull private static final String PASSWORD_CHANGE_METHOD_AD = 249 "active-directory"; 250 251 252 253 /** 254 * The long identifier for the {@link LDAPCommandLineTool} argument used to 255 * specify the bind DN to use when authenticating to the directory server. 256 */ 257 @NotNull private static final String BIND_DN_ARGUMENT_LONG_IDENTIFIER = 258 "bindDN"; 259 260 261 262 /** 263 * The name of the default attribute that will be assumed to hold the password 264 * in most directory servers. 265 */ 266 @NotNull private static final String DEFAULT_PASSWORD_ATTRIBUTE = 267 "userPassword"; 268 269 270 271 /** 272 * The name of the attribute that Active Directory uses to hold the password. 273 */ 274 @NotNull private static final String AD_PASSWORD_ATTRIBUTE = "unicodePwd"; 275 276 277 278 /** 279 * The names of the attributes that will be used when searching for an entry 280 * from its username in most directory servers. 281 */ 282 @NotNull private static final List<String> DEFAULT_USERNAME_ATTRIBUTES = 283 Collections.singletonList("uid"); 284 285 286 287 /** 288 * The names of the attributes that will be used when searching for an entry 289 * from its username in an Active Directory server. 290 */ 291 @NotNull private static final List<String> AD_USERNAME_ATTRIBUTES = 292 Collections.unmodifiableList(Arrays.asList("samAccountName", 293 "userPrincipalName")); 294 295 296 297 /** 298 * The OID base that has been assigned to Microsoft. 299 */ 300 @NotNull private static final String MICROSOFT_BASE_OBJECT_IDENTIFIER = 301 "1.2.840.113556"; 302 303 304 305 // A reference to the completion message to return for this tool. 306 @NotNull private final AtomicReference<String> completionMessage; 307 308 // A reference to the argument parser for this tool. 309 @Nullable private ArgumentParser argumentParser; 310 311 // The supported command-line arguments. 312 @Nullable private BooleanArgument followReferrals; 313 @Nullable private BooleanArgument generateClientSideNewPassword; 314 @Nullable private BooleanArgument getPasswordValidationDetails; 315 @Nullable private BooleanArgument getUserResourceLimits; 316 @Nullable private BooleanArgument noOperation; 317 @Nullable private BooleanArgument promptForCurrentPassword; 318 @Nullable private BooleanArgument promptForNewPassword; 319 @Nullable private BooleanArgument provideBindDNAsUserIdentity; 320 @Nullable private BooleanArgument purgeCurrentPassword; 321 @Nullable private BooleanArgument retireCurrentPassword; 322 @Nullable private BooleanArgument scriptFriendly; 323 @Nullable private BooleanArgument useAdministrativeSession; 324 @Nullable private BooleanArgument useAssuredReplication; 325 @Nullable private BooleanArgument useAuthorizationIdentityControl; 326 @Nullable private BooleanArgument usePasswordPolicyControlOnBind; 327 @Nullable private BooleanArgument usePasswordPolicyControlOnUpdate; 328 @Nullable private BooleanArgument verbose; 329 @Nullable private ControlArgument bindControl; 330 @Nullable private ControlArgument updateControl; 331 @Nullable private DNArgument searchBaseDN; 332 @Nullable private DurationArgument assuredReplicationTimeout; 333 @Nullable private FileArgument currentPasswordFile; 334 @Nullable private FileArgument newPasswordFile; 335 @Nullable private IntegerArgument generatedPasswordLength; 336 @Nullable private StringArgument assuredReplicationLocalLevel; 337 @Nullable private StringArgument assuredReplicationRemoteLevel; 338 @Nullable private StringArgument currentPassword; 339 @Nullable private StringArgument generatedPasswordCharacterSet; 340 @Nullable private StringArgument getAuthorizationEntryAttribute; 341 @Nullable private StringArgument newPassword; 342 @Nullable private StringArgument operationPurpose; 343 @Nullable private StringArgument passwordAttribute; 344 @Nullable private StringArgument passwordChangeMethod; 345 @Nullable private StringArgument passwordUpdateBehavior; 346 @Nullable private StringArgument userIdentity; 347 @Nullable private StringArgument usernameAttribute; 348 349 350 351 352 /** 353 * Invokes this tool with the provided set of arguments. The default standard 354 * output and error streams will be used. 355 * 356 * @param args The command-line arguments provided to this program. 357 */ 358 public static void main(@NotNull final String... args) 359 { 360 final ResultCode resultCode = main(System.out, System.err, args); 361 if (resultCode != ResultCode.SUCCESS) 362 { 363 System.exit(resultCode.intValue()); 364 } 365 } 366 367 368 369 /** 370 * Invokes this tool with the provided set of arguments, and using the 371 * provided streams for standard output and error. 372 * 373 * @param out The output stream to use for standard output. It may be 374 * {@code null} if standard output should be suppressed. 375 * @param err The output stream to use for standard error. It may be 376 * {@code null} if standard error should be suppressed. 377 * @param args The command-line arguments provided to this program. 378 * 379 * @return The result code obtained when running the tool. Any result code 380 * other than {@link ResultCode#SUCCESS} indicates an error. 381 */ 382 @NotNull() 383 public static ResultCode main(@Nullable final OutputStream out, 384 @Nullable final OutputStream err, 385 @NotNull final String... args) 386 { 387 final LDAPPasswordModify tool = new LDAPPasswordModify(out, err); 388 return tool.runTool(args); 389 } 390 391 392 393 /** 394 * Creates a new instance of this tool with the provided output and error 395 * streams. 396 * 397 * @param out The output stream to use for standard output. It may be 398 * {@code null} if standard output should be suppressed. 399 * @param err The output stream to use for standard error. It may be 400 * {@code null} if standard error should be suppressed. 401 */ 402 public LDAPPasswordModify(@Nullable final OutputStream out, 403 @Nullable final OutputStream err) 404 { 405 super(out, err); 406 407 completionMessage = new AtomicReference<>(); 408 409 argumentParser = null; 410 411 followReferrals = null; 412 generateClientSideNewPassword = null; 413 getPasswordValidationDetails = null; 414 getUserResourceLimits = null; 415 noOperation = null; 416 promptForCurrentPassword = null; 417 promptForNewPassword = null; 418 provideBindDNAsUserIdentity = null; 419 purgeCurrentPassword = null; 420 retireCurrentPassword = null; 421 scriptFriendly = null; 422 useAdministrativeSession = null; 423 useAssuredReplication = null; 424 useAuthorizationIdentityControl = null; 425 usePasswordPolicyControlOnBind = null; 426 usePasswordPolicyControlOnUpdate = null; 427 verbose = null; 428 bindControl = null; 429 updateControl = null; 430 searchBaseDN = null; 431 assuredReplicationTimeout = null; 432 currentPasswordFile = null; 433 newPasswordFile = null; 434 generatedPasswordLength = null; 435 assuredReplicationLocalLevel = null; 436 assuredReplicationRemoteLevel = null; 437 currentPassword = null; 438 generatedPasswordCharacterSet = null; 439 getAuthorizationEntryAttribute = null; 440 newPassword = null; 441 operationPurpose = null; 442 passwordAttribute = null; 443 passwordChangeMethod = null; 444 passwordUpdateBehavior = null; 445 userIdentity = null; 446 usernameAttribute = null; 447 } 448 449 450 451 /** 452 * {@inheritDoc} 453 */ 454 @Override() 455 @NotNull() 456 public String getToolName() 457 { 458 return "ldappasswordmodify"; 459 } 460 461 462 463 /** 464 * {@inheritDoc} 465 */ 466 @Override() 467 @NotNull() 468 public String getToolDescription() 469 { 470 return INFO_PWMOD_TOOL_DESCRIPTION_1.get(); 471 } 472 473 474 475 /** 476 * {@inheritDoc} 477 */ 478 @Override() 479 @NotNull() 480 public List<String> getAdditionalDescriptionParagraphs() 481 { 482 return Collections.unmodifiableList(Arrays.asList( 483 INFO_PWMOD_TOOL_DESCRIPTION_2.get(), 484 INFO_PWMOD_TOOL_DESCRIPTION_3.get(), 485 INFO_PWMOD_TOOL_DESCRIPTION_4.get())); 486 } 487 488 489 490 /** 491 * {@inheritDoc} 492 */ 493 @Override() 494 @NotNull() 495 public String getToolVersion() 496 { 497 return Version.NUMERIC_VERSION_STRING; 498 } 499 500 501 502 /** 503 * {@inheritDoc} 504 */ 505 @Override() 506 public boolean supportsInteractiveMode() 507 { 508 return true; 509 } 510 511 512 513 /** 514 * {@inheritDoc} 515 */ 516 @Override() 517 public boolean defaultsToInteractiveMode() 518 { 519 return true; 520 } 521 522 523 524 /** 525 * {@inheritDoc} 526 */ 527 @Override() 528 public boolean supportsPropertiesFile() 529 { 530 return true; 531 } 532 533 534 535 /** 536 * {@inheritDoc} 537 */ 538 @Override() 539 protected boolean supportsOutputFile() 540 { 541 return true; 542 } 543 544 545 546 /** 547 * {@inheritDoc} 548 */ 549 @Override() 550 protected boolean supportsAuthentication() 551 { 552 return true; 553 } 554 555 556 557 /** 558 * {@inheritDoc} 559 */ 560 @Override() 561 protected boolean defaultToPromptForBindPassword() 562 { 563 return true; 564 } 565 566 567 568 /** 569 * {@inheritDoc} 570 */ 571 @Override() 572 protected boolean supportsSASLHelp() 573 { 574 return true; 575 } 576 577 578 579 /** 580 * {@inheritDoc} 581 */ 582 @Override() 583 protected boolean includeAlternateLongIdentifiers() 584 { 585 return true; 586 } 587 588 589 590 /** 591 * {@inheritDoc} 592 */ 593 @Override() 594 @NotNull() 595 protected List<Control> getBindControls() 596 { 597 final List<Control> bindControls = new ArrayList<>(10); 598 599 if (bindControl.isPresent()) 600 { 601 bindControls.addAll(bindControl.getValues()); 602 } 603 604 if (useAuthorizationIdentityControl.isPresent()) 605 { 606 bindControls.add(new AuthorizationIdentityRequestControl(false)); 607 } 608 609 if (getAuthorizationEntryAttribute.isPresent()) 610 { 611 bindControls.add(new GetAuthorizationEntryRequestControl(true, true, 612 getAuthorizationEntryAttribute.getValues())); 613 } 614 615 if (getUserResourceLimits.isPresent()) 616 { 617 bindControls.add(new GetUserResourceLimitsRequestControl()); 618 } 619 620 if (usePasswordPolicyControlOnBind.isPresent()) 621 { 622 bindControls.add(new PasswordPolicyRequestControl()); 623 } 624 625 return bindControls; 626 } 627 628 629 630 /** 631 * {@inheritDoc} 632 */ 633 @Override() 634 protected boolean supportsMultipleServers() 635 { 636 return true; 637 } 638 639 640 641 /** 642 * {@inheritDoc} 643 */ 644 @Override() 645 protected boolean supportsSSLDebugging() 646 { 647 return true; 648 } 649 650 651 652 /** 653 * {@inheritDoc} 654 */ 655 @Override() 656 @NotNull() 657 public LDAPConnectionOptions getConnectionOptions() 658 { 659 final LDAPConnectionOptions options = new LDAPConnectionOptions(); 660 661 options.setUseSynchronousMode(true); 662 options.setFollowReferrals(followReferrals.isPresent()); 663 options.setUnsolicitedNotificationHandler(this); 664 options.setResponseTimeoutMillis(0L); 665 666 return options; 667 } 668 669 670 671 /** 672 * {@inheritDoc} 673 */ 674 @Override() 675 protected boolean logToolInvocationByDefault() 676 { 677 return true; 678 } 679 680 681 682 /** 683 * {@inheritDoc} 684 */ 685 @Override() 686 @Nullable() 687 protected String getToolCompletionMessage() 688 { 689 return completionMessage.get(); 690 } 691 692 693 694 /** 695 * {@inheritDoc} 696 */ 697 @Override() 698 public void addNonLDAPArguments(@NotNull final ArgumentParser parser) 699 throws ArgumentException 700 { 701 argumentParser = parser; 702 703 // Authorization identity arguments. 704 userIdentity = new StringArgument('a', "userIdentity", false, 1, 705 INFO_PWMOD_ARG_PLACEHOLDER_DN_OR_AUTHZID.get(), 706 INFO_PWMOD_ARG_DESC_USER_IDENTITY.get()); 707 userIdentity.addLongIdentifier("user-identity", true); 708 userIdentity.addLongIdentifier("userDN", true); 709 userIdentity.addLongIdentifier("user-dn", true); 710 userIdentity.addLongIdentifier("authzID", true); 711 userIdentity.addLongIdentifier("authz-id", true); 712 userIdentity.addLongIdentifier("authorizationID", true); 713 userIdentity.addLongIdentifier("authorization-id", true); 714 userIdentity.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); 715 parser.addArgument(userIdentity); 716 717 provideBindDNAsUserIdentity = new BooleanArgument('A', 718 "provideBindDNAsUserIdentity", 1, 719 INFO_PWMOD_ARG_DESC_PROVIDE_BIND_DN_AS_USER_IDENTITY.get()); 720 provideBindDNAsUserIdentity.addLongIdentifier( 721 "provide-bind-dn-as-user-identity", true); 722 provideBindDNAsUserIdentity.addLongIdentifier( 723 "provideBindDNForUserIdentity", true); 724 provideBindDNAsUserIdentity.addLongIdentifier( 725 "provide-bind-dn-for-user-identity", true); 726 provideBindDNAsUserIdentity.addLongIdentifier("provideDNAsUserIdentity", 727 true); 728 provideBindDNAsUserIdentity.addLongIdentifier("provide-dn-as-user-identity", 729 true); 730 provideBindDNAsUserIdentity.addLongIdentifier("provideDNForUserIdentity", 731 true); 732 provideBindDNAsUserIdentity.addLongIdentifier( 733 "provide-dn-for-user-identity", true); 734 provideBindDNAsUserIdentity.addLongIdentifier("useBindDNAsUserIdentity", 735 true); 736 provideBindDNAsUserIdentity.addLongIdentifier( 737 "use-bind-dn-as-user-identity", true); 738 provideBindDNAsUserIdentity.addLongIdentifier("useBindDNForUserIdentity", 739 true); 740 provideBindDNAsUserIdentity.addLongIdentifier( 741 "use-bind-dn-for-user-identity", true); 742 provideBindDNAsUserIdentity.addLongIdentifier("useDNAsUserIdentity", true); 743 provideBindDNAsUserIdentity.addLongIdentifier("use-dn-as-user-identity", 744 true); 745 provideBindDNAsUserIdentity.addLongIdentifier("useDNForUserIdentity", true); 746 provideBindDNAsUserIdentity.addLongIdentifier("use-dn-for-user-identity", 747 true); 748 provideBindDNAsUserIdentity.addLongIdentifier("useBindDNForAuthzID", true); 749 provideBindDNAsUserIdentity.addLongIdentifier("use-bind-dn-for-authz-id", 750 true); 751 provideBindDNAsUserIdentity.addLongIdentifier("provideDNForAuthzID", true); 752 provideBindDNAsUserIdentity.addLongIdentifier("provide-dn-for-authz-id", 753 true); 754 provideBindDNAsUserIdentity.setArgumentGroupName( 755 INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); 756 parser.addArgument(provideBindDNAsUserIdentity); 757 758 usernameAttribute = new StringArgument(null, "usernameAttribute", false, 0, 759 INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), 760 INFO_PWMOD_ARG_DESC_USERNAME_ATTRIBUTE.get()); 761 usernameAttribute.addLongIdentifier("username-attribute", true); 762 usernameAttribute.addLongIdentifier("usernameAttr", true); 763 usernameAttribute.addLongIdentifier("username-attr", true); 764 usernameAttribute.addLongIdentifier("userIDAttribute", true); 765 usernameAttribute.addLongIdentifier("user-id-attribute", true); 766 usernameAttribute.addLongIdentifier("userIDAttr", true); 767 usernameAttribute.addLongIdentifier("user-id-attr", true); 768 usernameAttribute.setArgumentGroupName( 769 INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); 770 parser.addArgument(usernameAttribute); 771 772 searchBaseDN = new DNArgument('b', "searchBaseDN", false, 0, null, 773 INFO_PWMOD_ARG_DESC_SEARCH_BASE_DN.get(), DN.NULL_DN); 774 searchBaseDN.addLongIdentifier("search-base-dn", true); 775 searchBaseDN.addLongIdentifier("baseDN", true); 776 searchBaseDN.addLongIdentifier("base-dn", true); 777 searchBaseDN.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_USER_IDENTITY.get()); 778 parser.addArgument(searchBaseDN); 779 780 781 // New password arguments. 782 newPassword = new StringArgument('n', "newPassword", false, 1, 783 INFO_PWMOD_ARG_PLACEHOLDER_PASSWORD.get(), 784 INFO_PWMOD_ARG_DESC_NEW_PASSWORD.get()); 785 newPassword.addLongIdentifier("new-password", true); 786 newPassword.addLongIdentifier("newPW", true); 787 newPassword.addLongIdentifier("new-pw", true); 788 newPassword.addLongIdentifier("new", true); 789 newPassword.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 790 parser.addArgument(newPassword); 791 792 newPasswordFile = new FileArgument('N', "newPasswordFile", false, 1, null, 793 INFO_PWMOD_ARG_DESC_NEW_PASSWORD_FILE.get(), true, true, true, false); 794 newPasswordFile.addLongIdentifier("new-password-file", true); 795 newPasswordFile.addLongIdentifier("newPWFile", true); 796 newPasswordFile.addLongIdentifier("new-pw-file", true); 797 newPasswordFile.addLongIdentifier("newFile", true); 798 newPasswordFile.addLongIdentifier("new-file", true); 799 newPasswordFile.addLongIdentifier("newPasswordPath", true); 800 newPasswordFile.addLongIdentifier("new-password-path", true); 801 newPasswordFile.addLongIdentifier("newPWPath", true); 802 newPasswordFile.addLongIdentifier("new-pw-path", true); 803 newPasswordFile.addLongIdentifier("newPath", true); 804 newPasswordFile.addLongIdentifier("new-path", true); 805 newPasswordFile.setArgumentGroupName( 806 INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 807 parser.addArgument(newPasswordFile); 808 809 promptForNewPassword = new BooleanArgument(null, "promptForNewPassword", 1, 810 INFO_PWMOD_ARG_DESC_PROMPT_FOR_NEW_PASSWORD.get()); 811 promptForNewPassword.addLongIdentifier("prompt-for-new-password", true); 812 promptForNewPassword.addLongIdentifier("promptForNewPW", true); 813 promptForNewPassword.addLongIdentifier("prompt-for-new-pw", true); 814 promptForNewPassword.addLongIdentifier("promptForNew", true); 815 promptForNewPassword.addLongIdentifier("prompt-for-new", true); 816 promptForNewPassword.addLongIdentifier("promptNew", true); 817 promptForNewPassword.addLongIdentifier("prompt-new", true); 818 promptForNewPassword.setArgumentGroupName( 819 INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 820 parser.addArgument(promptForNewPassword); 821 822 generateClientSideNewPassword = new BooleanArgument(null, 823 "generateClientSideNewPassword", 1, 824 INFO_PWMOD_ARG_DESC_GENERATE_CLIENT_SIDE_NEW_PASSWORD.get()); 825 generateClientSideNewPassword.addLongIdentifier( 826 "generate-client-side-new-password", true); 827 generateClientSideNewPassword.addLongIdentifier("generateClientSideNewPW", 828 true); 829 generateClientSideNewPassword.addLongIdentifier( 830 "generate-client-side-new-pw", true); 831 generateClientSideNewPassword.addLongIdentifier("generateNewPassword", 832 true); 833 generateClientSideNewPassword.addLongIdentifier("generate-new-password", 834 true); 835 generateClientSideNewPassword.addLongIdentifier("generateNewPW", true); 836 generateClientSideNewPassword.addLongIdentifier("generate-new-pw", true); 837 generateClientSideNewPassword.addLongIdentifier("generatePassword", true); 838 generateClientSideNewPassword.addLongIdentifier("generate-password", true); 839 generateClientSideNewPassword.addLongIdentifier("generatePW", true); 840 generateClientSideNewPassword.addLongIdentifier("generate-pw", true); 841 generateClientSideNewPassword.setArgumentGroupName( 842 INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 843 parser.addArgument(generateClientSideNewPassword); 844 845 generatedPasswordLength = new IntegerArgument(null, 846 "generatedPasswordLength", false, 1, 847 INFO_PWMOD_ARG_PLACEHOLDER_LENGTH.get(), 848 INFO_PWMOD_ARG_DESC_GENERATED_PASSWORD_LENGTH.get(), 1, 849 Integer.MAX_VALUE, 12); 850 generatedPasswordLength.addLongIdentifier("generated-password-length", 851 true); 852 generatedPasswordLength.addLongIdentifier("generatedPWLength", true); 853 generatedPasswordLength.addLongIdentifier("generated-pw-length", true); 854 generatedPasswordLength.addLongIdentifier("passwordLength", true); 855 generatedPasswordLength.addLongIdentifier("password-length", true); 856 generatedPasswordLength.addLongIdentifier("pwLength", true); 857 generatedPasswordLength.addLongIdentifier("pw-length", true); 858 generatedPasswordLength.setArgumentGroupName( 859 INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 860 parser.addArgument(generatedPasswordLength); 861 862 generatedPasswordCharacterSet = new StringArgument(null, 863 "generatedPasswordCharacterSet", false, 0, 864 INFO_PWMOD_ARG_PLACEHOLDER_CHARS.get(), 865 INFO_PWMOD_ARG_DESC_GENERATED_PASSWORD_CHARACTER_SET.get(), null, 866 Collections.unmodifiableList(Arrays.asList( 867 "abcdefghijmnopqrstuvwxyz", // Note that some letters and 868 "ABCDEFGHJLMNPQRSTUVWXYZ", // digits are missing in an attempt 869 "23456789", // to avoid ambiguous characters. 870 "@#-_=+."))); 871 generatedPasswordCharacterSet.addLongIdentifier( 872 "generated-password-character-set", true); 873 generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharacterSet", 874 true); 875 generatedPasswordCharacterSet.addLongIdentifier( 876 "generated-pw-character-set", true); 877 generatedPasswordCharacterSet.addLongIdentifier("generatedPasswordCharSet", 878 true); 879 generatedPasswordCharacterSet.addLongIdentifier( 880 "generated-password-char-set", true); 881 generatedPasswordCharacterSet.addLongIdentifier( 882 "generated-password-charset", true); 883 generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharSet", true); 884 generatedPasswordCharacterSet.addLongIdentifier("generated-pw-char-set", 885 true); 886 generatedPasswordCharacterSet.addLongIdentifier("generated-pw-charset", 887 true); 888 generatedPasswordCharacterSet.addLongIdentifier( 889 "generatedPasswordCharacters", true); 890 generatedPasswordCharacterSet.addLongIdentifier( 891 "generated-password-characters", true); 892 generatedPasswordCharacterSet.addLongIdentifier("generatedPWCharacters", 893 true); 894 generatedPasswordCharacterSet.addLongIdentifier("generated-pw-characters", 895 true); 896 generatedPasswordCharacterSet.addLongIdentifier("generatedPasswordChars", 897 true); 898 generatedPasswordCharacterSet.addLongIdentifier("generated-password-chars", 899 true); 900 generatedPasswordCharacterSet.addLongIdentifier("generatedPWChars", true); 901 generatedPasswordCharacterSet.addLongIdentifier("generated-pw-chars", true); 902 generatedPasswordCharacterSet.addLongIdentifier("passwordCharacters", true); 903 generatedPasswordCharacterSet.addLongIdentifier("password-characters", 904 true); 905 generatedPasswordCharacterSet.addLongIdentifier("pwCharacters", true); 906 generatedPasswordCharacterSet.addLongIdentifier("pw-characters", true); 907 generatedPasswordCharacterSet.addLongIdentifier("passwordCharacterSet", 908 true); 909 generatedPasswordCharacterSet.addLongIdentifier("password-character-set", 910 true); 911 generatedPasswordCharacterSet.addLongIdentifier("pwCharacterSet", true); 912 generatedPasswordCharacterSet.addLongIdentifier("pw-character-set", true); 913 generatedPasswordCharacterSet.addLongIdentifier("passwordCharSet", true); 914 generatedPasswordCharacterSet.addLongIdentifier("password-charset", true); 915 generatedPasswordCharacterSet.addLongIdentifier("password-char-set", true); 916 generatedPasswordCharacterSet.addLongIdentifier("pwCharSet", true); 917 generatedPasswordCharacterSet.addLongIdentifier("pw-charset", true); 918 generatedPasswordCharacterSet.addLongIdentifier("pw-char-set", true); 919 generatedPasswordCharacterSet.addLongIdentifier("passwordChars", true); 920 generatedPasswordCharacterSet.addLongIdentifier("password-chars", true); 921 generatedPasswordCharacterSet.addLongIdentifier("pw-chars", true); 922 generatedPasswordCharacterSet.setArgumentGroupName( 923 INFO_PWMOD_ARG_GROUP_NEW_PASSWORD.get()); 924 parser.addArgument(generatedPasswordCharacterSet); 925 926 927 // Current password arguments. 928 currentPassword = new StringArgument('c', "currentPassword", false, 1, 929 INFO_PWMOD_ARG_PLACEHOLDER_PASSWORD.get(), 930 INFO_PWMOD_ARG_DESC_CURRENT_PASSWORD.get()); 931 currentPassword.addLongIdentifier("current-password", true); 932 currentPassword.addLongIdentifier("currentPW", true); 933 currentPassword.addLongIdentifier("current-pw", true); 934 currentPassword.addLongIdentifier("current", true); 935 currentPassword.addLongIdentifier("oldPassword", true); 936 currentPassword.addLongIdentifier("old-password", true); 937 currentPassword.addLongIdentifier("oldPW", true); 938 currentPassword.addLongIdentifier("old-pw", true); 939 currentPassword.addLongIdentifier("old", true); 940 currentPassword.setArgumentGroupName( 941 INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); 942 parser.addArgument(currentPassword); 943 944 currentPasswordFile = new FileArgument('C', "currentPasswordFile", false, 1, 945 null, INFO_PWMOD_ARG_DESC_CURRENT_PASSWORD_FILE.get(), true, true, 946 true, false); 947 currentPasswordFile.addLongIdentifier("current-password-file", true); 948 currentPasswordFile.addLongIdentifier("currentPWFile", true); 949 currentPasswordFile.addLongIdentifier("current-pw-file", true); 950 currentPasswordFile.addLongIdentifier("currentFile", true); 951 currentPasswordFile.addLongIdentifier("current-file", true); 952 currentPasswordFile.addLongIdentifier("currentPasswordPath", true); 953 currentPasswordFile.addLongIdentifier("current-password-path", true); 954 currentPasswordFile.addLongIdentifier("currentPWPath", true); 955 currentPasswordFile.addLongIdentifier("current-pw-path", true); 956 currentPasswordFile.addLongIdentifier("currentPath", true); 957 currentPasswordFile.addLongIdentifier("current-path", true); 958 currentPasswordFile.addLongIdentifier("oldPasswordFile", true); 959 currentPasswordFile.addLongIdentifier("old-password-file", true); 960 currentPasswordFile.addLongIdentifier("oldPWFile", true); 961 currentPasswordFile.addLongIdentifier("old-pw-file", true); 962 currentPasswordFile.addLongIdentifier("oldFile", true); 963 currentPasswordFile.addLongIdentifier("old-file", true); 964 currentPasswordFile.addLongIdentifier("oldPasswordPath", true); 965 currentPasswordFile.addLongIdentifier("old-password-path", true); 966 currentPasswordFile.addLongIdentifier("oldPWPath", true); 967 currentPasswordFile.addLongIdentifier("old-pw-path", true); 968 currentPasswordFile.addLongIdentifier("oldPath", true); 969 currentPasswordFile.addLongIdentifier("old-path", true); 970 currentPasswordFile.setArgumentGroupName( 971 INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); 972 parser.addArgument(currentPasswordFile); 973 974 promptForCurrentPassword = new BooleanArgument(null, 975 "promptForCurrentPassword", 1, 976 INFO_PWMOD_ARG_DESC_PROMPT_FOR_CURRENT_PASSWORD.get()); 977 promptForCurrentPassword.addLongIdentifier("prompt-for-current-password", 978 true); 979 promptForCurrentPassword.addLongIdentifier("promptForCurrentPW", true); 980 promptForCurrentPassword.addLongIdentifier("prompt-for-current-pw", true); 981 promptForCurrentPassword.addLongIdentifier("promptForCurrent", true); 982 promptForCurrentPassword.addLongIdentifier("prompt-for-current", true); 983 promptForCurrentPassword.addLongIdentifier("promptCurrent", true); 984 promptForCurrentPassword.addLongIdentifier("prompt-current", true); 985 promptForCurrentPassword.addLongIdentifier("promptForOldPassword", true); 986 promptForCurrentPassword.addLongIdentifier("prompt-for-old-password", true); 987 promptForCurrentPassword.addLongIdentifier("promptForOldPW", true); 988 promptForCurrentPassword.addLongIdentifier("prompt-for-old-pw", true); 989 promptForCurrentPassword.addLongIdentifier("promptForOld", true); 990 promptForCurrentPassword.addLongIdentifier("prompt-for-old", true); 991 promptForCurrentPassword.addLongIdentifier("promptOld", true); 992 promptForCurrentPassword.addLongIdentifier("prompt-old", true); 993 promptForCurrentPassword.setArgumentGroupName( 994 INFO_PWMOD_ARG_GROUP_CURRENT_PASSWORD.get()); 995 parser.addArgument(promptForCurrentPassword); 996 997 998 // Bind control arguments. 999 bindControl = new ControlArgument(null, "bindControl", false, 0, null, 1000 INFO_PWMOD_ARG_DESC_BIND_CONTROL.get()); 1001 bindControl.addLongIdentifier("bind-control", true); 1002 bindControl.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); 1003 parser.addArgument(bindControl); 1004 1005 useAuthorizationIdentityControl = new BooleanArgument(null, 1006 "useAuthorizationIdentityControl", 1, 1007 INFO_PWMOD_ARG_DESC_USE_AUTHZ_ID_CONTROL.get()); 1008 useAuthorizationIdentityControl.addLongIdentifier( 1009 "use-authorization-identity-control", true); 1010 useAuthorizationIdentityControl.addLongIdentifier( 1011 "useAuthorizationID-control", true); 1012 useAuthorizationIdentityControl.addLongIdentifier( 1013 "use-authorization-id-control", true); 1014 useAuthorizationIdentityControl.addLongIdentifier( 1015 "authorizationIdentityControl", true); 1016 useAuthorizationIdentityControl.addLongIdentifier( 1017 "authorization-identity-control", true); 1018 useAuthorizationIdentityControl.addLongIdentifier("authorizationIDControl", 1019 true); 1020 useAuthorizationIdentityControl.addLongIdentifier( 1021 "authorization-id-control", true); 1022 useAuthorizationIdentityControl.addLongIdentifier("authzIDControl", true); 1023 useAuthorizationIdentityControl.addLongIdentifier("authz-id-control", true); 1024 useAuthorizationIdentityControl.setArgumentGroupName( 1025 INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); 1026 parser.addArgument(useAuthorizationIdentityControl); 1027 1028 usePasswordPolicyControlOnBind = new BooleanArgument(null, 1029 "usePasswordPolicyControlOnBind", 1, 1030 INFO_PWMOD_ARG_DESC_USE_PW_POLICY_CONTROL_ON_BIND.get()); 1031 usePasswordPolicyControlOnBind.addLongIdentifier( 1032 "use-password-policy-control-on-bind", true); 1033 usePasswordPolicyControlOnBind.addLongIdentifier("usePWPolicyControlOnBind", 1034 true); 1035 usePasswordPolicyControlOnBind.addLongIdentifier( 1036 "use-pw-policy-control-on-bind", true); 1037 usePasswordPolicyControlOnBind.setArgumentGroupName( 1038 INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); 1039 parser.addArgument(usePasswordPolicyControlOnBind); 1040 1041 getAuthorizationEntryAttribute = new StringArgument(null, 1042 "getAuthorizationEntryAttribute", false, 0, 1043 INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), 1044 INFO_PWMOD_ARG_DESC_GET_AUTHZ_ENTRY_ATTRIBUTE.get()); 1045 getAuthorizationEntryAttribute.addLongIdentifier( 1046 "get-authorization-entry-attribute", true); 1047 getAuthorizationEntryAttribute.setArgumentGroupName( 1048 INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); 1049 parser.addArgument(getAuthorizationEntryAttribute); 1050 1051 getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 1052 1, INFO_PWMOD_ARG_DESC_GET_USER_RESOURCE_LIMITS.get()); 1053 getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); 1054 getUserResourceLimits.setArgumentGroupName( 1055 INFO_PWMOD_ARG_GROUP_BIND_CONTROL.get()); 1056 parser.addArgument(getUserResourceLimits); 1057 1058 1059 // Update control arguments. 1060 updateControl = new ControlArgument('J', "updateControl", false, 0, null, 1061 INFO_PWMOD_ARG_DESC_UPDATE_CONTROL.get()); 1062 updateControl.addLongIdentifier("update-control", true); 1063 updateControl.addLongIdentifier("control", true); 1064 updateControl.setArgumentGroupName( 1065 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1066 parser.addArgument(updateControl); 1067 1068 usePasswordPolicyControlOnUpdate = new BooleanArgument(null, 1069 "usePasswordPolicyControlOnUpdate", 1, 1070 INFO_PWMOD_ARG_DESC_USE_PW_POLICY_CONTROL_ON_UPDATE.get()); 1071 usePasswordPolicyControlOnUpdate.addLongIdentifier( 1072 "use-password-policy-control-on-update", true); 1073 usePasswordPolicyControlOnUpdate.addLongIdentifier( 1074 "usePWPolicyControlOnUpdate", true); 1075 usePasswordPolicyControlOnUpdate.addLongIdentifier( 1076 "use-pw-policy-control-on-update", true); 1077 usePasswordPolicyControlOnUpdate.setArgumentGroupName( 1078 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1079 parser.addArgument(usePasswordPolicyControlOnUpdate); 1080 1081 noOperation = new BooleanArgument(null, "noOperation", 1, 1082 INFO_PWMOD_ARG_DESC_NO_OPERATION.get()); 1083 noOperation.addLongIdentifier("no-operation", true); 1084 noOperation.addLongIdentifier("noOp", true); 1085 noOperation.addLongIdentifier("no-op", true); 1086 noOperation.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1087 parser.addArgument(noOperation); 1088 1089 getPasswordValidationDetails = new BooleanArgument(null, 1090 "getPasswordValidationDetails", 1, 1091 INFO_PWMOD_ARG_DESC_GET_PW_VALIDATION_DETAILS.get()); 1092 getPasswordValidationDetails.addLongIdentifier( 1093 "get-password-validation-details", true); 1094 getPasswordValidationDetails.addLongIdentifier("getPWValidationDetails", 1095 true); 1096 getPasswordValidationDetails.addLongIdentifier("get-pw-validation-details", 1097 true); 1098 getPasswordValidationDetails.setArgumentGroupName( 1099 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1100 parser.addArgument(getPasswordValidationDetails); 1101 1102 retireCurrentPassword = new BooleanArgument(null, "retireCurrentPassword", 1103 1, INFO_PWMOD_ARG_DESC_RETIRE_CURRENT_PASSWORD.get()); 1104 retireCurrentPassword.addLongIdentifier("retire-current-password", true); 1105 retireCurrentPassword.addLongIdentifier("retireCurrentPW", true); 1106 retireCurrentPassword.addLongIdentifier("retire-current-pw", true); 1107 retireCurrentPassword.addLongIdentifier("retirePassword", true); 1108 retireCurrentPassword.addLongIdentifier("retire-password", true); 1109 retireCurrentPassword.addLongIdentifier("retirePW", true); 1110 retireCurrentPassword.addLongIdentifier("retire-pw", true); 1111 retireCurrentPassword.setArgumentGroupName( 1112 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1113 parser.addArgument(retireCurrentPassword); 1114 1115 purgeCurrentPassword = new BooleanArgument(null, "purgeCurrentPassword", 1, 1116 INFO_PWMOD_ARG_DESC_PURGE_CURRENT_PASSWORD.get()); 1117 purgeCurrentPassword.addLongIdentifier("purge-current-password", true); 1118 purgeCurrentPassword.addLongIdentifier("purgeCurrentPW", true); 1119 purgeCurrentPassword.addLongIdentifier("purge-current-pw", true); 1120 purgeCurrentPassword.addLongIdentifier("purgePassword", true); 1121 purgeCurrentPassword.addLongIdentifier("purge-password", true); 1122 purgeCurrentPassword.addLongIdentifier("purgePW", true); 1123 purgeCurrentPassword.addLongIdentifier("purge-pw", true); 1124 purgeCurrentPassword.setArgumentGroupName( 1125 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1126 parser.addArgument(purgeCurrentPassword); 1127 1128 passwordUpdateBehavior = new StringArgument(null, 1129 "passwordUpdateBehavior", false, 0, 1130 INFO_PWMOD_ARG_PLACEHOLDER_NAME_VALUE.get(), 1131 INFO_PWMOD_ARG_DESC_PASSWORD_UPDATE_BEHAVIOR.get()); 1132 passwordUpdateBehavior.addLongIdentifier("password-update-behavior", true); 1133 passwordUpdateBehavior.addLongIdentifier("pwUpdateBehavior", true); 1134 passwordUpdateBehavior.addLongIdentifier("pw-update-behavior", true); 1135 passwordUpdateBehavior.addLongIdentifier("updateBehavior", true); 1136 passwordUpdateBehavior.addLongIdentifier("update-behavior", true); 1137 passwordUpdateBehavior.setArgumentGroupName( 1138 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1139 parser.addArgument(passwordUpdateBehavior); 1140 1141 useAssuredReplication = new BooleanArgument(null, "useAssuredReplication", 1142 1, INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION.get()); 1143 useAssuredReplication.addLongIdentifier("use-assured-replication", true); 1144 useAssuredReplication.addLongIdentifier("assuredReplication", true); 1145 useAssuredReplication.addLongIdentifier("assured-replication", true); 1146 useAssuredReplication.setArgumentGroupName( 1147 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1148 parser.addArgument(useAssuredReplication); 1149 1150 assuredReplicationLocalLevel = new StringArgument(null, 1151 "assuredReplicationLocalLevel", false, 1, 1152 INFO_PWMOD_ARG_PLACEHOLDER_LEVEL.get(), 1153 INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_LOCAL_LEVEL.get(), 1154 StaticUtils.setOf( 1155 ASSURED_REPLICATION_LOCAL_LEVEL_NONE, 1156 ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER, 1157 ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS)); 1158 assuredReplicationLocalLevel.addLongIdentifier( 1159 "assured-replication-local-level", true); 1160 assuredReplicationLocalLevel.addLongIdentifier("localLevel", true); 1161 assuredReplicationLocalLevel.addLongIdentifier("local-level", true); 1162 assuredReplicationLocalLevel.setArgumentGroupName( 1163 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1164 parser.addArgument(assuredReplicationLocalLevel); 1165 1166 assuredReplicationRemoteLevel = new StringArgument(null, 1167 "assuredReplicationRemoteLevel", false, 1, 1168 INFO_PWMOD_ARG_PLACEHOLDER_LEVEL.get(), 1169 INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_REMOTE_LEVEL.get(), 1170 StaticUtils.setOf( 1171 ASSURED_REPLICATION_REMOTE_LEVEL_NONE, 1172 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION, 1173 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS, 1174 ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS)); 1175 assuredReplicationRemoteLevel.addLongIdentifier( 1176 "assured-replication-remote-level", true); 1177 assuredReplicationRemoteLevel.addLongIdentifier("remoteLevel", true); 1178 assuredReplicationRemoteLevel.addLongIdentifier("remote-level", true); 1179 assuredReplicationRemoteLevel.setArgumentGroupName( 1180 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1181 parser.addArgument(assuredReplicationRemoteLevel); 1182 1183 assuredReplicationTimeout = new DurationArgument(null, 1184 "assuredReplicationTimeout", false, 1185 INFO_PWMOD_ARG_PLACEHOLDER_TIMEOUT.get(), 1186 INFO_PWMOD_ARG_DESC_ASSURED_REPLICATION_TIMEOUT.get()); 1187 assuredReplicationTimeout.addLongIdentifier("assured-replication-timeout", 1188 true); 1189 assuredReplicationTimeout.setArgumentGroupName( 1190 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1191 parser.addArgument(assuredReplicationTimeout); 1192 1193 operationPurpose = new StringArgument(null, "operationPurpose", false, 1, 1194 INFO_PWMOD_ARG_PLACEHOLDER_PURPOSE.get(), 1195 INFO_PWMOD_ARG_DESC_OPERATION_PURPOSE.get()); 1196 operationPurpose.addLongIdentifier("operation-purpose", true); 1197 operationPurpose.setArgumentGroupName( 1198 INFO_PWMOD_ARG_GROUP_UPDATE_CONTROL.get()); 1199 parser.addArgument(operationPurpose); 1200 1201 1202 // Other arguments 1203 passwordAttribute = new StringArgument(null, "passwordAttribute", false, 1, 1204 INFO_PWMOD_ARG_PLACEHOLDER_ATTRIBUTE_NAME.get(), 1205 INFO_PWMOD_ARG_DESC_PASSWORD_ATTRIBUTE.get(), 1206 DEFAULT_PASSWORD_ATTRIBUTE); 1207 passwordAttribute.addLongIdentifier("password-attribute", true); 1208 passwordAttribute.addLongIdentifier("passwordAttr", true); 1209 passwordAttribute.addLongIdentifier("password-attr", true); 1210 passwordAttribute.addLongIdentifier("pwAttribute", true); 1211 passwordAttribute.addLongIdentifier("pw-attribute", true); 1212 passwordAttribute.addLongIdentifier("pwAttr", true); 1213 passwordAttribute.addLongIdentifier("pw-attr", true); 1214 passwordAttribute.setArgumentGroupName( 1215 INFO_PWMOD_ARG_GROUP_OTHER.get()); 1216 1217 passwordChangeMethod = new StringArgument(null, "passwordChangeMethod", 1218 false, 1, INFO_PWMOD_ARG_PLACEHOLDER_CHANGE_METHOD.get(), 1219 INFO_PWMOD_ARG_DESC_PASSWORD_CHANGE_METHOD.get(), 1220 StaticUtils.setOf( 1221 PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP, 1222 PASSWORD_CHANGE_METHOD_LDAP_MOD, 1223 PASSWORD_CHANGE_METHOD_AD)); 1224 passwordChangeMethod.addLongIdentifier("password-change-method", true); 1225 passwordChangeMethod.addLongIdentifier("pwChangeMethod", true); 1226 passwordChangeMethod.addLongIdentifier("pw-change-method", true); 1227 passwordChangeMethod.addLongIdentifier("changeMethod", true); 1228 passwordChangeMethod.addLongIdentifier("change-method", true); 1229 passwordChangeMethod.addLongIdentifier("method", true); 1230 passwordChangeMethod.setArgumentGroupName( 1231 INFO_PWMOD_ARG_GROUP_OTHER.get()); 1232 parser.addArgument(passwordChangeMethod); 1233 1234 followReferrals = new BooleanArgument(null, "followReferrals", 1, 1235 INFO_PWMOD_ARG_DESC_FOLLOW_REFERRALS.get()); 1236 followReferrals.addLongIdentifier("follow-referrals", true); 1237 followReferrals.setArgumentGroupName( 1238 INFO_PWMOD_ARG_GROUP_OTHER.get()); 1239 parser.addArgument(followReferrals); 1240 1241 useAdministrativeSession = new BooleanArgument(null, 1242 "useAdministrativeSession", 1, 1243 INFO_PWMOD_ARG_DESC_USE_ADMIN_SESSION.get()); 1244 useAdministrativeSession.addLongIdentifier("use-administrative-session", 1245 true); 1246 useAdministrativeSession.addLongIdentifier("useAdminSession", true); 1247 useAdministrativeSession.addLongIdentifier("use-admin-session", true); 1248 useAdministrativeSession.addLongIdentifier("administrativeSession", true); 1249 useAdministrativeSession.addLongIdentifier("administrative-session", true); 1250 useAdministrativeSession.addLongIdentifier("adminSession", true); 1251 useAdministrativeSession.addLongIdentifier("admin-session", true); 1252 useAdministrativeSession.setArgumentGroupName( 1253 INFO_PWMOD_ARG_GROUP_OTHER.get()); 1254 parser.addArgument(useAdministrativeSession); 1255 1256 verbose = new BooleanArgument('v', "verbose", 1, 1257 INFO_PWMOD_ARG_DESC_VERBOSE.get()); 1258 verbose.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_OTHER.get()); 1259 parser.addArgument(verbose); 1260 1261 // This argument isn't actually used, but provides command-line backward 1262 // compatibility with an existing implementation. 1263 scriptFriendly = new BooleanArgument(null, "script-friendly", 1, 1264 INFO_PWMOD_ARG_DESC_SCRIPT_FRIENDLY.get()); 1265 scriptFriendly.setArgumentGroupName(INFO_PWMOD_ARG_GROUP_OTHER.get()); 1266 scriptFriendly.setHidden(true); 1267 parser.addArgument(scriptFriendly); 1268 1269 1270 // Argument constraints. 1271 parser.addExclusiveArgumentSet(userIdentity, provideBindDNAsUserIdentity); 1272 1273 final DNArgument bindDNArgument = 1274 parser.getDNArgument(BIND_DN_ARGUMENT_LONG_IDENTIFIER); 1275 parser.addDependentArgumentSet(provideBindDNAsUserIdentity, bindDNArgument); 1276 1277 parser.addExclusiveArgumentSet(newPassword, newPasswordFile, 1278 promptForNewPassword, generateClientSideNewPassword); 1279 1280 parser.addDependentArgumentSet(generatedPasswordLength, 1281 generateClientSideNewPassword); 1282 parser.addDependentArgumentSet(generatedPasswordCharacterSet, 1283 generateClientSideNewPassword); 1284 1285 parser.addExclusiveArgumentSet(currentPassword, currentPasswordFile, 1286 promptForCurrentPassword); 1287 1288 parser.addDependentArgumentSet(assuredReplicationLocalLevel, 1289 useAssuredReplication); 1290 parser.addDependentArgumentSet(assuredReplicationRemoteLevel, 1291 useAssuredReplication); 1292 parser.addDependentArgumentSet(assuredReplicationTimeout, 1293 useAssuredReplication); 1294 1295 parser.addExclusiveArgumentSet(retireCurrentPassword, purgeCurrentPassword); 1296 } 1297 1298 1299 1300 /** 1301 * {@inheritDoc} 1302 */ 1303 @Override() 1304 @NotNull() 1305 protected Set<Character> getSuppressedShortIdentifiers() 1306 { 1307 return StaticUtils.setOf('N'); 1308 } 1309 1310 1311 1312 /** 1313 * {@inheritDoc} 1314 */ 1315 @Override() 1316 public void doExtendedNonLDAPArgumentValidation() 1317 throws ArgumentException 1318 { 1319 // Make sure that if any generate password character sets were provided, 1320 // they must all be non-empty. 1321 if (generatedPasswordCharacterSet.isPresent()) 1322 { 1323 for (final String charSet : generatedPasswordCharacterSet.getValues()) 1324 { 1325 if (charSet.isEmpty()) 1326 { 1327 throw new ArgumentException(ERR_PWMOD_CHAR_SET_EMPTY.get( 1328 generatedPasswordCharacterSet.getIdentifierString())); 1329 } 1330 } 1331 } 1332 } 1333 1334 1335 1336 /** 1337 * {@inheritDoc} 1338 */ 1339 @Override() 1340 @NotNull() 1341 public ResultCode doToolProcessing() 1342 { 1343 LDAPConnectionPool pool = null; 1344 try 1345 { 1346 // Create a connection pool that will be used to communicate with the 1347 // directory server. If we should use an administrative session, then 1348 // create a connect processor that will be used to start the session 1349 // before performing the bind. 1350 try 1351 { 1352 final StartAdministrativeSessionPostConnectProcessor p; 1353 if (useAdministrativeSession.isPresent()) 1354 { 1355 p = new StartAdministrativeSessionPostConnectProcessor( 1356 new StartAdministrativeSessionExtendedRequest(getToolName(), 1357 true)); 1358 } 1359 else 1360 { 1361 p = null; 1362 } 1363 1364 pool = getConnectionPool(1, 2, 0, p, null, true, 1365 new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, 1366 verbose.isPresent())); 1367 1368 1369 // Figure out the method to use to update the password. 1370 final String updateMethod; 1371 try 1372 { 1373 updateMethod = getPasswordUpdateMethod(pool); 1374 } 1375 catch (final LDAPException e) 1376 { 1377 Debug.debugException(e); 1378 logCompletionMessage(true, e.getMessage()); 1379 return e.getResultCode(); 1380 } 1381 1382 1383 switch (updateMethod) 1384 { 1385 case PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP: 1386 return doPasswordModifyExtendedOperation(pool); 1387 1388 case PASSWORD_CHANGE_METHOD_AD: 1389 return doLDAPModifyPasswordUpdate(pool, true); 1390 1391 case PASSWORD_CHANGE_METHOD_LDAP_MOD: 1392 default: 1393 return doLDAPModifyPasswordUpdate(pool, false); 1394 } 1395 } 1396 catch (final LDAPException le) 1397 { 1398 Debug.debugException(le); 1399 1400 // Unable to create the connection pool, which means that either the 1401 // connection could not be established or the attempt to authenticate 1402 // the connection failed. If the bind failed, then the report bind 1403 // result health check should have already reported the bind failure. 1404 // If the failure was something else, then display that failure result. 1405 if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) 1406 { 1407 for (final String line : 1408 ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) 1409 { 1410 err(line); 1411 } 1412 } 1413 return le.getResultCode(); 1414 } 1415 } 1416 finally 1417 { 1418 if (pool != null) 1419 { 1420 pool.close(); 1421 } 1422 } 1423 } 1424 1425 1426 1427 /** 1428 * Determines the method that should be used to update the password. 1429 * 1430 * @param pool The connection pool to use to communicate with the 1431 * directory server, if appropriate. 1432 * 1433 * @return The method that should be used to update the password. The value 1434 * returned will be one of 1435 * {@link #PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP}, 1436 * {@link #PASSWORD_CHANGE_METHOD_LDAP_MOD}, or 1437 * {@link #PASSWORD_CHANGE_METHOD_AD}. 1438 * 1439 * @throws LDAPException If a problem occurs while attempting to make the 1440 * determination. 1441 */ 1442 @NotNull() 1443 private String getPasswordUpdateMethod(@NotNull final LDAPConnectionPool pool) 1444 throws LDAPException 1445 { 1446 if (passwordChangeMethod.isPresent()) 1447 { 1448 switch (StaticUtils.toLowerCase(passwordChangeMethod.getValue())) 1449 { 1450 case PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP: 1451 return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; 1452 case PASSWORD_CHANGE_METHOD_LDAP_MOD: 1453 return PASSWORD_CHANGE_METHOD_LDAP_MOD; 1454 case PASSWORD_CHANGE_METHOD_AD: 1455 return PASSWORD_CHANGE_METHOD_AD; 1456 } 1457 } 1458 1459 1460 // Retrieve the root DSE from the directory server. If we can't get the 1461 // root DSE, then default to the password modify extended operation. 1462 final RootDSE rootDSE; 1463 try 1464 { 1465 rootDSE = pool.getRootDSE(); 1466 } 1467 catch (final LDAPException e) 1468 { 1469 Debug.debugException(e); 1470 return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; 1471 } 1472 1473 if (rootDSE == null) 1474 { 1475 return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; 1476 } 1477 1478 1479 // If the root DSE claims support for the password modify extended 1480 // operation, then use that method. 1481 if (rootDSE.supportsExtendedOperation( 1482 PasswordModifyExtendedRequest.PASSWORD_MODIFY_REQUEST_OID)) 1483 { 1484 if (verbose.isPresent()) 1485 { 1486 wrapOut(0, WRAP_COLUMN, 1487 INFO_PWMOD_SELECTING_PW_MOD_EXTOP_METHOD.get()); 1488 } 1489 1490 return PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP; 1491 } 1492 1493 1494 // We need to differentiate between Active Directory and other types of 1495 // servers. Unfortunately, Active Directory doesn't seem to provide 1496 // vendorName or vendorVersion attributes in its root DSE, so we'll need to 1497 // use some other means of detecting it. Let's assume that if the server 1498 // advertises support for at least twenty supported controls in Microsoft's 1499 // OID range (starting with 1.2.840.113556), then it's an Active Directory 1500 // instance. At the time this was written, two different AD versions each 1501 // advertised support for nearly double that number. 1502 int numMicrosoftControlsSupported = 0; 1503 for (final String oid : rootDSE.getSupportedControlOIDs()) 1504 { 1505 if (oid.startsWith(MICROSOFT_BASE_OBJECT_IDENTIFIER + '.')) 1506 { 1507 numMicrosoftControlsSupported++; 1508 } 1509 } 1510 1511 if (numMicrosoftControlsSupported >= 20) 1512 { 1513 if (verbose.isPresent()) 1514 { 1515 wrapOut(0, WRAP_COLUMN, 1516 INFO_PWMOD_SELECTING_AD_METHOD_CONTROL_COUNT.get( 1517 numMicrosoftControlsSupported, 1518 MICROSOFT_BASE_OBJECT_IDENTIFIER)); 1519 } 1520 1521 return PASSWORD_CHANGE_METHOD_AD; 1522 } 1523 1524 1525 // Fall back to a default of a regular LDAP modify operation. 1526 if (verbose.isPresent()) 1527 { 1528 wrapOut(0, WRAP_COLUMN, 1529 INFO_PWMOD_DEFAULTING_TO_LDAP_MOD.get()); 1530 } 1531 1532 return PASSWORD_CHANGE_METHOD_LDAP_MOD; 1533 } 1534 1535 1536 1537 /** 1538 * Attempts a password modify extended operation to change the target user's 1539 * password. 1540 * 1541 * @param pool A connection pool to use to communicate with the directory 1542 * server. 1543 * 1544 * @return A result code that indicates whether the password update was 1545 * successful. 1546 */ 1547 @NotNull() 1548 private ResultCode doPasswordModifyExtendedOperation( 1549 @NotNull final LDAPConnectionPool pool) 1550 { 1551 // Create the password modify extended request to be processed. 1552 final String identity; 1553 final byte[] currentPW; 1554 final byte[] newPW; 1555 final Control[] controls; 1556 try 1557 { 1558 identity = getUserIdentity(null, false); 1559 currentPW = getCurrentPassword(); 1560 newPW = getNewPassword(); 1561 controls = getUpdateControls(); 1562 } 1563 catch (final LDAPException e) 1564 { 1565 Debug.debugException(e); 1566 logCompletionMessage(true, e.getMessage()); 1567 return e.getResultCode(); 1568 } 1569 1570 final PasswordModifyExtendedRequest passwordModifyRequest = 1571 new PasswordModifyExtendedRequest(identity, currentPW, newPW, 1572 controls); 1573 1574 1575 // Send the request and interpret the response, including special handling 1576 // for any referral that might have been returned. 1577 LDAPConnection connection = null; 1578 try 1579 { 1580 connection = pool.getConnection(); 1581 final PasswordModifyExtendedResult passwordModifyResult = 1582 (PasswordModifyExtendedResult) 1583 connection.processExtendedOperation(passwordModifyRequest); 1584 1585 out(); 1586 out(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); 1587 for (final String line : 1588 ResultUtils.formatResult(passwordModifyResult, true, 0, WRAP_COLUMN)) 1589 { 1590 out(line); 1591 } 1592 out(); 1593 1594 final String generatedPassword = 1595 passwordModifyResult.getGeneratedPassword(); 1596 if (passwordModifyResult.getResultCode() == ResultCode.SUCCESS) 1597 { 1598 logCompletionMessage(false, INFO_PWMOD_EXTOP_SUCCESSFUL.get()); 1599 if (generatedPassword != null) 1600 { 1601 out(); 1602 wrapOut(0, WRAP_COLUMN, 1603 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1604 } 1605 1606 return ResultCode.SUCCESS; 1607 } 1608 else if (passwordModifyResult.getResultCode() == ResultCode.NO_OPERATION) 1609 { 1610 logCompletionMessage(false, INFO_PWMOD_EXTOP_NO_OP.get()); 1611 if (generatedPassword != null) 1612 { 1613 out(); 1614 wrapOut(0, WRAP_COLUMN, 1615 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1616 } 1617 1618 return ResultCode.SUCCESS; 1619 } 1620 else if ((passwordModifyResult.getResultCode() == ResultCode.REFERRAL) && 1621 followReferrals.isPresent() && 1622 (passwordModifyResult.getReferralURLs().length > 0)) 1623 { 1624 // The LDAP SDK doesn't support automatic referral following for 1625 // extended operations. If appropriate, try to follow it ourselves. 1626 return followPasswordModifyReferral(passwordModifyRequest, 1627 passwordModifyResult, connection, 1); 1628 } 1629 else 1630 { 1631 logCompletionMessage(true, 1632 ERR_PWMOD_EXTOP_FAILED.get( 1633 String.valueOf(passwordModifyResult.getResultCode()), 1634 passwordModifyResult.getDiagnosticMessage())); 1635 return passwordModifyResult.getResultCode(); 1636 } 1637 } 1638 catch (final LDAPException e) 1639 { 1640 Debug.debugException(e); 1641 1642 err(); 1643 err(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); 1644 for (final String line : 1645 ResultUtils.formatResult(e, true, 0, WRAP_COLUMN)) 1646 { 1647 err(line); 1648 } 1649 err(); 1650 1651 if (connection != null) 1652 { 1653 pool.releaseDefunctConnection(connection); 1654 connection = null; 1655 } 1656 1657 logCompletionMessage(true, 1658 ERR_PWMOD_EXTOP_ERROR.get(String.valueOf(e.getResultCode()), 1659 e.getMessage())); 1660 return e.getResultCode(); 1661 } 1662 finally 1663 { 1664 if (connection != null) 1665 { 1666 pool.releaseConnection(connection); 1667 } 1668 } 1669 } 1670 1671 1672 1673 /** 1674 * Attempts to follow a referral that was returned in response to a password 1675 * modify extended request. 1676 * 1677 * @param request The extended request that was sent. 1678 * @param result The extended result that was received, 1679 * including the referral details. 1680 * @param receivedOnConnection The LDAP connection on which the referral 1681 * result was received. 1682 * @param referralCount The number of referrals that have been 1683 * returned so far. If this is too high, then 1684 * subsequent referrals will not be followed. 1685 * 1686 * @return A result code that indicates whether the password update was 1687 * successful. 1688 */ 1689 @NotNull() 1690 private ResultCode followPasswordModifyReferral( 1691 @NotNull final PasswordModifyExtendedRequest request, 1692 @NotNull final PasswordModifyExtendedResult result, 1693 @NotNull final LDAPConnection receivedOnConnection, 1694 final int referralCount) 1695 { 1696 final List<LDAPURL> referralURLs = new ArrayList<>(); 1697 for (final String urlString : result.getReferralURLs()) 1698 { 1699 try 1700 { 1701 referralURLs.add(new LDAPURL(urlString)); 1702 } 1703 catch (final LDAPException e) 1704 { 1705 Debug.debugException(e); 1706 } 1707 } 1708 1709 if (referralURLs.isEmpty()) 1710 { 1711 logCompletionMessage(true, 1712 ERR_PWMOD_EXTOP_NO_VALID_REFERRAL_URLS.get(String.valueOf(result))); 1713 return ResultCode.REFERRAL; 1714 } 1715 1716 LDAPException firstException = null; 1717 for (final LDAPURL url : referralURLs) 1718 { 1719 try (LDAPConnection referralConnection = 1720 receivedOnConnection.getReferralConnection(url, 1721 receivedOnConnection)) 1722 { 1723 final String referredUserIdentity; 1724 if (url.getBaseDN().isNullDN()) 1725 { 1726 referredUserIdentity = request.getUserIdentity(); 1727 } 1728 else 1729 { 1730 referredUserIdentity = url.getBaseDN().toString(); 1731 } 1732 1733 final PasswordModifyExtendedRequest referralRequest = 1734 new PasswordModifyExtendedRequest(referredUserIdentity, 1735 request.getOldPassword(), request.getNewPassword(), 1736 request.getControls()); 1737 final PasswordModifyExtendedResult referralResult = 1738 (PasswordModifyExtendedResult) 1739 referralConnection.processExtendedOperation(referralRequest); 1740 1741 out(); 1742 out(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); 1743 for (final String line : 1744 ResultUtils.formatResult(referralResult, true, 0, WRAP_COLUMN)) 1745 { 1746 out(line); 1747 } 1748 out(); 1749 1750 final String generatedPassword = referralResult.getGeneratedPassword(); 1751 if (referralResult.getResultCode() == ResultCode.SUCCESS) 1752 { 1753 logCompletionMessage(false, INFO_PWMOD_EXTOP_SUCCESSFUL.get()); 1754 if (generatedPassword != null) 1755 { 1756 out(); 1757 wrapOut(0, WRAP_COLUMN, 1758 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1759 } 1760 1761 return ResultCode.SUCCESS; 1762 } 1763 else if (referralResult.getResultCode() == ResultCode.NO_OPERATION) 1764 { 1765 logCompletionMessage(false, INFO_PWMOD_EXTOP_NO_OP.get()); 1766 if (generatedPassword != null) 1767 { 1768 out(); 1769 wrapOut(0, WRAP_COLUMN, 1770 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1771 } 1772 1773 return ResultCode.SUCCESS; 1774 } 1775 else if (referralResult.getResultCode() == ResultCode.REFERRAL) 1776 { 1777 final int maxReferralCount = receivedOnConnection. 1778 getConnectionOptions().getReferralHopLimit(); 1779 if (referralCount > maxReferralCount) 1780 { 1781 logCompletionMessage(true, 1782 ERR_PWMOD_TOO_MANY_REFERRALS.get()); 1783 return ResultCode.REFERRAL_LIMIT_EXCEEDED; 1784 } 1785 else 1786 { 1787 return followPasswordModifyReferral(referralRequest, referralResult, 1788 referralConnection, (referralCount + 1)); 1789 } 1790 } 1791 else 1792 { 1793 if (firstException == null) 1794 { 1795 firstException = new LDAPExtendedOperationException(referralResult); 1796 } 1797 } 1798 } 1799 catch (final LDAPException e) 1800 { 1801 Debug.debugException(e); 1802 if (firstException == null) 1803 { 1804 firstException = e; 1805 } 1806 } 1807 } 1808 1809 1810 logCompletionMessage(true, 1811 ERR_PWMOD_FOLLOW_REFERRAL_FAILED.get( 1812 String.valueOf(firstException.getResultCode()), 1813 firstException.getDiagnosticMessage())); 1814 return firstException.getResultCode(); 1815 } 1816 1817 1818 1819 /** 1820 * Attempts a regular LDAP modify operation to change the target user's 1821 * password. 1822 * 1823 * @param pool A connection pool to use to communicate with the 1824 * directory server. 1825 * @param isActiveDirectory Indicates whether the target directory server 1826 * is believed to be an Active Directory instance. 1827 * 1828 * @return A result code that indicates whether the password update was 1829 * successful. 1830 */ 1831 @NotNull() 1832 private ResultCode doLDAPModifyPasswordUpdate( 1833 @NotNull final LDAPConnectionPool pool, 1834 final boolean isActiveDirectory) 1835 { 1836 // Get the information to include in the password modify extended request. 1837 byte[] currentPW; 1838 byte[] newPW; 1839 final String identity; 1840 final Control[] controls; 1841 try 1842 { 1843 identity = getUserIdentity(pool, isActiveDirectory); 1844 currentPW = getCurrentPassword(); 1845 newPW = getNewPassword(); 1846 controls = getUpdateControls(); 1847 } 1848 catch (final LDAPException e) 1849 { 1850 Debug.debugException(e); 1851 logCompletionMessage(true, e.getMessage()); 1852 return e.getResultCode(); 1853 } 1854 1855 1856 // If there is no new password, then fail. 1857 if (newPW == null) 1858 { 1859 logCompletionMessage(true, 1860 ERR_PWMOD_NO_NEW_PW_FOR_MODIFY.get(newPassword.getIdentifierString(), 1861 newPasswordFile.getIdentifierString(), 1862 promptForNewPassword.getIdentifierString(), 1863 generateClientSideNewPassword.getIdentifierString())); 1864 return ResultCode.PARAM_ERROR; 1865 } 1866 1867 1868 // Determine the name of the attribute to modify. 1869 final String passwordAttr; 1870 if (isActiveDirectory) 1871 { 1872 passwordAttr = AD_PASSWORD_ATTRIBUTE; 1873 currentPW = encodePasswordForActiveDirectory(currentPW); 1874 newPW = encodePasswordForActiveDirectory(newPW); 1875 } 1876 else 1877 { 1878 passwordAttr = passwordAttribute.getValue(); 1879 } 1880 1881 1882 // Construct the modify request to send to the server. 1883 final ModifyRequest modifyRequest; 1884 if (currentPW == null) 1885 { 1886 modifyRequest = new ModifyRequest(identity, 1887 new Modification(ModificationType.REPLACE, passwordAttr, newPW)); 1888 } 1889 else 1890 { 1891 modifyRequest = new ModifyRequest(identity, 1892 new Modification(ModificationType.DELETE, passwordAttr, currentPW), 1893 new Modification(ModificationType.ADD, passwordAttr, newPW)); 1894 } 1895 1896 modifyRequest.setControls(controls); 1897 1898 1899 // Send the modify request and read the result. 1900 LDAPResult modifyResult; 1901 try 1902 { 1903 modifyResult = pool.modify(modifyRequest); 1904 } 1905 catch (final LDAPException e) 1906 { 1907 Debug.debugException(e); 1908 modifyResult = e.toLDAPResult(); 1909 } 1910 1911 1912 out(); 1913 out(INFO_PWMOD_MODIFY_RESULT_HEADER.get()); 1914 for (final String line : 1915 ResultUtils.formatResult(modifyResult, true, 0, WRAP_COLUMN)) 1916 { 1917 out(line); 1918 } 1919 out(); 1920 1921 if (modifyResult.getResultCode() == ResultCode.SUCCESS) 1922 { 1923 logCompletionMessage(false, INFO_PWMOD_MODIFY_SUCCESSFUL.get()); 1924 return ResultCode.SUCCESS; 1925 } 1926 else if (modifyResult.getResultCode() == ResultCode.NO_OPERATION) 1927 { 1928 logCompletionMessage(false, INFO_PWMOD_MODIFY_NO_OP.get()); 1929 return ResultCode.SUCCESS; 1930 } 1931 else 1932 { 1933 logCompletionMessage(true, 1934 ERR_PWMOD_MODIFY_FAILED.get( 1935 String.valueOf(modifyResult.getResultCode()), 1936 modifyResult.getDiagnosticMessage())); 1937 return modifyResult.getResultCode(); 1938 } 1939 } 1940 1941 1942 1943 /** 1944 * Encodes the provided password in the form that is needed when changing a 1945 * password in Active Directory. The password must be surrounded in 1946 * quotation marks and encoded as UTF-16 with little-Endian ordering. 1947 * 1948 * @param pw The password to be encoded. It may optionally be {@code null}. 1949 * 1950 * @return The encoded password. 1951 */ 1952 @Nullable() 1953 static byte[] encodePasswordForActiveDirectory(@Nullable final byte[] pw) 1954 { 1955 if (pw == null) 1956 { 1957 return null; 1958 } 1959 1960 final String quotedPassword = '"' + StaticUtils.toUTF8String(pw) + '"'; 1961 return quotedPassword.getBytes(StandardCharsets.UTF_16LE); 1962 } 1963 1964 1965 1966 /** 1967 * Retrieves the user identity for whom to update the password. 1968 * 1969 * @param pool A connection pool to use to communicate with the 1970 * directory server, if necessary. This may be 1971 * {@code null} if only an explicitly provided user 1972 * identity should be used. If it is 1973 * non-{@code null}, then an attempt will be made 1974 * to infer the correct value, and the value 1975 * returned will be a DN. 1976 * @param isActiveDirectory Indicates whether the target directory server 1977 * is believed to be an Active Directory instance. 1978 * 1979 * @return The user identity for whom to update the password. 1980 * 1981 * @throws LDAPException If a problem occurs while attempting to obtain the 1982 * user identity. 1983 */ 1984 @NotNull() 1985 private String getUserIdentity(@NotNull final LDAPConnectionPool pool, 1986 final boolean isActiveDirectory) 1987 throws LDAPException 1988 { 1989 String identity = null; 1990 final DNArgument bindDNArgument = 1991 argumentParser.getDNArgument(BIND_DN_ARGUMENT_LONG_IDENTIFIER); 1992 if (userIdentity.isPresent()) 1993 { 1994 identity = userIdentity.getValue(); 1995 } 1996 else if (provideBindDNAsUserIdentity.isPresent()) 1997 { 1998 identity = bindDNArgument.getStringValue(); 1999 if ((pool == null) && verbose.isPresent()) 2000 { 2001 out(); 2002 wrapOut(0, WRAP_COLUMN, 2003 INFO_PWMOD_USING_USER_IDENTITY_FROM_DN_FOR_EXTOP.get(identity)); 2004 } 2005 } 2006 else 2007 { 2008 if ((pool == null) && verbose.isPresent()) 2009 { 2010 out(); 2011 wrapOut(0, WRAP_COLUMN, 2012 INFO_PWMOD_OMITTING_USER_IDENTITY_FROM_EXTOP.get()); 2013 } 2014 } 2015 2016 if (pool == null) 2017 { 2018 return identity; 2019 } 2020 2021 if (identity == null) 2022 { 2023 if (bindDNArgument.isPresent()) 2024 { 2025 final DN bindDN = bindDNArgument.getValue(); 2026 if (! bindDN.isNullDN()) 2027 { 2028 return bindDN.toString(); 2029 } 2030 } 2031 2032 final WhoAmIExtendedRequest whoAmIRequest = new WhoAmIExtendedRequest(); 2033 try 2034 { 2035 final WhoAmIExtendedResult whoAmIResult = (WhoAmIExtendedResult) 2036 pool.processExtendedOperation(whoAmIRequest); 2037 if (whoAmIResult.getResultCode() == ResultCode.SUCCESS) 2038 { 2039 identity = whoAmIResult.getAuthorizationID(); 2040 } 2041 } 2042 catch (final LDAPException e) 2043 { 2044 Debug.debugException(e); 2045 } 2046 } 2047 2048 if (identity == null) 2049 { 2050 throw new LDAPException(ResultCode.PARAM_ERROR, 2051 ERR_PWMOD_CANNOT_DETERMINE_USER_IDENTITY.get( 2052 userIdentity.getIdentifierString())); 2053 } 2054 2055 2056 final String userDN; 2057 final String lowerIdentity = StaticUtils.toLowerCase(identity); 2058 if (lowerIdentity.startsWith("dn:")) 2059 { 2060 userDN = identity.substring(3).trim(); 2061 } 2062 else if (lowerIdentity.startsWith("u:")) 2063 { 2064 final String username = identity.substring(2).trim(); 2065 if (username.isEmpty()) 2066 { 2067 throw new LDAPException(ResultCode.PARAM_ERROR, 2068 ERR_PWMOD_USER_IDENTITY_EMPTY_USERNAME.get( 2069 userIdentity.getIdentifierString())); 2070 } 2071 2072 userDN = searchForUser(pool, username, isActiveDirectory); 2073 } 2074 else 2075 { 2076 userDN = identity; 2077 } 2078 2079 final DN parsedUserDN; 2080 try 2081 { 2082 parsedUserDN = new DN(userDN); 2083 } 2084 catch (final LDAPException e) 2085 { 2086 Debug.debugException(e); 2087 throw new LDAPException(ResultCode.PARAM_ERROR, 2088 ERR_PWMOD_USER_IDENTITY_NOT_VALID_DN.get(userDN, 2089 userIdentity.getIdentifierString()), 2090 e); 2091 } 2092 2093 if (parsedUserDN.isNullDN()) 2094 { 2095 throw new LDAPException(ResultCode.PARAM_ERROR, 2096 ERR_PWMOD_USER_IDENTITY_EMPTY_DN.get( 2097 userIdentity.getIdentifierString())); 2098 } 2099 2100 if (verbose.isPresent()) 2101 { 2102 out(); 2103 INFO_PWMOD_USER_IDENTITY_DN_FOR_MOD.get(userDN); 2104 } 2105 2106 return userDN; 2107 } 2108 2109 2110 2111 /** 2112 * Performs a search to determine the DN for the user with the given username. 2113 * 2114 * @param pool A connection pool to use to communicate with the 2115 * directory server. It must not be {@code null}. 2116 * @param username The username for the target user. It must not 2117 * be {@code null}. 2118 * @param isActiveDirectory Indicates whether the target directory server 2119 * is believed to be an Active Directory instance. 2120 * 2121 * @return The DN for the user with the given username. 2122 * 2123 * @throws LDAPException If a problem occurs while searching for the user, 2124 * or if the search does not match exactly one user. 2125 */ 2126 @NotNull() 2127 private String searchForUser(@NotNull final LDAPConnectionPool pool, 2128 @NotNull final String username, 2129 final boolean isActiveDirectory) 2130 throws LDAPException 2131 { 2132 // Construct the filter to use for the search. 2133 final List<String> filterAttributeNames; 2134 if (usernameAttribute.isPresent()) 2135 { 2136 filterAttributeNames = usernameAttribute.getValues(); 2137 } 2138 else if (isActiveDirectory) 2139 { 2140 filterAttributeNames = AD_USERNAME_ATTRIBUTES; 2141 } 2142 else 2143 { 2144 filterAttributeNames = DEFAULT_USERNAME_ATTRIBUTES; 2145 } 2146 2147 final Filter filter; 2148 if (filterAttributeNames.size() == 1) 2149 { 2150 filter = Filter.createEqualityFilter(filterAttributeNames.get(0), 2151 username); 2152 } 2153 else 2154 { 2155 final List<Filter> orComponents = 2156 new ArrayList<>(filterAttributeNames.size()); 2157 for (final String attrName : filterAttributeNames) 2158 { 2159 orComponents.add(Filter.createEqualityFilter(attrName, username)); 2160 } 2161 2162 filter = Filter.createORFilter(orComponents); 2163 } 2164 2165 2166 // Create the search request to use to find the target user entry. 2167 final SearchRequest searchRequest = new SearchRequest( 2168 searchBaseDN.getStringValue(), SearchScope.SUB, filter, 2169 SearchRequest.NO_ATTRIBUTES); 2170 searchRequest.setSizeLimit(1); 2171 2172 if (verbose.isPresent()) 2173 { 2174 out(); 2175 wrapOut(0, WRAP_COLUMN, 2176 INFO_PWMOD_ISSUING_SEARCH_FOR_USER.get( 2177 String.valueOf(searchRequest), username)); 2178 } 2179 2180 2181 // Issue the search and get the results. 2182 SearchResult searchResult; 2183 LDAPException searchException = null; 2184 try 2185 { 2186 searchResult = pool.search(searchRequest); 2187 } 2188 catch (final LDAPException e) 2189 { 2190 Debug.debugException(e); 2191 searchException = e; 2192 searchResult = new SearchResult(e); 2193 } 2194 2195 if (verbose.isPresent()) 2196 { 2197 out(); 2198 for (final String line : 2199 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2200 { 2201 out(line); 2202 } 2203 } 2204 2205 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2206 { 2207 if (searchResult.getEntryCount() == 1) 2208 { 2209 return searchResult.getSearchEntries().get(0).getDN(); 2210 } 2211 else 2212 { 2213 throw new LDAPException(ResultCode.NO_RESULTS_RETURNED, 2214 ERR_PWMOD_SEARCH_FOR_USER_NO_MATCHES.get(username)); 2215 } 2216 } 2217 else if (searchResult.getResultCode() == ResultCode.SIZE_LIMIT_EXCEEDED) 2218 { 2219 throw new LDAPException(ResultCode.SIZE_LIMIT_EXCEEDED, 2220 ERR_PWMOD_SEARCH_FOR_USER_MULTIPLE_MATCHES.get(username), 2221 searchException); 2222 } 2223 else 2224 { 2225 throw new LDAPException(searchResult.getResultCode(), 2226 ERR_PWMOD_SEARCH_FOR_USER_FAILED.get(username, 2227 String.valueOf(searchResult.getResultCode()), 2228 searchResult.getDiagnosticMessage()), 2229 searchException); 2230 } 2231 } 2232 2233 2234 2235 /** 2236 * Retrieves the bytes that comprise the current password for the user, if one 2237 * should be provided in the password update request. 2238 * 2239 * @return The bytes that comprise the current password for the user, or 2240 * {@code null} if none should be provided in the password update 2241 * request. 2242 * 2243 * @throws LDAPException If a problem occurs while trying to obtain the 2244 * current password. 2245 */ 2246 @Nullable() 2247 private byte[] getCurrentPassword() 2248 throws LDAPException 2249 { 2250 if (currentPassword.isPresent()) 2251 { 2252 return StaticUtils.getBytes(currentPassword.getValue()); 2253 } 2254 else if (currentPasswordFile.isPresent()) 2255 { 2256 final File f = currentPasswordFile.getValue(); 2257 try 2258 { 2259 final char[] currentPasswordChars = 2260 getPasswordFileReader().readPassword(f); 2261 return StaticUtils.getBytes(new String(currentPasswordChars)); 2262 } 2263 catch (final LDAPException e) 2264 { 2265 Debug.debugException(e); 2266 throw new LDAPException(e.getResultCode(), 2267 ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), 2268 e.getMessage()), 2269 e); 2270 } 2271 catch (final Exception e) 2272 { 2273 Debug.debugException(e); 2274 throw new LDAPException(ResultCode.LOCAL_ERROR, 2275 ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), 2276 StaticUtils.getExceptionMessage(e)), 2277 e); 2278 } 2279 } 2280 else if (promptForCurrentPassword.isPresent()) 2281 { 2282 while (true) 2283 { 2284 getOut().print(INFO_PWMOD_PROMPT_CURRENT_PW.get()); 2285 try 2286 { 2287 final byte[] pwBytes = PasswordReader.readPassword(); 2288 if ((pwBytes == null) || (pwBytes.length == 0)) 2289 { 2290 err(); 2291 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); 2292 err(); 2293 continue; 2294 } 2295 2296 return pwBytes; 2297 } 2298 catch (final Exception e) 2299 { 2300 throw new LDAPException(ResultCode.LOCAL_ERROR, 2301 ERR_PWMOD_CANNOT_PROMPT_FOR_CURRENT_PW.get( 2302 StaticUtils.getExceptionMessage(e)), 2303 e); 2304 } 2305 } 2306 } 2307 else 2308 { 2309 return null; 2310 } 2311 } 2312 2313 2314 2315 /** 2316 * Retrieves the bytes that comprise the new password for the user, if one 2317 * should be provided in the password update request. 2318 * 2319 * @return The bytes that comprise the new password for the user, or 2320 * {@code null} if none should be provided in the password update 2321 * request. 2322 * 2323 * @throws LDAPException If a problem occurs while trying to obtain the new 2324 * password. 2325 */ 2326 @Nullable() 2327 private byte[] getNewPassword() 2328 throws LDAPException 2329 { 2330 if (newPassword.isPresent()) 2331 { 2332 return StaticUtils.getBytes(newPassword.getValue()); 2333 } 2334 else if (newPasswordFile.isPresent()) 2335 { 2336 final File f = newPasswordFile.getValue(); 2337 try 2338 { 2339 final char[] newPasswordChars = getPasswordFileReader().readPassword(f); 2340 return StaticUtils.getBytes(new String(newPasswordChars)); 2341 } 2342 catch (final LDAPException e) 2343 { 2344 Debug.debugException(e); 2345 throw new LDAPException(e.getResultCode(), 2346 ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), 2347 e.getMessage()), 2348 e); 2349 } 2350 catch (final Exception e) 2351 { 2352 Debug.debugException(e); 2353 throw new LDAPException(ResultCode.LOCAL_ERROR, 2354 ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), 2355 StaticUtils.getExceptionMessage(e)), 2356 e); 2357 } 2358 } 2359 else if (promptForNewPassword.isPresent()) 2360 { 2361 while (true) 2362 { 2363 getOut().print(INFO_PWMOD_PROMPT_NEW_PW.get()); 2364 2365 final byte[] pwBytes; 2366 try 2367 { 2368 pwBytes = PasswordReader.readPassword(); 2369 if ((pwBytes == null) || (pwBytes.length == 0)) 2370 { 2371 err(); 2372 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); 2373 err(); 2374 continue; 2375 } 2376 2377 getOut().print(INFO_PWMOD_CONFIRM_NEW_PW.get()); 2378 final byte[] confirmBytes = PasswordReader.readPassword(); 2379 if ((confirmBytes == null) || 2380 (! Arrays.equals(pwBytes, confirmBytes))) 2381 { 2382 Arrays.fill(pwBytes, (byte) 0x00); 2383 Arrays.fill(confirmBytes, (byte) 0x00); 2384 2385 err(); 2386 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_NEW_PW_MISMATCH.get()); 2387 err(); 2388 continue; 2389 } 2390 2391 Arrays.fill(confirmBytes, (byte) 0x00); 2392 return pwBytes; 2393 } 2394 catch (final Exception e) 2395 { 2396 Debug.debugException(e); 2397 throw new LDAPException(ResultCode.LOCAL_ERROR, 2398 ERR_PWMOD_CANNOT_PROMPT_FOR_NEW_PW.get( 2399 StaticUtils.getExceptionMessage(e)), 2400 e); 2401 } 2402 } 2403 } 2404 else if (generateClientSideNewPassword.isPresent()) 2405 { 2406 return generatePassword(); 2407 } 2408 else 2409 { 2410 return null; 2411 } 2412 } 2413 2414 2415 2416 /** 2417 * Generates a new password for the user. 2418 * 2419 * @return The new password that was generated. 2420 */ 2421 @NotNull() 2422 private byte[] generatePassword() 2423 { 2424 final int length = generatedPasswordLength.getValue(); 2425 final StringBuilder generatedPassword = new StringBuilder(length); 2426 2427 final SecureRandom random = ThreadLocalSecureRandom.get(); 2428 final StringBuilder allPasswordCharacters = new StringBuilder(); 2429 for (final String charSet : generatedPasswordCharacterSet.getValues()) 2430 { 2431 allPasswordCharacters.append(charSet); 2432 2433 // Pick one character at random from the provided set to include in the 2434 // password. 2435 generatedPassword.append( 2436 charSet.charAt(random.nextInt(charSet.length()))); 2437 } 2438 2439 2440 // Choose as many additional characters (across all of the sets) as needed 2441 // to reach the desired length. 2442 while (generatedPassword.length() < length) 2443 { 2444 generatedPassword.append(allPasswordCharacters.charAt( 2445 random.nextInt(allPasswordCharacters.length()))); 2446 } 2447 2448 2449 // Scramble the generated password. 2450 final StringBuilder scrambledPassword = 2451 new StringBuilder(generatedPassword.length()); 2452 while (true) 2453 { 2454 if (generatedPassword.length() == 1) 2455 { 2456 scrambledPassword.append(generatedPassword.charAt(0)); 2457 break; 2458 } 2459 else 2460 { 2461 final int pos = random.nextInt(generatedPassword.length()); 2462 scrambledPassword.append(generatedPassword.charAt(pos)); 2463 generatedPassword.deleteCharAt(pos); 2464 } 2465 } 2466 2467 final String scrambledPasswordString = scrambledPassword.toString(); 2468 out(); 2469 wrapOut(0, WRAP_COLUMN, 2470 INFO_PWMOD_CLIENT_SIDE_GEN_PW.get(getToolName(), 2471 scrambledPasswordString)); 2472 return StaticUtils.getBytes(scrambledPasswordString); 2473 } 2474 2475 2476 2477 /** 2478 * Retrieves the controls that should be included in the password update 2479 * request. 2480 * 2481 * @return The controls that should be included in the password update 2482 * request, or an empty array if no controls should be included. 2483 * 2484 * @throws LDAPException If a problem occurs while trying to create any of 2485 * the controls. 2486 */ 2487 @NotNull() 2488 private Control[] getUpdateControls() 2489 throws LDAPException 2490 { 2491 final List<Control> controls = new ArrayList<>(); 2492 2493 if (updateControl.isPresent()) 2494 { 2495 controls.addAll(updateControl.getValues()); 2496 } 2497 2498 if (usePasswordPolicyControlOnUpdate.isPresent()) 2499 { 2500 controls.add(new PasswordPolicyRequestControl()); 2501 } 2502 2503 if (noOperation.isPresent()) 2504 { 2505 controls.add(new NoOpRequestControl()); 2506 } 2507 2508 if (getPasswordValidationDetails.isPresent()) 2509 { 2510 controls.add(new PasswordValidationDetailsRequestControl()); 2511 } 2512 2513 if (retireCurrentPassword.isPresent()) 2514 { 2515 controls.add(new RetirePasswordRequestControl(false)); 2516 } 2517 2518 if (purgeCurrentPassword.isPresent()) 2519 { 2520 controls.add(new PurgePasswordRequestControl(false)); 2521 } 2522 2523 if (passwordUpdateBehavior.isPresent()) 2524 { 2525 controls.add(LDAPModify.createPasswordUpdateBehaviorRequestControl( 2526 passwordUpdateBehavior.getIdentifierString(), 2527 passwordUpdateBehavior.getValues())); 2528 } 2529 2530 if (operationPurpose.isPresent()) 2531 { 2532 controls.add(new OperationPurposeRequestControl(false, getToolName(), 2533 getToolVersion(), 2534 LDAPPasswordModify.class.getName() + ".getUpdateControls", 2535 operationPurpose.getValue())); 2536 } 2537 2538 if (useAssuredReplication.isPresent()) 2539 { 2540 AssuredReplicationLocalLevel localLevel = null; 2541 if (assuredReplicationLocalLevel.isPresent()) 2542 { 2543 final String level = assuredReplicationLocalLevel.getValue(); 2544 if (level.equalsIgnoreCase(ASSURED_REPLICATION_LOCAL_LEVEL_NONE)) 2545 { 2546 localLevel = AssuredReplicationLocalLevel.NONE; 2547 } 2548 else if (level.equalsIgnoreCase( 2549 ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER)) 2550 { 2551 localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER; 2552 } 2553 else if (level.equalsIgnoreCase( 2554 ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS)) 2555 { 2556 localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS; 2557 } 2558 } 2559 2560 AssuredReplicationRemoteLevel remoteLevel = null; 2561 if (assuredReplicationRemoteLevel.isPresent()) 2562 { 2563 final String level = assuredReplicationRemoteLevel.getValue(); 2564 if (level.equalsIgnoreCase(ASSURED_REPLICATION_REMOTE_LEVEL_NONE)) 2565 { 2566 remoteLevel = AssuredReplicationRemoteLevel.NONE; 2567 } 2568 else if (level.equalsIgnoreCase( 2569 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION)) 2570 { 2571 remoteLevel = 2572 AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION; 2573 } 2574 else if (level.equalsIgnoreCase( 2575 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS)) 2576 { 2577 remoteLevel = 2578 AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS; 2579 } 2580 else if (level.equalsIgnoreCase( 2581 ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS)) 2582 { 2583 remoteLevel = 2584 AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS; 2585 } 2586 } 2587 2588 Long timeoutMillis = null; 2589 if (assuredReplicationTimeout.isPresent()) 2590 { 2591 timeoutMillis = 2592 assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS); 2593 } 2594 2595 controls.add(new AssuredReplicationRequestControl(true, localLevel, 2596 localLevel, remoteLevel, remoteLevel, timeoutMillis, false)); 2597 } 2598 2599 2600 return controls.toArray(StaticUtils.NO_CONTROLS); 2601 } 2602 2603 2604 2605 /** 2606 * Writes the provided message and sets it as the completion message. 2607 * 2608 * @param isError Indicates whether the message should be written to 2609 * standard error rather than standard output. 2610 * @param message The message to be written. 2611 */ 2612 private void logCompletionMessage(final boolean isError, 2613 @NotNull final String message) 2614 { 2615 completionMessage.compareAndSet(null, message); 2616 2617 if (isError) 2618 { 2619 wrapErr(0, WRAP_COLUMN, message); 2620 } 2621 else 2622 { 2623 wrapOut(0, WRAP_COLUMN, message); 2624 } 2625 } 2626 2627 2628 2629 /** 2630 * {@inheritDoc} 2631 */ 2632 @Override() 2633 public void handleUnsolicitedNotification( 2634 @NotNull final LDAPConnection connection, 2635 @NotNull final ExtendedResult notification) 2636 { 2637 final ArrayList<String> lines = new ArrayList<>(10); 2638 ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, 2639 WRAP_COLUMN); 2640 for (final String line : lines) 2641 { 2642 err(line); 2643 } 2644 err(); 2645 } 2646 2647 2648 2649 /** 2650 * {@inheritDoc} 2651 */ 2652 @Override() 2653 @NotNull() 2654 public LinkedHashMap<String[],String> getExampleUsages() 2655 { 2656 final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(); 2657 2658 examples.put( 2659 new String[] 2660 { 2661 "--hostname", "ds.example.com", 2662 "--port", "636", 2663 "--useSSL", 2664 "--userIdentity", "u:jdoe", 2665 "--promptForCurrentPassword", 2666 "--promptForNewPassword" 2667 }, 2668 INFO_PWMOD_EXAMPLE_1.get()); 2669 2670 examples.put( 2671 new String[] 2672 { 2673 "--hostname", "ds.example.com", 2674 "--port", "636", 2675 "--useSSL", 2676 "--bindDN", "uid=admin,dc=example,dc=com", 2677 "--bindPasswordFile", "admin-password.txt", 2678 "--userIdentity", "uid=jdoe,ou=People,dc=example,dc=com", 2679 "--generateClientSideNewPassword", 2680 "--passwordChangeMethod", "ldap-modify" 2681 }, 2682 INFO_PWMOD_EXAMPLE_2.get()); 2683 2684 return examples; 2685 } 2686}