001/* 002 * Copyright 2008-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.extensions; 022 023 024 025import java.util.ArrayList; 026 027import com.unboundid.asn1.ASN1Element; 028import com.unboundid.asn1.ASN1OctetString; 029import com.unboundid.asn1.ASN1Sequence; 030import com.unboundid.ldap.sdk.Control; 031import com.unboundid.ldap.sdk.ExtendedRequest; 032import com.unboundid.ldap.sdk.ExtendedResult; 033import com.unboundid.ldap.sdk.LDAPConnection; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.ResultCode; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*; 041import static com.unboundid.util.Debug.*; 042import static com.unboundid.util.StaticUtils.*; 043 044 045 046/** 047 * This class provides an implementation of the LDAP password modify extended 048 * request as defined in 049 * <A HREF="http://www.ietf.org/rfc/rfc3062.txt">RFC 3062</A>. It may be used 050 * to change the password for a user in the directory, and provides the ability 051 * to specify the current password for verification. It also offers the ability 052 * to request that the server generate a new password for the user. 053 * <BR><BR> 054 * The elements of a password modify extended request include: 055 * <UL> 056 * <LI>{@code userIdentity} -- This specifies the user for which to change the 057 * password. It should generally be the DN for the target user (although 058 * the specification does indicate that some servers may accept other 059 * values). If no value is provided, then the server will attempt to 060 * change the password for the currently-authenticated user.</LI> 061 * <LI>{@code oldPassword} -- This specifies the current password for the 062 * user. Some servers may require that the old password be provided when 063 * a user is changing his or her own password as an extra level of 064 * verification, but it is generally not necessary when an administrator 065 * is resetting the password for another user.</LI> 066 * <LI>{@code newPassword} -- This specifies the new password to use for the 067 * user. If it is not provided, then the server may attempt to generate a 068 * new password for the user, and in that case it will be included in the 069 * {@code generatedPassword} field of the corresponding 070 * {@link PasswordModifyExtendedResult}. Note that some servers may not 071 * support generating a new password, in which case the client will always 072 * be required to provide it.</LI> 073 * </UL> 074 * <H2>Example</H2> 075 * The following example demonstrates the use of the password modify extended 076 * operation to change the password for user 077 * "uid=test.user,ou=People,dc=example,dc=com". Neither the current password 078 * nor a new password will be provided, so the server will generate a new 079 * password for the user. 080 * <PRE> 081 * PasswordModifyExtendedRequest passwordModifyRequest = 082 * new PasswordModifyExtendedRequest( 083 * "uid=test.user,ou=People,dc=example,dc=com", // The user to update 084 * (String) null, // The current password for the user. 085 * (String) null); // The new password. null = server will generate 086 * 087 * PasswordModifyExtendedResult passwordModifyResult; 088 * try 089 * { 090 * passwordModifyResult = (PasswordModifyExtendedResult) 091 * connection.processExtendedOperation(passwordModifyRequest); 092 * // This doesn't necessarily mean that the operation was successful, since 093 * // some kinds of extended operations return non-success results under 094 * // normal conditions. 095 * } 096 * catch (LDAPException le) 097 * { 098 * // For an extended operation, this generally means that a problem was 099 * // encountered while trying to send the request or read the result. 100 * passwordModifyResult = new PasswordModifyExtendedResult( 101 * new ExtendedResult(le)); 102 * } 103 * 104 * LDAPTestUtils.assertResultCodeEquals(passwordModifyResult, 105 * ResultCode.SUCCESS); 106 * String serverGeneratedNewPassword = 107 * passwordModifyResult.getGeneratedPassword(); 108 * </PRE> 109 */ 110@NotMutable() 111@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 112public final class PasswordModifyExtendedRequest 113 extends ExtendedRequest 114{ 115 /** 116 * The OID (1.3.6.1.4.1.4203.1.11.1) for the password modify extended request. 117 */ 118 public static final String PASSWORD_MODIFY_REQUEST_OID = 119 "1.3.6.1.4.1.4203.1.11.1"; 120 121 122 123 /** 124 * The BER type for the user identity element. 125 */ 126 private static final byte TYPE_USER_IDENTITY = (byte) 0x80; 127 128 129 130 /** 131 * The BER type for the old password element. 132 */ 133 private static final byte TYPE_OLD_PASSWORD = (byte) 0x81; 134 135 136 137 /** 138 * The BER type for the new password element. 139 */ 140 private static final byte TYPE_NEW_PASSWORD = (byte) 0x82; 141 142 143 144 /** 145 * The serial version UID for this serializable class. 146 */ 147 private static final long serialVersionUID = 4965048727456933570L; 148 149 150 151 // The old password for this request. 152 private final ASN1OctetString oldPassword; 153 154 // The new password for this request. 155 private final ASN1OctetString newPassword; 156 157 // The user identity string for this request. 158 private final String userIdentity; 159 160 161 162 /** 163 * Creates a new password modify extended request that will attempt to change 164 * the password of the currently-authenticated user. 165 * 166 * @param newPassword The new password for the user. It may be {@code null} 167 * if the new password should be generated by the 168 * directory server. 169 */ 170 public PasswordModifyExtendedRequest(final String newPassword) 171 { 172 this(null, null, newPassword, null); 173 } 174 175 176 177 /** 178 * Creates a new password modify extended request that will attempt to change 179 * the password of the currently-authenticated user. 180 * 181 * @param newPassword The new password for the user. It may be {@code null} 182 * if the new password should be generated by the 183 * directory server. 184 */ 185 public PasswordModifyExtendedRequest(final byte[] newPassword) 186 { 187 this(null, null, newPassword, null); 188 } 189 190 191 192 /** 193 * Creates a new password modify extended request that will attempt to change 194 * the password of the currently-authenticated user. 195 * 196 * @param oldPassword The current password for the user. It may be 197 * {@code null} if the directory server does not require 198 * the user's current password for self changes. 199 * @param newPassword The new password for the user. It may be {@code null} 200 * if the new password should be generated by the 201 * directory server. 202 */ 203 public PasswordModifyExtendedRequest(final String oldPassword, 204 final String newPassword) 205 { 206 this(null, oldPassword, newPassword, null); 207 } 208 209 210 211 /** 212 * Creates a new password modify extended request that will attempt to change 213 * the password of the currently-authenticated user. 214 * 215 * @param oldPassword The current password for the user. It may be 216 * {@code null} if the directory server does not require 217 * the user's current password for self changes. 218 * @param newPassword The new password for the user. It may be {@code null} 219 * if the new password should be generated by the 220 * directory server. 221 */ 222 public PasswordModifyExtendedRequest(final byte[] oldPassword, 223 final byte[] newPassword) 224 { 225 this(null, oldPassword, newPassword, null); 226 } 227 228 229 230 /** 231 * Creates a new password modify extended request that will attempt to change 232 * the password for the specified user. 233 * 234 * @param userIdentity The string that identifies the user whose password 235 * should be changed. It may or may not be a DN, but if 236 * it is not a DN, then the directory server must be 237 * able to identify the appropriate user from the 238 * provided identifier. It may be {@code null} to 239 * indicate that the password change should be for the 240 * currently-authenticated user. 241 * @param oldPassword The current password for the user. It may be 242 * {@code null} if the directory server does not require 243 * the user's current password for self changes. 244 * @param newPassword The new password for the user. It may be 245 * {@code null} if the new password should be generated 246 * by the directory server. 247 */ 248 public PasswordModifyExtendedRequest(final String userIdentity, 249 final String oldPassword, 250 final String newPassword) 251 { 252 this(userIdentity, oldPassword, newPassword, null); 253 } 254 255 256 257 /** 258 * Creates a new password modify extended request that will attempt to change 259 * the password for the specified user. 260 * 261 * @param userIdentity The string that identifies the user whose password 262 * should be changed. It may or may not be a DN, but if 263 * it is not a DN, then the directory server must be 264 * able to identify the appropriate user from the 265 * provided identifier. It may be {@code null} to 266 * indicate that the password change should be for the 267 * currently-authenticated user. 268 * @param oldPassword The current password for the user. It may be 269 * {@code null} if the directory server does not require 270 * the user's current password for self changes. 271 * @param newPassword The new password for the user. It may be 272 * {@code null} if the new password should be generated 273 * by the directory server. 274 */ 275 public PasswordModifyExtendedRequest(final String userIdentity, 276 final byte[] oldPassword, 277 final byte[] newPassword) 278 { 279 this(userIdentity, oldPassword, newPassword, null); 280 } 281 282 283 284 /** 285 * Creates a new password modify extended request that will attempt to change 286 * the password for the specified user. 287 * 288 * @param userIdentity The string that identifies the user whose password 289 * should be changed. It may or may not be a DN, but if 290 * it is not a DN, then the directory server must be 291 * able to identify the appropriate user from the 292 * provided identifier. It may be {@code null} to 293 * indicate that the password change should be for the 294 * currently-authenticated user. 295 * @param oldPassword The current password for the user. It may be 296 * {@code null} if the directory server does not require 297 * the user's current password for self changes. 298 * @param newPassword The new password for the user. It may be 299 * {@code null} if the new password should be generated 300 * by the directory server. 301 * @param controls The set of controls to include in the request. 302 */ 303 public PasswordModifyExtendedRequest(final String userIdentity, 304 final String oldPassword, 305 final String newPassword, 306 final Control[] controls) 307 { 308 super(PASSWORD_MODIFY_REQUEST_OID, 309 encodeValue(userIdentity, oldPassword, newPassword), controls); 310 311 this.userIdentity = userIdentity; 312 313 if (oldPassword == null) 314 { 315 this.oldPassword = null; 316 } 317 else 318 { 319 this.oldPassword = new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword); 320 } 321 322 if (newPassword == null) 323 { 324 this.newPassword = null; 325 } 326 else 327 { 328 this.newPassword = new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword); 329 } 330 } 331 332 333 334 /** 335 * Creates a new password modify extended request that will attempt to change 336 * the password for the specified user. 337 * 338 * @param userIdentity The string that identifies the user whose password 339 * should be changed. It may or may not be a DN, but if 340 * it is not a DN, then the directory server must be 341 * able to identify the appropriate user from the 342 * provided identifier. It may be {@code null} to 343 * indicate that the password change should be for the 344 * currently-authenticated user. 345 * @param oldPassword The current password for the user. It may be 346 * {@code null} if the directory server does not require 347 * the user's current password for self changes. 348 * @param newPassword The new password for the user. It may be 349 * {@code null} if the new password should be generated 350 * by the directory server. 351 * @param controls The set of controls to include in the request. 352 */ 353 public PasswordModifyExtendedRequest(final String userIdentity, 354 final byte[] oldPassword, 355 final byte[] newPassword, 356 final Control[] controls) 357 { 358 super(PASSWORD_MODIFY_REQUEST_OID, 359 encodeValue(userIdentity, oldPassword, newPassword), controls); 360 361 this.userIdentity = userIdentity; 362 363 if (oldPassword == null) 364 { 365 this.oldPassword = null; 366 } 367 else 368 { 369 this.oldPassword = new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword); 370 } 371 372 if (newPassword == null) 373 { 374 this.newPassword = null; 375 } 376 else 377 { 378 this.newPassword = new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword); 379 } 380 } 381 382 383 384 /** 385 * Creates a new password modify extended request from the provided generic 386 * extended request. 387 * 388 * @param extendedRequest The generic extended request to use to create this 389 * password modify extended request. 390 * 391 * @throws LDAPException If a problem occurs while decoding the request. 392 */ 393 public PasswordModifyExtendedRequest(final ExtendedRequest extendedRequest) 394 throws LDAPException 395 { 396 super(extendedRequest); 397 398 final ASN1OctetString value = extendedRequest.getValue(); 399 if (value == null) 400 { 401 throw new LDAPException(ResultCode.DECODING_ERROR, 402 ERR_PW_MODIFY_REQUEST_NO_VALUE.get()); 403 } 404 405 try 406 { 407 ASN1OctetString oldPW = null; 408 ASN1OctetString newPW = null; 409 String userID = null; 410 411 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 412 final ASN1Element[] elements = 413 ASN1Sequence.decodeAsSequence(valueElement).elements(); 414 for (final ASN1Element e : elements) 415 { 416 switch (e.getType()) 417 { 418 case TYPE_USER_IDENTITY: 419 userID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 420 break; 421 422 case TYPE_OLD_PASSWORD: 423 oldPW = ASN1OctetString.decodeAsOctetString(e); 424 break; 425 426 case TYPE_NEW_PASSWORD: 427 newPW = ASN1OctetString.decodeAsOctetString(e); 428 break; 429 430 default: 431 throw new LDAPException(ResultCode.DECODING_ERROR, 432 ERR_PW_MODIFY_REQUEST_INVALID_TYPE.get( 433 toHex(e.getType()))); 434 } 435 } 436 437 userIdentity = userID; 438 oldPassword = oldPW; 439 newPassword = newPW; 440 } 441 catch (LDAPException le) 442 { 443 debugException(le); 444 throw le; 445 } 446 catch (Exception e) 447 { 448 debugException(e); 449 throw new LDAPException(ResultCode.DECODING_ERROR, 450 ERR_PW_MODIFY_REQUEST_CANNOT_DECODE.get(e), e); 451 } 452 } 453 454 455 456 /** 457 * Encodes the provided information into an ASN.1 octet string suitable for 458 * use as the value of this extended request. 459 * 460 * @param userIdentity The string that identifies the user whose password 461 * should be changed. It may or may not be a DN, but if 462 * it is not a DN, then the directory server must be 463 * able to identify the appropriate user from the 464 * provided identifier. It may be {@code null} to 465 * indicate that the password change should be for the 466 * currently-authenticated user. 467 * @param oldPassword The current password for the user. It may be 468 * {@code null} if the directory server does not require 469 * the user's current password for self changes. 470 * @param newPassword The new password for the user. It may be 471 * {@code null} if the new password should be generated 472 * by the directory server. 473 * 474 * @return The ASN.1 octet string containing the encoded value. 475 */ 476 private static ASN1OctetString encodeValue(final String userIdentity, 477 final String oldPassword, 478 final String newPassword) 479 { 480 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3); 481 482 if (userIdentity != null) 483 { 484 elements.add(new ASN1OctetString(TYPE_USER_IDENTITY, userIdentity)); 485 } 486 487 if (oldPassword != null) 488 { 489 elements.add(new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword)); 490 } 491 492 if (newPassword != null) 493 { 494 elements.add(new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword)); 495 } 496 497 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 498 } 499 500 501 502 /** 503 * Encodes the provided information into an ASN.1 octet string suitable for 504 * use as the value of this extended request. 505 * 506 * @param userIdentity The string that identifies the user whose password 507 * should be changed. It may or may not be a DN, but if 508 * it is not a DN, then the directory server must be 509 * able to identify the appropriate user from the 510 * provided identifier. It may be {@code null} to 511 * indicate that the password change should be for the 512 * currently-authenticated user. 513 * @param oldPassword The current password for the user. It may be 514 * {@code null} if the directory server does not require 515 * the user's current password for self changes. 516 * @param newPassword The new password for the user. It may be 517 * {@code null} if the new password should be generated 518 * by the directory server. 519 * 520 * @return The ASN.1 octet string containing the encoded value. 521 */ 522 private static ASN1OctetString encodeValue(final String userIdentity, 523 final byte[] oldPassword, 524 final byte[] newPassword) 525 { 526 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3); 527 528 if (userIdentity != null) 529 { 530 elements.add(new ASN1OctetString(TYPE_USER_IDENTITY, userIdentity)); 531 } 532 533 if (oldPassword != null) 534 { 535 elements.add(new ASN1OctetString(TYPE_OLD_PASSWORD, oldPassword)); 536 } 537 538 if (newPassword != null) 539 { 540 elements.add(new ASN1OctetString(TYPE_NEW_PASSWORD, newPassword)); 541 } 542 543 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 544 } 545 546 547 548 /** 549 * Retrieves the user identity for this request, if available. 550 * 551 * @return The user identity for this request, or {@code null} if the 552 * password change should target the currently-authenticated user. 553 */ 554 public String getUserIdentity() 555 { 556 return userIdentity; 557 } 558 559 560 561 /** 562 * Retrieves the string representation of the old password for this request, 563 * if available. 564 * 565 * @return The string representation of the old password for this request, or 566 * {@code null} if it was not provided. 567 */ 568 public String getOldPassword() 569 { 570 if (oldPassword == null) 571 { 572 return null; 573 } 574 else 575 { 576 return oldPassword.stringValue(); 577 } 578 } 579 580 581 582 /** 583 * Retrieves the binary representation of the old password for this request, 584 * if available. 585 * 586 * @return The binary representation of the old password for this request, or 587 * {@code null} if it was not provided. 588 */ 589 public byte[] getOldPasswordBytes() 590 { 591 if (oldPassword == null) 592 { 593 return null; 594 } 595 else 596 { 597 return oldPassword.getValue(); 598 } 599 } 600 601 602 603 /** 604 * Retrieves the raw old password for this request, if available. 605 * 606 * @return The raw old password for this request, or {@code null} if it was 607 * not provided. 608 */ 609 public ASN1OctetString getRawOldPassword() 610 { 611 return oldPassword; 612 } 613 614 615 616 /** 617 * Retrieves the string representation of the new password for this request, 618 * if available. 619 * 620 * @return The string representation of the new password for this request, or 621 * {@code null} if it was not provided. 622 */ 623 public String getNewPassword() 624 { 625 if (newPassword == null) 626 { 627 return null; 628 } 629 else 630 { 631 return newPassword.stringValue(); 632 } 633 } 634 635 636 637 /** 638 * Retrieves the binary representation of the new password for this request, 639 * if available. 640 * 641 * @return The binary representation of the new password for this request, or 642 * {@code null} if it was not provided. 643 */ 644 public byte[] getNewPasswordBytes() 645 { 646 if (newPassword == null) 647 { 648 return null; 649 } 650 else 651 { 652 return newPassword.getValue(); 653 } 654 } 655 656 657 658 /** 659 * Retrieves the raw new password for this request, if available. 660 * 661 * @return The raw new password for this request, or {@code null} if it was 662 * not provided. 663 */ 664 public ASN1OctetString getRawNewPassword() 665 { 666 return newPassword; 667 } 668 669 670 671 /** 672 * {@inheritDoc} 673 */ 674 @Override() 675 public PasswordModifyExtendedResult process(final LDAPConnection connection, 676 final int depth) 677 throws LDAPException 678 { 679 final ExtendedResult extendedResponse = super.process(connection, depth); 680 return new PasswordModifyExtendedResult(extendedResponse); 681 } 682 683 684 685 /** 686 * {@inheritDoc} 687 */ 688 @Override() 689 public PasswordModifyExtendedRequest duplicate() 690 { 691 return duplicate(getControls()); 692 } 693 694 695 696 /** 697 * {@inheritDoc} 698 */ 699 @Override() 700 public PasswordModifyExtendedRequest duplicate(final Control[] controls) 701 { 702 final byte[] oldPWBytes = 703 (oldPassword == null) ? null : oldPassword.getValue(); 704 final byte[] newPWBytes = 705 (newPassword == null) ? null : newPassword.getValue(); 706 707 final PasswordModifyExtendedRequest r = 708 new PasswordModifyExtendedRequest(userIdentity, oldPWBytes, 709 newPWBytes, controls); 710 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 711 return r; 712 } 713 714 715 716 /** 717 * {@inheritDoc} 718 */ 719 @Override() 720 public String getExtendedRequestName() 721 { 722 return INFO_EXTENDED_REQUEST_NAME_PASSWORD_MODIFY.get(); 723 } 724 725 726 727 /** 728 * {@inheritDoc} 729 */ 730 @Override() 731 public void toString(final StringBuilder buffer) 732 { 733 buffer.append("PasswordModifyExtendedRequest("); 734 735 boolean dataAdded = false; 736 737 if (userIdentity != null) 738 { 739 buffer.append("userIdentity='"); 740 buffer.append(userIdentity); 741 buffer.append('\''); 742 dataAdded = true; 743 } 744 745 if (oldPassword != null) 746 { 747 if (dataAdded) 748 { 749 buffer.append(", "); 750 } 751 752 buffer.append("oldPassword='"); 753 buffer.append(oldPassword.stringValue()); 754 buffer.append('\''); 755 dataAdded = true; 756 } 757 758 if (newPassword != null) 759 { 760 if (dataAdded) 761 { 762 buffer.append(", "); 763 } 764 765 buffer.append("newPassword='"); 766 buffer.append(newPassword.stringValue()); 767 buffer.append('\''); 768 dataAdded = true; 769 } 770 771 final Control[] controls = getControls(); 772 if (controls.length > 0) 773 { 774 if (dataAdded) 775 { 776 buffer.append(", "); 777 } 778 779 buffer.append("controls={"); 780 for (int i=0; i < controls.length; i++) 781 { 782 if (i > 0) 783 { 784 buffer.append(", "); 785 } 786 787 buffer.append(controls[i]); 788 } 789 buffer.append('}'); 790 } 791 792 buffer.append(')'); 793 } 794}