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