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