001/* 002 * Copyright 2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2020 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 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.File; 041import java.io.OutputStream; 042import java.nio.charset.StandardCharsets; 043import java.security.SecureRandom; 044import java.util.ArrayList; 045import java.util.Arrays; 046import java.util.Collections; 047import java.util.LinkedHashMap; 048import java.util.List; 049import java.util.Set; 050import java.util.concurrent.TimeUnit; 051import java.util.concurrent.atomic.AtomicReference; 052 053import com.unboundid.ldap.sdk.Control; 054import com.unboundid.ldap.sdk.DN; 055import com.unboundid.ldap.sdk.ExtendedResult; 056import com.unboundid.ldap.sdk.Filter; 057import com.unboundid.ldap.sdk.LDAPConnection; 058import com.unboundid.ldap.sdk.LDAPConnectionOptions; 059import com.unboundid.ldap.sdk.LDAPConnectionPool; 060import com.unboundid.ldap.sdk.LDAPException; 061import com.unboundid.ldap.sdk.LDAPExtendedOperationException; 062import com.unboundid.ldap.sdk.LDAPResult; 063import com.unboundid.ldap.sdk.LDAPURL; 064import com.unboundid.ldap.sdk.Modification; 065import com.unboundid.ldap.sdk.ModificationType; 066import com.unboundid.ldap.sdk.ModifyRequest; 067import com.unboundid.ldap.sdk.ResultCode; 068import com.unboundid.ldap.sdk.RootDSE; 069import com.unboundid.ldap.sdk.SearchRequest; 070import com.unboundid.ldap.sdk.SearchResult; 071import com.unboundid.ldap.sdk.SearchScope; 072import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler; 073import com.unboundid.ldap.sdk.Version; 074import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 075import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedRequest; 076import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedResult; 077import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedRequest; 078import com.unboundid.ldap.sdk.extensions.WhoAmIExtendedResult; 079import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel; 080import com.unboundid.ldap.sdk.unboundidds.controls. 081 AssuredReplicationRemoteLevel; 082import com.unboundid.ldap.sdk.unboundidds.controls. 083 AssuredReplicationRequestControl; 084import com.unboundid.ldap.sdk.unboundidds.controls. 085 GetAuthorizationEntryRequestControl; 086import com.unboundid.ldap.sdk.unboundidds.controls. 087 GetUserResourceLimitsRequestControl; 088import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl; 089import com.unboundid.ldap.sdk.unboundidds.controls. 090 OperationPurposeRequestControl; 091import com.unboundid.ldap.sdk.unboundidds.controls. 092 PasswordPolicyRequestControl; 093import com.unboundid.ldap.sdk.unboundidds.controls. 094 PasswordValidationDetailsRequestControl; 095import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl; 096import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl; 097import com.unboundid.ldap.sdk.unboundidds.extensions. 098 StartAdministrativeSessionExtendedRequest; 099import com.unboundid.ldap.sdk.unboundidds.extensions. 100 StartAdministrativeSessionPostConnectProcessor; 101import com.unboundid.util.Debug; 102import com.unboundid.util.LDAPCommandLineTool; 103import com.unboundid.util.NotNull; 104import com.unboundid.util.Nullable; 105import com.unboundid.util.PasswordReader; 106import com.unboundid.util.StaticUtils; 107import com.unboundid.util.ThreadSafety; 108import com.unboundid.util.ThreadSafetyLevel; 109import com.unboundid.util.args.ArgumentException; 110import com.unboundid.util.args.ArgumentParser; 111import com.unboundid.util.args.BooleanArgument; 112import com.unboundid.util.args.ControlArgument; 113import com.unboundid.util.args.DNArgument; 114import com.unboundid.util.args.DurationArgument; 115import com.unboundid.util.args.FileArgument; 116import com.unboundid.util.args.IntegerArgument; 117import com.unboundid.util.args.StringArgument; 118 119import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 120 121 122 123/** 124 * This class provides an implementation of an LDAP command-line tool that may 125 * be used to change passwords in a directory server. Three types of password 126 * changes are supported: the password modify extended operation (as described 127 * in <A HREF="http://www.ietf.org/rfc/rfc3062.txt">RFC 3062</A>), a standard 128 * LDAP modify operation that targets an attribute like userPassword, or an 129 * Active Directory-specific password change that uses an LDAP modify operation 130 * to replace the value of the unicodePwd attribute with a value that is the 131 * password surrounded by quotation marks and encoded with UTF-16-LE. 132 * <BR> 133 * <BLOCKQUOTE> 134 * <B>NOTE:</B> This class, and other classes within the 135 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 136 * supported for use against Ping Identity, UnboundID, and 137 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 138 * for proprietary functionality or for external specifications that are not 139 * considered stable or mature enough to be guaranteed to work in an 140 * interoperable way with other types of LDAP servers. 141 * </BLOCKQUOTE> 142 */ 143@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 144public final class LDAPPasswordModify 145 extends LDAPCommandLineTool 146 implements UnsolicitedNotificationHandler 147{ 148 /** 149 * The column at which output should be wrapped. 150 */ 151 private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 152 153 154 155 /** 156 * The assured replication local level value that indicates no assurance is 157 * needed. 158 */ 159 @NotNull private static final String ASSURED_REPLICATION_LOCAL_LEVEL_NONE = 160 "none"; 161 162 163 164 /** 165 * The assured replication local level value that indicates the change should 166 * be received by at least one other local server. 167 */ 168 @NotNull private static final String 169 ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER = 170 "received-any-server"; 171 172 173 174 /** 175 * The assured replication local level value that indicates the change should 176 * be processed by all available local servers. 177 */ 178 @NotNull private static final String 179 ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS = 180 "processed-all-servers"; 181 182 183 184 /** 185 * The assured replication remote level value that indicates no assurance is 186 * needed. 187 */ 188 @NotNull private static final String ASSURED_REPLICATION_REMOTE_LEVEL_NONE = 189 "none"; 190 191 192 193 /** 194 * The assured replication remote level value that indicates the change should 195 * be received by at least one other remote server in at least one remote 196 * location. 197 */ 198 @NotNull private static final String 199 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION = 200 "received-any-remote-location"; 201 202 203 204 /** 205 * The assured replication remote level value that indicates the change should 206 * be received by at least one other remote server in every remote 207 * location. 208 */ 209 @NotNull private static final String 210 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS = 211 "received-all-remote-locations"; 212 213 214 215 /** 216 * The assured replication remote level value that indicates the change should 217 * be processed by all available remote servers in all locations. 218 */ 219 @NotNull private static final String 220 ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS = 221 "processed-all-remote-servers"; 222 223 224 225 /** 226 * The password change method that will be used to indicate that the password 227 * modify extended operation should be used. 228 */ 229 @NotNull private static final String PASSWORD_CHANGE_METHOD_PW_MOD_EXTOP = 230 "password-modify-extended-operation"; 231 232 233 234 /** 235 * The password change method that will be used to indicate that a regular 236 * LDAP modify operation should be used. 237 */ 238 @NotNull private static final String PASSWORD_CHANGE_METHOD_LDAP_MOD = 239 "ldap-modify"; 240 241 242 243 /** 244 * The password change method that will be used to indicate that an 245 * Active Directory-specific operation should be used. 246 */ 247 @NotNull private static final String PASSWORD_CHANGE_METHOD_AD = 248 "active-directory"; 249 250 251 252 /** 253 * The long identifier for the {@link LDAPCommandLineTool} argument used to 254 * specify the bind DN to use when authenticating to the directory server. 255 */ 256 @NotNull private static final String BIND_DN_ARGUMENT_LONG_IDENTIFIER = 257 "bindDN"; 258 259 260 261 /** 262 * The name of the default attribute that will be assumed to hold the password 263 * in most directory servers. 264 */ 265 @NotNull private static final String DEFAULT_PASSWORD_ATTRIBUTE = 266 "userPassword"; 267 268 269 270 /** 271 * The name of the attribute that Active Directory uses to hold the password. 272 */ 273 @NotNull private static final String AD_PASSWORD_ATTRIBUTE = "unicodePwd"; 274 275 276 277 /** 278 * The names of the attributes that will be used when searching for an entry 279 * from its username in most directory servers. 280 */ 281 @NotNull private static final List<String> DEFAULT_USERNAME_ATTRIBUTES = 282 Collections.singletonList("uid"); 283 284 285 286 /** 287 * The names of the attributes that will be used when searching for an entry 288 * from its username in an Active Directory server. 289 */ 290 @NotNull private static final List<String> AD_USERNAME_ATTRIBUTES = 291 Collections.unmodifiableList(Arrays.asList("samAccountName", 292 "userPrincipalName")); 293 294 295 296 /** 297 * The OID base that has been assigned to Microsoft. 298 */ 299 @NotNull private static final String MICROSOFT_BASE_OID = "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_OID + '.')) 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, MICROSOFT_BASE_OID)); 1516 } 1517 1518 return PASSWORD_CHANGE_METHOD_AD; 1519 } 1520 1521 1522 // Fall back to a default of a regular LDAP modify operation. 1523 if (verbose.isPresent()) 1524 { 1525 wrapOut(0, WRAP_COLUMN, 1526 INFO_PWMOD_DEFAULTING_TO_LDAP_MOD.get()); 1527 } 1528 1529 return PASSWORD_CHANGE_METHOD_LDAP_MOD; 1530 } 1531 1532 1533 1534 /** 1535 * Attempts a password modify extended operation to change the target user's 1536 * password. 1537 * 1538 * @param pool A connection pool to use to communicate with the directory 1539 * server. 1540 * 1541 * @return A result code that indicates whether the password update was 1542 * successful. 1543 */ 1544 @NotNull() 1545 private ResultCode doPasswordModifyExtendedOperation( 1546 @NotNull final LDAPConnectionPool pool) 1547 { 1548 // Create the password modify extended request to be processed. 1549 final String identity; 1550 final byte[] currentPW; 1551 final byte[] newPW; 1552 final Control[] controls; 1553 try 1554 { 1555 identity = getUserIdentity(null, false); 1556 currentPW = getCurrentPassword(); 1557 newPW = getNewPassword(); 1558 controls = getUpdateControls(); 1559 } 1560 catch (final LDAPException e) 1561 { 1562 Debug.debugException(e); 1563 logCompletionMessage(true, e.getMessage()); 1564 return e.getResultCode(); 1565 } 1566 1567 final PasswordModifyExtendedRequest passwordModifyRequest = 1568 new PasswordModifyExtendedRequest(identity, currentPW, newPW, 1569 controls); 1570 1571 1572 // Send the request and interpret the response, including special handling 1573 // for any referral that might have been returned. 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 if ((passwordModifyResult.getResultCode() == ResultCode.REFERRAL) && 1618 followReferrals.isPresent() && 1619 (passwordModifyResult.getReferralURLs().length > 0)) 1620 { 1621 // The LDAP SDK doesn't support automatic referral following for 1622 // extended operations. If appropriate, try to follow it ourselves. 1623 return followPasswordModifyReferral(passwordModifyRequest, 1624 passwordModifyResult, connection, 1); 1625 } 1626 else 1627 { 1628 logCompletionMessage(true, 1629 ERR_PWMOD_EXTOP_FAILED.get( 1630 String.valueOf(passwordModifyResult.getResultCode()), 1631 passwordModifyResult.getDiagnosticMessage())); 1632 return passwordModifyResult.getResultCode(); 1633 } 1634 } 1635 catch (final LDAPException e) 1636 { 1637 Debug.debugException(e); 1638 1639 err(); 1640 err(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); 1641 for (final String line : 1642 ResultUtils.formatResult(e, true, 0, WRAP_COLUMN)) 1643 { 1644 err(line); 1645 } 1646 err(); 1647 1648 if (connection != null) 1649 { 1650 pool.releaseDefunctConnection(connection); 1651 connection = null; 1652 } 1653 1654 logCompletionMessage(true, 1655 ERR_PWMOD_EXTOP_ERROR.get(String.valueOf(e.getResultCode()), 1656 e.getMessage())); 1657 return e.getResultCode(); 1658 } 1659 finally 1660 { 1661 if (connection != null) 1662 { 1663 pool.releaseConnection(connection); 1664 } 1665 } 1666 } 1667 1668 1669 1670 /** 1671 * Attempts to follow a referral that was returned in response to a password 1672 * modify extended request. 1673 * 1674 * @param request The extended request that was sent. 1675 * @param result The extended result that was received, 1676 * including the referral details. 1677 * @param receivedOnConnection The LDAP connection on which the referral 1678 * result was received. 1679 * @param referralCount The number of referrals that have been 1680 * returned so far. If this is too high, then 1681 * subsequent referrals will not be followed. 1682 * 1683 * @return A result code that indicates whether the password update was 1684 * successful. 1685 */ 1686 @NotNull() 1687 private ResultCode followPasswordModifyReferral( 1688 @NotNull final PasswordModifyExtendedRequest request, 1689 @NotNull final PasswordModifyExtendedResult result, 1690 @NotNull final LDAPConnection receivedOnConnection, 1691 final int referralCount) 1692 { 1693 final List<LDAPURL> referralURLs = new ArrayList<>(); 1694 for (final String urlString : result.getReferralURLs()) 1695 { 1696 try 1697 { 1698 referralURLs.add(new LDAPURL(urlString)); 1699 } 1700 catch (final LDAPException e) 1701 { 1702 Debug.debugException(e); 1703 } 1704 } 1705 1706 if (referralURLs.isEmpty()) 1707 { 1708 logCompletionMessage(true, 1709 ERR_PWMOD_EXTOP_NO_VALID_REFERRAL_URLS.get(String.valueOf(result))); 1710 return ResultCode.REFERRAL; 1711 } 1712 1713 LDAPException firstException = null; 1714 for (final LDAPURL url : referralURLs) 1715 { 1716 try (LDAPConnection referralConnection = 1717 receivedOnConnection.getReferralConnection(url, 1718 receivedOnConnection)) 1719 { 1720 final String referredUserIdentity; 1721 if (url.getBaseDN().isNullDN()) 1722 { 1723 referredUserIdentity = request.getUserIdentity(); 1724 } 1725 else 1726 { 1727 referredUserIdentity = url.getBaseDN().toString(); 1728 } 1729 1730 final PasswordModifyExtendedRequest referralRequest = 1731 new PasswordModifyExtendedRequest(referredUserIdentity, 1732 request.getOldPassword(), request.getNewPassword(), 1733 request.getControls()); 1734 final PasswordModifyExtendedResult referralResult = 1735 (PasswordModifyExtendedResult) 1736 referralConnection.processExtendedOperation(referralRequest); 1737 1738 out(); 1739 out(INFO_PWMOD_EXTOP_RESULT_HEADER.get()); 1740 for (final String line : 1741 ResultUtils.formatResult(referralResult, true, 0, WRAP_COLUMN)) 1742 { 1743 out(line); 1744 } 1745 out(); 1746 1747 final String generatedPassword = referralResult.getGeneratedPassword(); 1748 if (referralResult.getResultCode() == ResultCode.SUCCESS) 1749 { 1750 logCompletionMessage(false, INFO_PWMOD_EXTOP_SUCCESSFUL.get()); 1751 if (generatedPassword != null) 1752 { 1753 out(); 1754 wrapOut(0, WRAP_COLUMN, 1755 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1756 } 1757 1758 return ResultCode.SUCCESS; 1759 } 1760 else if (referralResult.getResultCode() == ResultCode.NO_OPERATION) 1761 { 1762 logCompletionMessage(false, INFO_PWMOD_EXTOP_NO_OP.get()); 1763 if (generatedPassword != null) 1764 { 1765 out(); 1766 wrapOut(0, WRAP_COLUMN, 1767 INFO_PWMOD_SERVER_GENERATED_PW.get(generatedPassword)); 1768 } 1769 1770 return ResultCode.SUCCESS; 1771 } 1772 else if (referralResult.getResultCode() == ResultCode.REFERRAL) 1773 { 1774 final int maxReferralCount = receivedOnConnection. 1775 getConnectionOptions().getReferralHopLimit(); 1776 if (referralCount > maxReferralCount) 1777 { 1778 logCompletionMessage(true, 1779 ERR_PWMOD_TOO_MANY_REFERRALS.get()); 1780 return ResultCode.REFERRAL_LIMIT_EXCEEDED; 1781 } 1782 else 1783 { 1784 return followPasswordModifyReferral(referralRequest, referralResult, 1785 referralConnection, (referralCount + 1)); 1786 } 1787 } 1788 else 1789 { 1790 if (firstException == null) 1791 { 1792 firstException = new LDAPExtendedOperationException(referralResult); 1793 } 1794 } 1795 } 1796 catch (final LDAPException e) 1797 { 1798 Debug.debugException(e); 1799 if (firstException == null) 1800 { 1801 firstException = e; 1802 } 1803 } 1804 } 1805 1806 1807 logCompletionMessage(true, 1808 ERR_PWMOD_FOLLOW_REFERRAL_FAILED.get( 1809 String.valueOf(firstException.getResultCode()), 1810 firstException.getDiagnosticMessage())); 1811 return firstException.getResultCode(); 1812 } 1813 1814 1815 1816 /** 1817 * Attempts a regular LDAP modify operation to change the target user's 1818 * password. 1819 * 1820 * @param pool A connection pool to use to communicate with the 1821 * directory server. 1822 * @param isActiveDirectory Indicates whether the target directory server 1823 * is believed to be an Active Directory instance. 1824 * 1825 * @return A result code that indicates whether the password update was 1826 * successful. 1827 */ 1828 @NotNull() 1829 private ResultCode doLDAPModifyPasswordUpdate( 1830 @NotNull final LDAPConnectionPool pool, 1831 final boolean isActiveDirectory) 1832 { 1833 // Get the information to include in the password modify extended request. 1834 byte[] currentPW; 1835 byte[] newPW; 1836 final String identity; 1837 final Control[] controls; 1838 try 1839 { 1840 identity = getUserIdentity(pool, isActiveDirectory); 1841 currentPW = getCurrentPassword(); 1842 newPW = getNewPassword(); 1843 controls = getUpdateControls(); 1844 } 1845 catch (final LDAPException e) 1846 { 1847 Debug.debugException(e); 1848 logCompletionMessage(true, e.getMessage()); 1849 return e.getResultCode(); 1850 } 1851 1852 1853 // If there is no new password, then fail. 1854 if (newPW == null) 1855 { 1856 logCompletionMessage(true, 1857 ERR_PWMOD_NO_NEW_PW_FOR_MODIFY.get(newPassword.getIdentifierString(), 1858 newPasswordFile.getIdentifierString(), 1859 promptForNewPassword.getIdentifierString(), 1860 generateClientSideNewPassword.getIdentifierString())); 1861 return ResultCode.PARAM_ERROR; 1862 } 1863 1864 1865 // Determine the name of the attribute to modify. 1866 final String passwordAttr; 1867 if (isActiveDirectory) 1868 { 1869 passwordAttr = AD_PASSWORD_ATTRIBUTE; 1870 currentPW = encodePasswordForActiveDirectory(currentPW); 1871 newPW = encodePasswordForActiveDirectory(newPW); 1872 } 1873 else 1874 { 1875 passwordAttr = passwordAttribute.getValue(); 1876 } 1877 1878 1879 // Construct the modify request to send to the server. 1880 final ModifyRequest modifyRequest; 1881 if (currentPW == null) 1882 { 1883 modifyRequest = new ModifyRequest(identity, 1884 new Modification(ModificationType.REPLACE, passwordAttr, newPW)); 1885 } 1886 else 1887 { 1888 modifyRequest = new ModifyRequest(identity, 1889 new Modification(ModificationType.DELETE, passwordAttr, currentPW), 1890 new Modification(ModificationType.ADD, passwordAttr, newPW)); 1891 } 1892 1893 modifyRequest.setControls(controls); 1894 1895 1896 // Send the modify request and read the result. 1897 LDAPResult modifyResult; 1898 try 1899 { 1900 modifyResult = pool.modify(modifyRequest); 1901 } 1902 catch (final LDAPException e) 1903 { 1904 Debug.debugException(e); 1905 modifyResult = e.toLDAPResult(); 1906 } 1907 1908 1909 out(); 1910 out(INFO_PWMOD_MODIFY_RESULT_HEADER.get()); 1911 for (final String line : 1912 ResultUtils.formatResult(modifyResult, true, 0, WRAP_COLUMN)) 1913 { 1914 out(line); 1915 } 1916 out(); 1917 1918 if (modifyResult.getResultCode() == ResultCode.SUCCESS) 1919 { 1920 logCompletionMessage(false, INFO_PWMOD_MODIFY_SUCCESSFUL.get()); 1921 return ResultCode.SUCCESS; 1922 } 1923 else if (modifyResult.getResultCode() == ResultCode.NO_OPERATION) 1924 { 1925 logCompletionMessage(false, INFO_PWMOD_MODIFY_NO_OP.get()); 1926 return ResultCode.SUCCESS; 1927 } 1928 else 1929 { 1930 logCompletionMessage(true, 1931 ERR_PWMOD_MODIFY_FAILED.get( 1932 String.valueOf(modifyResult.getResultCode()), 1933 modifyResult.getDiagnosticMessage())); 1934 return modifyResult.getResultCode(); 1935 } 1936 } 1937 1938 1939 1940 /** 1941 * Encodes the provided password in the form that is needed when changing a 1942 * password in Active Directory. The password must be surrounded in 1943 * quotation marks and encoded as UTF-16 with little-Endian ordering. 1944 * 1945 * @param pw The password to be encoded. It may optionally be {@code null}. 1946 * 1947 * @return The encoded password. 1948 */ 1949 @Nullable() 1950 static byte[] encodePasswordForActiveDirectory(@Nullable final byte[] pw) 1951 { 1952 if (pw == null) 1953 { 1954 return null; 1955 } 1956 1957 final String quotedPassword = '"' + StaticUtils.toUTF8String(pw) + '"'; 1958 return quotedPassword.getBytes(StandardCharsets.UTF_16LE); 1959 } 1960 1961 1962 1963 /** 1964 * Retrieves the user identity for whom to update the password. 1965 * 1966 * @param pool A connection pool to use to communicate with the 1967 * directory server, if necessary. This may be 1968 * {@code null} if only an explicitly provided user 1969 * identity should be used. If it is 1970 * non-{@code null}, then an attempt will be made 1971 * to infer the correct value, and the value 1972 * returned will be a DN. 1973 * @param isActiveDirectory Indicates whether the target directory server 1974 * is believed to be an Active Directory instance. 1975 * 1976 * @return The user identity for whom to update the password. 1977 * 1978 * @throws LDAPException If a problem occurs while attempting to obtain the 1979 * user identity. 1980 */ 1981 @NotNull() 1982 private String getUserIdentity(@NotNull final LDAPConnectionPool pool, 1983 final boolean isActiveDirectory) 1984 throws LDAPException 1985 { 1986 String identity = null; 1987 final DNArgument bindDNArgument = 1988 argumentParser.getDNArgument(BIND_DN_ARGUMENT_LONG_IDENTIFIER); 1989 if (userIdentity.isPresent()) 1990 { 1991 identity = userIdentity.getValue(); 1992 } 1993 else if (provideBindDNAsUserIdentity.isPresent()) 1994 { 1995 identity = bindDNArgument.getStringValue(); 1996 if ((pool == null) && verbose.isPresent()) 1997 { 1998 out(); 1999 wrapOut(0, WRAP_COLUMN, 2000 INFO_PWMOD_USING_USER_IDENTITY_FROM_DN_FOR_EXTOP.get(identity)); 2001 } 2002 } 2003 else 2004 { 2005 if ((pool == null) && verbose.isPresent()) 2006 { 2007 out(); 2008 wrapOut(0, WRAP_COLUMN, 2009 INFO_PWMOD_OMITTING_USER_IDENTITY_FROM_EXTOP.get()); 2010 } 2011 } 2012 2013 if (pool == null) 2014 { 2015 return identity; 2016 } 2017 2018 if (identity == null) 2019 { 2020 if (bindDNArgument.isPresent()) 2021 { 2022 final DN bindDN = bindDNArgument.getValue(); 2023 if (! bindDN.isNullDN()) 2024 { 2025 return bindDN.toString(); 2026 } 2027 } 2028 2029 final WhoAmIExtendedRequest whoAmIRequest = new WhoAmIExtendedRequest(); 2030 try 2031 { 2032 final WhoAmIExtendedResult whoAmIResult = (WhoAmIExtendedResult) 2033 pool.processExtendedOperation(whoAmIRequest); 2034 if (whoAmIResult.getResultCode() == ResultCode.SUCCESS) 2035 { 2036 identity = whoAmIResult.getAuthorizationID(); 2037 } 2038 } 2039 catch (final LDAPException e) 2040 { 2041 Debug.debugException(e); 2042 } 2043 } 2044 2045 if (identity == null) 2046 { 2047 throw new LDAPException(ResultCode.PARAM_ERROR, 2048 ERR_PWMOD_CANNOT_DETERMINE_USER_IDENTITY.get( 2049 userIdentity.getIdentifierString())); 2050 } 2051 2052 2053 final String userDN; 2054 final String lowerIdentity = StaticUtils.toLowerCase(identity); 2055 if (lowerIdentity.startsWith("dn:")) 2056 { 2057 userDN = identity.substring(3).trim(); 2058 } 2059 else if (lowerIdentity.startsWith("u:")) 2060 { 2061 final String username = identity.substring(2).trim(); 2062 if (username.isEmpty()) 2063 { 2064 throw new LDAPException(ResultCode.PARAM_ERROR, 2065 ERR_PWMOD_USER_IDENTITY_EMPTY_USERNAME.get( 2066 userIdentity.getIdentifierString())); 2067 } 2068 2069 userDN = searchForUser(pool, username, isActiveDirectory); 2070 } 2071 else 2072 { 2073 userDN = identity; 2074 } 2075 2076 final DN parsedUserDN; 2077 try 2078 { 2079 parsedUserDN = new DN(userDN); 2080 } 2081 catch (final LDAPException e) 2082 { 2083 Debug.debugException(e); 2084 throw new LDAPException(ResultCode.PARAM_ERROR, 2085 ERR_PWMOD_USER_IDENTITY_NOT_VALID_DN.get(userDN, 2086 userIdentity.getIdentifierString()), 2087 e); 2088 } 2089 2090 if (parsedUserDN.isNullDN()) 2091 { 2092 throw new LDAPException(ResultCode.PARAM_ERROR, 2093 ERR_PWMOD_USER_IDENTITY_EMPTY_DN.get( 2094 userIdentity.getIdentifierString())); 2095 } 2096 2097 if (verbose.isPresent()) 2098 { 2099 out(); 2100 INFO_PWMOD_USER_IDENTITY_DN_FOR_MOD.get(userDN); 2101 } 2102 2103 return userDN; 2104 } 2105 2106 2107 2108 /** 2109 * Performs a search to determine the DN for the user with the given username. 2110 * 2111 * @param pool A connection pool to use to communicate with the 2112 * directory server. It must not be {@code null}. 2113 * @param username The username for the target user. It must not 2114 * be {@code null}. 2115 * @param isActiveDirectory Indicates whether the target directory server 2116 * is believed to be an Active Directory instance. 2117 * 2118 * @return The DN for the user with the given username. 2119 * 2120 * @throws LDAPException If a problem occurs while searching for the user, 2121 * or if the search does not match exactly one user. 2122 */ 2123 @NotNull() 2124 private String searchForUser(@NotNull final LDAPConnectionPool pool, 2125 @NotNull final String username, 2126 final boolean isActiveDirectory) 2127 throws LDAPException 2128 { 2129 // Construct the filter to use for the search. 2130 final List<String> filterAttributeNames; 2131 if (usernameAttribute.isPresent()) 2132 { 2133 filterAttributeNames = usernameAttribute.getValues(); 2134 } 2135 else if (isActiveDirectory) 2136 { 2137 filterAttributeNames = AD_USERNAME_ATTRIBUTES; 2138 } 2139 else 2140 { 2141 filterAttributeNames = DEFAULT_USERNAME_ATTRIBUTES; 2142 } 2143 2144 final Filter filter; 2145 if (filterAttributeNames.size() == 1) 2146 { 2147 filter = Filter.createEqualityFilter(filterAttributeNames.get(0), 2148 username); 2149 } 2150 else 2151 { 2152 final List<Filter> orComponents = 2153 new ArrayList<>(filterAttributeNames.size()); 2154 for (final String attrName : filterAttributeNames) 2155 { 2156 orComponents.add(Filter.createEqualityFilter(attrName, username)); 2157 } 2158 2159 filter = Filter.createORFilter(orComponents); 2160 } 2161 2162 2163 // Create the search request to use to find the target user entry. 2164 final SearchRequest searchRequest = new SearchRequest( 2165 searchBaseDN.getStringValue(), SearchScope.SUB, filter, 2166 SearchRequest.NO_ATTRIBUTES); 2167 searchRequest.setSizeLimit(1); 2168 2169 if (verbose.isPresent()) 2170 { 2171 out(); 2172 wrapOut(0, WRAP_COLUMN, 2173 INFO_PWMOD_ISSUING_SEARCH_FOR_USER.get( 2174 String.valueOf(searchRequest), username)); 2175 } 2176 2177 2178 // Issue the search and get the results. 2179 SearchResult searchResult; 2180 LDAPException searchException = null; 2181 try 2182 { 2183 searchResult = pool.search(searchRequest); 2184 } 2185 catch (final LDAPException e) 2186 { 2187 Debug.debugException(e); 2188 searchException = e; 2189 searchResult = new SearchResult(e); 2190 } 2191 2192 if (verbose.isPresent()) 2193 { 2194 out(); 2195 for (final String line : 2196 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2197 { 2198 out(line); 2199 } 2200 } 2201 2202 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2203 { 2204 if (searchResult.getEntryCount() == 1) 2205 { 2206 return searchResult.getSearchEntries().get(0).getDN(); 2207 } 2208 else 2209 { 2210 throw new LDAPException(ResultCode.NO_RESULTS_RETURNED, 2211 ERR_PWMOD_SEARCH_FOR_USER_NO_MATCHES.get(username)); 2212 } 2213 } 2214 else if (searchResult.getResultCode() == ResultCode.SIZE_LIMIT_EXCEEDED) 2215 { 2216 throw new LDAPException(ResultCode.SIZE_LIMIT_EXCEEDED, 2217 ERR_PWMOD_SEARCH_FOR_USER_MULTIPLE_MATCHES.get(username), 2218 searchException); 2219 } 2220 else 2221 { 2222 throw new LDAPException(searchResult.getResultCode(), 2223 ERR_PWMOD_SEARCH_FOR_USER_FAILED.get(username, 2224 String.valueOf(searchResult.getResultCode()), 2225 searchResult.getDiagnosticMessage()), 2226 searchException); 2227 } 2228 } 2229 2230 2231 2232 /** 2233 * Retrieves the bytes that comprise the current password for the user, if one 2234 * should be provided in the password update request. 2235 * 2236 * @return The bytes that comprise the current password for the user, or 2237 * {@code null} if none should be provided in the password update 2238 * request. 2239 * 2240 * @throws LDAPException If a problem occurs while trying to obtain the 2241 * current password. 2242 */ 2243 @Nullable() 2244 private byte[] getCurrentPassword() 2245 throws LDAPException 2246 { 2247 if (currentPassword.isPresent()) 2248 { 2249 return StaticUtils.getBytes(currentPassword.getValue()); 2250 } 2251 else if (currentPasswordFile.isPresent()) 2252 { 2253 final File f = currentPasswordFile.getValue(); 2254 try 2255 { 2256 final char[] currentPasswordChars = 2257 getPasswordFileReader().readPassword(f); 2258 return StaticUtils.getBytes(new String(currentPasswordChars)); 2259 } 2260 catch (final LDAPException e) 2261 { 2262 Debug.debugException(e); 2263 throw new LDAPException(e.getResultCode(), 2264 ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), 2265 e.getMessage()), 2266 e); 2267 } 2268 catch (final Exception e) 2269 { 2270 Debug.debugException(e); 2271 throw new LDAPException(ResultCode.LOCAL_ERROR, 2272 ERR_PWMOD_CANNOT_READ_CURRENT_PW_FILE.get(f.getAbsolutePath(), 2273 StaticUtils.getExceptionMessage(e)), 2274 e); 2275 } 2276 } 2277 else if (promptForCurrentPassword.isPresent()) 2278 { 2279 while (true) 2280 { 2281 getOut().print(INFO_PWMOD_PROMPT_CURRENT_PW.get()); 2282 try 2283 { 2284 final byte[] pwBytes = PasswordReader.readPassword(); 2285 if ((pwBytes == null) || (pwBytes.length == 0)) 2286 { 2287 err(); 2288 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); 2289 err(); 2290 continue; 2291 } 2292 2293 return pwBytes; 2294 } 2295 catch (final Exception e) 2296 { 2297 throw new LDAPException(ResultCode.LOCAL_ERROR, 2298 ERR_PWMOD_CANNOT_PROMPT_FOR_CURRENT_PW.get( 2299 StaticUtils.getExceptionMessage(e)), 2300 e); 2301 } 2302 } 2303 } 2304 else 2305 { 2306 return null; 2307 } 2308 } 2309 2310 2311 2312 /** 2313 * Retrieves the bytes that comprise the new password for the user, if one 2314 * should be provided in the password update request. 2315 * 2316 * @return The bytes that comprise the new password for the user, or 2317 * {@code null} if none should be provided in the password update 2318 * request. 2319 * 2320 * @throws LDAPException If a problem occurs while trying to obtain the new 2321 * password. 2322 */ 2323 @Nullable() 2324 private byte[] getNewPassword() 2325 throws LDAPException 2326 { 2327 if (newPassword.isPresent()) 2328 { 2329 return StaticUtils.getBytes(newPassword.getValue()); 2330 } 2331 else if (newPasswordFile.isPresent()) 2332 { 2333 final File f = newPasswordFile.getValue(); 2334 try 2335 { 2336 final char[] newPasswordChars = getPasswordFileReader().readPassword(f); 2337 return StaticUtils.getBytes(new String(newPasswordChars)); 2338 } 2339 catch (final LDAPException e) 2340 { 2341 Debug.debugException(e); 2342 throw new LDAPException(e.getResultCode(), 2343 ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), 2344 e.getMessage()), 2345 e); 2346 } 2347 catch (final Exception e) 2348 { 2349 Debug.debugException(e); 2350 throw new LDAPException(ResultCode.LOCAL_ERROR, 2351 ERR_PWMOD_CANNOT_READ_NEW_PW_FILE.get(f.getAbsolutePath(), 2352 StaticUtils.getExceptionMessage(e)), 2353 e); 2354 } 2355 } 2356 else if (promptForNewPassword.isPresent()) 2357 { 2358 while (true) 2359 { 2360 getOut().print(INFO_PWMOD_PROMPT_NEW_PW.get()); 2361 2362 final byte[] pwBytes; 2363 try 2364 { 2365 pwBytes = PasswordReader.readPassword(); 2366 if ((pwBytes == null) || (pwBytes.length == 0)) 2367 { 2368 err(); 2369 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_PW_EMPTY.get()); 2370 err(); 2371 continue; 2372 } 2373 2374 getOut().print(INFO_PWMOD_CONFIRM_NEW_PW.get()); 2375 final byte[] confirmBytes = PasswordReader.readPassword(); 2376 if ((confirmBytes == null) || 2377 (! Arrays.equals(pwBytes, confirmBytes))) 2378 { 2379 Arrays.fill(pwBytes, (byte) 0x00); 2380 Arrays.fill(confirmBytes, (byte) 0x00); 2381 2382 err(); 2383 wrapErr(0, WRAP_COLUMN, ERR_PWMOD_NEW_PW_MISMATCH.get()); 2384 err(); 2385 continue; 2386 } 2387 2388 Arrays.fill(confirmBytes, (byte) 0x00); 2389 return pwBytes; 2390 } 2391 catch (final Exception e) 2392 { 2393 Debug.debugException(e); 2394 throw new LDAPException(ResultCode.LOCAL_ERROR, 2395 ERR_PWMOD_CANNOT_PROMPT_FOR_NEW_PW.get( 2396 StaticUtils.getExceptionMessage(e)), 2397 e); 2398 } 2399 } 2400 } 2401 else if (generateClientSideNewPassword.isPresent()) 2402 { 2403 return generatePassword(); 2404 } 2405 else 2406 { 2407 return null; 2408 } 2409 } 2410 2411 2412 2413 /** 2414 * Generates a new password for the user. 2415 * 2416 * @return The new password that was generated. 2417 */ 2418 @NotNull() 2419 private byte[] generatePassword() 2420 { 2421 final int length = generatedPasswordLength.getValue(); 2422 final StringBuilder generatedPassword = new StringBuilder(length); 2423 2424 final SecureRandom random = new SecureRandom(); 2425 final StringBuilder allPasswordCharacters = new StringBuilder(); 2426 for (final String charSet : generatedPasswordCharacterSet.getValues()) 2427 { 2428 allPasswordCharacters.append(charSet); 2429 2430 // Pick one character at random from the provided set to include in the 2431 // password. 2432 generatedPassword.append( 2433 charSet.charAt(random.nextInt(charSet.length()))); 2434 } 2435 2436 2437 // Choose as many additional characters (across all of the sets) as needed 2438 // to reach the desired length. 2439 while (generatedPassword.length() < length) 2440 { 2441 generatedPassword.append(allPasswordCharacters.charAt( 2442 random.nextInt(allPasswordCharacters.length()))); 2443 } 2444 2445 2446 // Scramble the generated password. 2447 final StringBuilder scrambledPassword = 2448 new StringBuilder(generatedPassword.length()); 2449 while (true) 2450 { 2451 if (generatedPassword.length() == 1) 2452 { 2453 scrambledPassword.append(generatedPassword.charAt(0)); 2454 break; 2455 } 2456 else 2457 { 2458 final int pos = random.nextInt(generatedPassword.length()); 2459 scrambledPassword.append(generatedPassword.charAt(pos)); 2460 generatedPassword.deleteCharAt(pos); 2461 } 2462 } 2463 2464 final String scrambledPasswordString = scrambledPassword.toString(); 2465 out(); 2466 wrapOut(0, WRAP_COLUMN, 2467 INFO_PWMOD_CLIENT_SIDE_GEN_PW.get(getToolName(), 2468 scrambledPasswordString)); 2469 return StaticUtils.getBytes(scrambledPasswordString); 2470 } 2471 2472 2473 2474 /** 2475 * Retrieves the controls that should be included in the password update 2476 * request. 2477 * 2478 * @return The controls that should be included in the password update 2479 * request, or an empty array if no controls should be included. 2480 * 2481 * @throws LDAPException If a problem occurs while trying to create any of 2482 * the controls. 2483 */ 2484 @NotNull() 2485 private Control[] getUpdateControls() 2486 throws LDAPException 2487 { 2488 final List<Control> controls = new ArrayList<>(); 2489 2490 if (updateControl.isPresent()) 2491 { 2492 controls.addAll(updateControl.getValues()); 2493 } 2494 2495 if (usePasswordPolicyControlOnUpdate.isPresent()) 2496 { 2497 controls.add(new PasswordPolicyRequestControl()); 2498 } 2499 2500 if (noOperation.isPresent()) 2501 { 2502 controls.add(new NoOpRequestControl()); 2503 } 2504 2505 if (getPasswordValidationDetails.isPresent()) 2506 { 2507 controls.add(new PasswordValidationDetailsRequestControl()); 2508 } 2509 2510 if (retireCurrentPassword.isPresent()) 2511 { 2512 controls.add(new RetirePasswordRequestControl(false)); 2513 } 2514 2515 if (purgeCurrentPassword.isPresent()) 2516 { 2517 controls.add(new PurgePasswordRequestControl(false)); 2518 } 2519 2520 if (passwordUpdateBehavior.isPresent()) 2521 { 2522 controls.add(LDAPModify.createPasswordUpdateBehaviorRequestControl( 2523 passwordUpdateBehavior.getIdentifierString(), 2524 passwordUpdateBehavior.getValues())); 2525 } 2526 2527 if (operationPurpose.isPresent()) 2528 { 2529 controls.add(new OperationPurposeRequestControl(false, getToolName(), 2530 getToolVersion(), 2531 LDAPPasswordModify.class.getName() + ".getUpdateControls", 2532 operationPurpose.getValue())); 2533 } 2534 2535 if (useAssuredReplication.isPresent()) 2536 { 2537 AssuredReplicationLocalLevel localLevel = null; 2538 if (assuredReplicationLocalLevel.isPresent()) 2539 { 2540 final String level = assuredReplicationLocalLevel.getValue(); 2541 if (level.equalsIgnoreCase(ASSURED_REPLICATION_LOCAL_LEVEL_NONE)) 2542 { 2543 localLevel = AssuredReplicationLocalLevel.NONE; 2544 } 2545 else if (level.equalsIgnoreCase( 2546 ASSURED_REPLICATION_LOCAL_LEVEL_RECEIVED_ANY_SERVER)) 2547 { 2548 localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER; 2549 } 2550 else if (level.equalsIgnoreCase( 2551 ASSURED_REPLICATION_LOCAL_LEVEL_PROCESSED_ALL_SERVERS)) 2552 { 2553 localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS; 2554 } 2555 } 2556 2557 AssuredReplicationRemoteLevel remoteLevel = null; 2558 if (assuredReplicationRemoteLevel.isPresent()) 2559 { 2560 final String level = assuredReplicationRemoteLevel.getValue(); 2561 if (level.equalsIgnoreCase(ASSURED_REPLICATION_REMOTE_LEVEL_NONE)) 2562 { 2563 remoteLevel = AssuredReplicationRemoteLevel.NONE; 2564 } 2565 else if (level.equalsIgnoreCase( 2566 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ANY_REMOTE_LOCATION)) 2567 { 2568 remoteLevel = 2569 AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION; 2570 } 2571 else if (level.equalsIgnoreCase( 2572 ASSURED_REPLICATION_REMOTE_LEVEL_RECEIVED_ALL_REMOTE_LOCATIONS)) 2573 { 2574 remoteLevel = 2575 AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS; 2576 } 2577 else if (level.equalsIgnoreCase( 2578 ASSURED_REPLICATION_REMOTE_LEVEL_PROCESSED_ALL_REMOTE_SERVERS)) 2579 { 2580 remoteLevel = 2581 AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS; 2582 } 2583 } 2584 2585 Long timeoutMillis = null; 2586 if (assuredReplicationTimeout.isPresent()) 2587 { 2588 timeoutMillis = 2589 assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS); 2590 } 2591 2592 controls.add(new AssuredReplicationRequestControl(true, localLevel, 2593 localLevel, remoteLevel, remoteLevel, timeoutMillis, false)); 2594 } 2595 2596 2597 return controls.toArray(StaticUtils.NO_CONTROLS); 2598 } 2599 2600 2601 2602 /** 2603 * Writes the provided message and sets it as the completion message. 2604 * 2605 * @param isError Indicates whether the message should be written to 2606 * standard error rather than standard output. 2607 * @param message The message to be written. 2608 */ 2609 private void logCompletionMessage(final boolean isError, 2610 @NotNull final String message) 2611 { 2612 completionMessage.compareAndSet(null, message); 2613 2614 if (isError) 2615 { 2616 wrapErr(0, WRAP_COLUMN, message); 2617 } 2618 else 2619 { 2620 wrapOut(0, WRAP_COLUMN, message); 2621 } 2622 } 2623 2624 2625 2626 /** 2627 * {@inheritDoc} 2628 */ 2629 @Override() 2630 public void handleUnsolicitedNotification( 2631 @NotNull final LDAPConnection connection, 2632 @NotNull final ExtendedResult notification) 2633 { 2634 final ArrayList<String> lines = new ArrayList<>(10); 2635 ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, 2636 WRAP_COLUMN); 2637 for (final String line : lines) 2638 { 2639 err(line); 2640 } 2641 err(); 2642 } 2643 2644 2645 2646 /** 2647 * {@inheritDoc} 2648 */ 2649 @Override() 2650 @NotNull() 2651 public LinkedHashMap<String[],String> getExampleUsages() 2652 { 2653 final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(); 2654 2655 examples.put( 2656 new String[] 2657 { 2658 "--hostname", "ds.example.com", 2659 "--port", "636", 2660 "--useSSL", 2661 "--userIdentity", "u:jdoe", 2662 "--promptForCurrentPassword", 2663 "--promptForNewPassword" 2664 }, 2665 INFO_PWMOD_EXAMPLE_1.get()); 2666 2667 examples.put( 2668 new String[] 2669 { 2670 "--hostname", "ds.example.com", 2671 "--port", "636", 2672 "--useSSL", 2673 "--bindDN", "uid=admin,dc=example,dc=com", 2674 "--bindPasswordFile", "admin-password.txt", 2675 "--userIdentity", "uid=jdoe,ou=People,dc=example,dc=com", 2676 "--generateClientSideNewPassword", 2677 "--passwordChangeMethod", "ldap-modify" 2678 }, 2679 INFO_PWMOD_EXAMPLE_2.get()); 2680 2681 return examples; 2682 } 2683}