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