001/* 002 * Copyright 2015-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2019 Ping Identity Corporation 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.unboundidds.controls; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1Element; 031import com.unboundid.asn1.ASN1Integer; 032import com.unboundid.asn1.ASN1OctetString; 033import com.unboundid.asn1.ASN1Sequence; 034import com.unboundid.ldap.sdk.BindResult; 035import com.unboundid.ldap.sdk.Control; 036import com.unboundid.ldap.sdk.DecodeableControl; 037import com.unboundid.ldap.sdk.LDAPException; 038import com.unboundid.ldap.sdk.ResultCode; 039import com.unboundid.ldap.sdk.unboundidds.extensions. 040 PasswordPolicyStateAccountUsabilityError; 041import com.unboundid.ldap.sdk.unboundidds.extensions. 042 PasswordPolicyStateAccountUsabilityNotice; 043import com.unboundid.ldap.sdk.unboundidds.extensions. 044 PasswordPolicyStateAccountUsabilityWarning; 045import com.unboundid.util.Debug; 046import com.unboundid.util.NotMutable; 047import com.unboundid.util.StaticUtils; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050 051import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 052 053 054 055/** 056 * This class provides an implementation of a response control that can be 057 * included in a bind response with information about any password policy state 058 * notices, warnings, and/or errors for the user. 059 * <BR> 060 * <BLOCKQUOTE> 061 * <B>NOTE:</B> This class, and other classes within the 062 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 063 * supported for use against Ping Identity, UnboundID, and 064 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 065 * for proprietary functionality or for external specifications that are not 066 * considered stable or mature enough to be guaranteed to work in an 067 * interoperable way with other types of LDAP servers. 068 * </BLOCKQUOTE> 069 * <BR> 070 * This control has an OID of 1.3.6.1.4.1.30221.2.5.47, a criticality of 071 * {@code false}, and a value with the following encoding: 072 * <PRE> 073 * GetPasswordPolicyStateIssuesResponse ::= SEQUENCE { 074 * notices [0] SEQUENCE OF SEQUENCE { 075 * type INTEGER, 076 * name OCTET STRING, 077 * message OCTET STRING OPTIONAL } OPTIONAL, 078 * warnings [1] SEQUENCE OF SEQUENCE { 079 * type INTEGER, 080 * name OCTET STRING, 081 * message OCTET STRING OPTIONAL } OPTIONAL, 082 * errors [2] SEQUENCE OF SEQUENCE { 083 * type INTEGER, 084 * name OCTET STRING, 085 * message OCTET STRING OPTIONAL } OPTIONAL, 086 * authFailureReason [3] SEQUENCE { 087 * type INTEGER, 088 * name OCTET STRING, 089 * message OCTET STRING OPTIONAL } OPTIONAL, 090 * ... } 091 * </PRE> 092 */ 093@NotMutable() 094@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 095public final class GetPasswordPolicyStateIssuesResponseControl 096 extends Control 097 implements DecodeableControl 098{ 099 /** 100 * The OID (1.3.6.1.4.1.30221.2.5.47) for the get password policy state issues 101 * response control. 102 */ 103 public static final String GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID = 104 "1.3.6.1.4.1.30221.2.5.47"; 105 106 107 108 /** 109 * The BER type to use for the value sequence element that holds the set of 110 * account usability notices. 111 */ 112 private static final byte TYPE_NOTICES = (byte) 0xA0; 113 114 115 116 /** 117 * The BER type to use for the value sequence element that holds the set of 118 * account usability warnings. 119 */ 120 private static final byte TYPE_WARNINGS = (byte) 0xA1; 121 122 123 124 /** 125 * The BER type to use for the value sequence element that holds the set of 126 * account usability errors. 127 */ 128 private static final byte TYPE_ERRORS = (byte) 0xA2; 129 130 131 132 /** 133 * The BER type to use for the value sequence element that holds the 134 * authentication failure reason. 135 */ 136 private static final byte TYPE_AUTH_FAILURE_REASON = (byte) 0xA3; 137 138 139 140 /** 141 * The serial version UID for this serializable class. 142 */ 143 private static final long serialVersionUID = 7509027658735069270L; 144 145 146 147 // The authentication failure reason for the bind operation. 148 private final AuthenticationFailureReason authFailureReason; 149 150 // The set of account usability errors. 151 private final List<PasswordPolicyStateAccountUsabilityError> errors; 152 153 // The set of account usability notices. 154 private final List<PasswordPolicyStateAccountUsabilityNotice> notices; 155 156 // The set of account usability warnings. 157 private final List<PasswordPolicyStateAccountUsabilityWarning> warnings; 158 159 160 161 /** 162 * Creates a new empty control instance that is intended to be used only for 163 * decoding controls via the {@code DecodeableControl} interface. 164 */ 165 GetPasswordPolicyStateIssuesResponseControl() 166 { 167 authFailureReason = null; 168 notices = Collections.emptyList(); 169 warnings = Collections.emptyList(); 170 errors = Collections.emptyList(); 171 } 172 173 174 175 /** 176 * Creates a new instance of this control with the provided information. 177 * 178 * @param notices The set of password policy state usability notices to 179 * include. It may be {@code null} or empty if there are 180 * no notices. 181 * @param warnings The set of password policy state usability warnings to 182 * include. It may be {@code null} or empty if there are 183 * no warnings. 184 * @param errors The set of password policy state usability errors to 185 * include. It may be {@code null} or empty if there are 186 * no errors. 187 */ 188 public GetPasswordPolicyStateIssuesResponseControl( 189 final List<PasswordPolicyStateAccountUsabilityNotice> notices, 190 final List<PasswordPolicyStateAccountUsabilityWarning> warnings, 191 final List<PasswordPolicyStateAccountUsabilityError> errors) 192 { 193 this(notices, warnings, errors, null); 194 } 195 196 197 198 /** 199 * Creates a new instance of this control with the provided information. 200 * 201 * @param notices The set of password policy state usability 202 * notices to include. It may be {@code null} or 203 * empty if there are no notices. 204 * @param warnings The set of password policy state usability 205 * warnings to include. It may be {@code null} or 206 * empty if there are no warnings. 207 * @param errors The set of password policy state usability 208 * errors to include. It may be {@code null} or 209 * empty if there are no errors. 210 * @param authFailureReason The authentication failure reason for the bind 211 * operation. It may be {@code null} if there is 212 * no authentication failure reason. 213 */ 214 public GetPasswordPolicyStateIssuesResponseControl( 215 final List<PasswordPolicyStateAccountUsabilityNotice> notices, 216 final List<PasswordPolicyStateAccountUsabilityWarning> warnings, 217 final List<PasswordPolicyStateAccountUsabilityError> errors, 218 final AuthenticationFailureReason authFailureReason) 219 { 220 super(GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID, false, 221 encodeValue(notices, warnings, errors, authFailureReason)); 222 223 this.authFailureReason = authFailureReason; 224 225 if (notices == null) 226 { 227 this.notices = Collections.emptyList(); 228 } 229 else 230 { 231 this.notices = Collections.unmodifiableList(new ArrayList<>(notices)); 232 } 233 234 if (warnings == null) 235 { 236 this.warnings = Collections.emptyList(); 237 } 238 else 239 { 240 this.warnings = Collections.unmodifiableList(new ArrayList<>(warnings)); 241 } 242 243 if (errors == null) 244 { 245 this.errors = Collections.emptyList(); 246 } 247 else 248 { 249 this.errors = Collections.unmodifiableList(new ArrayList<>(errors)); 250 } 251 } 252 253 254 255 /** 256 * Creates a new instance of this control that is decoded from the provided 257 * generic control. 258 * 259 * @param oid The OID for the control. 260 * @param isCritical Indicates whether this control should be marked 261 * critical. 262 * @param value The encoded value for the control. 263 * 264 * @throws LDAPException If a problem is encountered while attempting to 265 * decode the provided control as a get password 266 * policy state issues response control. 267 */ 268 public GetPasswordPolicyStateIssuesResponseControl(final String oid, 269 final boolean isCritical, final ASN1OctetString value) 270 throws LDAPException 271 { 272 super(oid, isCritical, value); 273 274 if (value == null) 275 { 276 throw new LDAPException(ResultCode.DECODING_ERROR, 277 ERR_GET_PWP_STATE_ISSUES_RESPONSE_NO_VALUE.get()); 278 } 279 280 AuthenticationFailureReason afr = null; 281 List<PasswordPolicyStateAccountUsabilityNotice> nList = 282 Collections.emptyList(); 283 List<PasswordPolicyStateAccountUsabilityWarning> wList = 284 Collections.emptyList(); 285 List<PasswordPolicyStateAccountUsabilityError> eList = 286 Collections.emptyList(); 287 288 try 289 { 290 for (final ASN1Element e : 291 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 292 { 293 switch (e.getType()) 294 { 295 case TYPE_NOTICES: 296 nList = new ArrayList<>(10); 297 for (final ASN1Element ne : 298 ASN1Sequence.decodeAsSequence(e).elements()) 299 { 300 final ASN1Element[] noticeElements = 301 ASN1Sequence.decodeAsSequence(ne).elements(); 302 final int type = ASN1Integer.decodeAsInteger( 303 noticeElements[0]).intValue(); 304 final String name = ASN1OctetString.decodeAsOctetString( 305 noticeElements[1]).stringValue(); 306 307 final String message; 308 if (noticeElements.length == 3) 309 { 310 message = ASN1OctetString.decodeAsOctetString( 311 noticeElements[2]).stringValue(); 312 } 313 else 314 { 315 message = null; 316 } 317 318 nList.add(new PasswordPolicyStateAccountUsabilityNotice(type, 319 name, message)); 320 } 321 nList = Collections.unmodifiableList(nList); 322 break; 323 324 case TYPE_WARNINGS: 325 wList = 326 new ArrayList<>(10); 327 for (final ASN1Element we : 328 ASN1Sequence.decodeAsSequence(e).elements()) 329 { 330 final ASN1Element[] warningElements = 331 ASN1Sequence.decodeAsSequence(we).elements(); 332 final int type = ASN1Integer.decodeAsInteger( 333 warningElements[0]).intValue(); 334 final String name = ASN1OctetString.decodeAsOctetString( 335 warningElements[1]).stringValue(); 336 337 final String message; 338 if (warningElements.length == 3) 339 { 340 message = ASN1OctetString.decodeAsOctetString( 341 warningElements[2]).stringValue(); 342 } 343 else 344 { 345 message = null; 346 } 347 348 wList.add(new PasswordPolicyStateAccountUsabilityWarning(type, 349 name, message)); 350 } 351 wList = Collections.unmodifiableList(wList); 352 break; 353 354 case TYPE_ERRORS: 355 eList = new ArrayList<>(10); 356 for (final ASN1Element ee : 357 ASN1Sequence.decodeAsSequence(e).elements()) 358 { 359 final ASN1Element[] errorElements = 360 ASN1Sequence.decodeAsSequence(ee).elements(); 361 final int type = ASN1Integer.decodeAsInteger( 362 errorElements[0]).intValue(); 363 final String name = ASN1OctetString.decodeAsOctetString( 364 errorElements[1]).stringValue(); 365 366 final String message; 367 if (errorElements.length == 3) 368 { 369 message = ASN1OctetString.decodeAsOctetString( 370 errorElements[2]).stringValue(); 371 } 372 else 373 { 374 message = null; 375 } 376 377 eList.add(new PasswordPolicyStateAccountUsabilityError(type, 378 name, message)); 379 } 380 eList = Collections.unmodifiableList(eList); 381 break; 382 383 case TYPE_AUTH_FAILURE_REASON: 384 final ASN1Element[] afrElements = 385 ASN1Sequence.decodeAsSequence(e).elements(); 386 final int afrType = 387 ASN1Integer.decodeAsInteger(afrElements[0]).intValue(); 388 final String afrName = ASN1OctetString.decodeAsOctetString( 389 afrElements[1]).stringValue(); 390 391 final String afrMessage; 392 if (afrElements.length == 3) 393 { 394 afrMessage = ASN1OctetString.decodeAsOctetString( 395 afrElements[2]).stringValue(); 396 } 397 else 398 { 399 afrMessage = null; 400 } 401 afr = new AuthenticationFailureReason(afrType, afrName, afrMessage); 402 break; 403 404 default: 405 throw new LDAPException(ResultCode.DECODING_ERROR, 406 ERR_GET_PWP_STATE_ISSUES_RESPONSE_UNEXPECTED_TYPE.get( 407 StaticUtils.toHex(e.getType()))); 408 } 409 } 410 } 411 catch (final LDAPException le) 412 { 413 Debug.debugException(le); 414 415 throw le; 416 } 417 catch (final Exception e) 418 { 419 Debug.debugException(e); 420 421 throw new LDAPException(ResultCode.DECODING_ERROR, 422 ERR_GET_PWP_STATE_ISSUES_RESPONSE_CANNOT_DECODE.get( 423 StaticUtils.getExceptionMessage(e)), 424 e); 425 } 426 427 authFailureReason = afr; 428 notices = nList; 429 warnings = wList; 430 errors = eList; 431 } 432 433 434 435 /** 436 * Encodes the provided information into an ASN.1 octet string suitable for 437 * use as the value of this control. 438 * 439 * @param notices The set of password policy state usability 440 * notices to include. It may be {@code null} or 441 * empty if there are no notices. 442 * @param warnings The set of password policy state usability 443 * warnings to include. It may be {@code null} or 444 * empty if there are no warnings. 445 * @param errors The set of password policy state usability 446 * errors to include. It may be {@code null} or 447 * empty if there are no errors. 448 * @param authFailureReason The authentication failure reason for the bind 449 * operation. It may be {@code null} if there is 450 * no authentication failure reason. 451 * 452 * @return The ASN.1 octet string containing the encoded control value. 453 */ 454 private static ASN1OctetString encodeValue( 455 final List<PasswordPolicyStateAccountUsabilityNotice> notices, 456 final List<PasswordPolicyStateAccountUsabilityWarning> warnings, 457 final List<PasswordPolicyStateAccountUsabilityError> errors, 458 final AuthenticationFailureReason authFailureReason) 459 { 460 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 461 if ((notices != null) && (! notices.isEmpty())) 462 { 463 final ArrayList<ASN1Element> noticeElements = 464 new ArrayList<>(notices.size()); 465 for (final PasswordPolicyStateAccountUsabilityNotice n : notices) 466 { 467 if (n.getMessage() == null) 468 { 469 noticeElements.add(new ASN1Sequence( 470 new ASN1Integer(n.getIntValue()), 471 new ASN1OctetString(n.getName()))); 472 } 473 else 474 { 475 noticeElements.add(new ASN1Sequence( 476 new ASN1Integer(n.getIntValue()), 477 new ASN1OctetString(n.getName()), 478 new ASN1OctetString(n.getMessage()))); 479 } 480 } 481 482 elements.add(new ASN1Sequence(TYPE_NOTICES, noticeElements)); 483 } 484 485 if ((warnings != null) && (! warnings.isEmpty())) 486 { 487 final ArrayList<ASN1Element> warningElements = 488 new ArrayList<>(warnings.size()); 489 for (final PasswordPolicyStateAccountUsabilityWarning w : warnings) 490 { 491 if (w.getMessage() == null) 492 { 493 warningElements.add(new ASN1Sequence( 494 new ASN1Integer(w.getIntValue()), 495 new ASN1OctetString(w.getName()))); 496 } 497 else 498 { 499 warningElements.add(new ASN1Sequence( 500 new ASN1Integer(w.getIntValue()), 501 new ASN1OctetString(w.getName()), 502 new ASN1OctetString(w.getMessage()))); 503 } 504 } 505 506 elements.add(new ASN1Sequence(TYPE_WARNINGS, warningElements)); 507 } 508 509 if ((errors != null) && (! errors.isEmpty())) 510 { 511 final ArrayList<ASN1Element> errorElements = 512 new ArrayList<>(errors.size()); 513 for (final PasswordPolicyStateAccountUsabilityError e : errors) 514 { 515 if (e.getMessage() == null) 516 { 517 errorElements.add(new ASN1Sequence( 518 new ASN1Integer(e.getIntValue()), 519 new ASN1OctetString(e.getName()))); 520 } 521 else 522 { 523 errorElements.add(new ASN1Sequence( 524 new ASN1Integer(e.getIntValue()), 525 new ASN1OctetString(e.getName()), 526 new ASN1OctetString(e.getMessage()))); 527 } 528 } 529 530 elements.add(new ASN1Sequence(TYPE_ERRORS, errorElements)); 531 } 532 533 if (authFailureReason != null) 534 { 535 if (authFailureReason.getMessage() == null) 536 { 537 elements.add(new ASN1Sequence(TYPE_AUTH_FAILURE_REASON, 538 new ASN1Integer(authFailureReason.getIntValue()), 539 new ASN1OctetString(authFailureReason.getName()))); 540 } 541 else 542 { 543 elements.add(new ASN1Sequence(TYPE_AUTH_FAILURE_REASON, 544 new ASN1Integer(authFailureReason.getIntValue()), 545 new ASN1OctetString(authFailureReason.getName()), 546 new ASN1OctetString(authFailureReason.getMessage()))); 547 } 548 } 549 550 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 551 } 552 553 554 555 /** 556 * {@inheritDoc} 557 */ 558 @Override() 559 public GetPasswordPolicyStateIssuesResponseControl decodeControl( 560 final String oid, final boolean isCritical, 561 final ASN1OctetString value) 562 throws LDAPException 563 { 564 return new GetPasswordPolicyStateIssuesResponseControl(oid, isCritical, 565 value); 566 } 567 568 569 570 /** 571 * Retrieves the set of account usability notices for the user. 572 * 573 * @return The set of account usability notices for the user, or an empty 574 * list if there are no notices. 575 */ 576 public List<PasswordPolicyStateAccountUsabilityNotice> getNotices() 577 { 578 return notices; 579 } 580 581 582 583 /** 584 * Retrieves the set of account usability warnings for the user. 585 * 586 * @return The set of account usability warnings for the user, or an empty 587 * list if there are no warnings. 588 */ 589 public List<PasswordPolicyStateAccountUsabilityWarning> getWarnings() 590 { 591 return warnings; 592 } 593 594 595 596 /** 597 * Retrieves the set of account usability errors for the user. 598 * 599 * @return The set of account usability errors for the user, or an empty 600 * list if there are no errors. 601 */ 602 public List<PasswordPolicyStateAccountUsabilityError> getErrors() 603 { 604 return errors; 605 } 606 607 608 609 /** 610 * Retrieves the authentication failure reason for the bind operation, if 611 * available. 612 * 613 * @return The authentication failure reason for the bind operation, or 614 * {@code null} if none was provided. 615 */ 616 public AuthenticationFailureReason getAuthenticationFailureReason() 617 { 618 return authFailureReason; 619 } 620 621 622 623 /** 624 * Extracts a get password policy state issues response control from the 625 * provided bind result. 626 * 627 * @param bindResult The bind result from which to retrieve the get password 628 * policy state issues response control. 629 * 630 * @return The get password policy state issues response control contained in 631 * the provided bind result, or {@code null} if the bind result did 632 * not contain a get password policy state issues response control. 633 * 634 * @throws LDAPException If a problem is encountered while attempting to 635 * decode the get password policy state issues 636 * response control contained in the provided bind 637 * result. 638 */ 639 public static GetPasswordPolicyStateIssuesResponseControl get( 640 final BindResult bindResult) 641 throws LDAPException 642 { 643 final Control c = bindResult.getResponseControl( 644 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID); 645 if (c == null) 646 { 647 return null; 648 } 649 650 if (c instanceof GetPasswordPolicyStateIssuesResponseControl) 651 { 652 return (GetPasswordPolicyStateIssuesResponseControl) c; 653 } 654 else 655 { 656 return new GetPasswordPolicyStateIssuesResponseControl(c.getOID(), 657 c.isCritical(), c.getValue()); 658 } 659 } 660 661 662 663 /** 664 * Extracts a get password policy state issues response control from the 665 * provided LDAP exception. 666 * 667 * @param ldapException The LDAP exception from which to retrieve the get 668 * password policy state issues response control. 669 * 670 * @return The get password policy state issues response control contained in 671 * the provided LDAP exception, or {@code null} if the exception did 672 * not contain a get password policy state issues response control. 673 * 674 * @throws LDAPException If a problem is encountered while attempting to 675 * decode the get password policy state issues 676 * response control contained in the provided LDAP 677 * exception. 678 */ 679 public static GetPasswordPolicyStateIssuesResponseControl get( 680 final LDAPException ldapException) 681 throws LDAPException 682 { 683 final Control c = ldapException.getResponseControl( 684 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID); 685 if (c == null) 686 { 687 return null; 688 } 689 690 if (c instanceof GetPasswordPolicyStateIssuesResponseControl) 691 { 692 return (GetPasswordPolicyStateIssuesResponseControl) c; 693 } 694 else 695 { 696 return new GetPasswordPolicyStateIssuesResponseControl(c.getOID(), 697 c.isCritical(), c.getValue()); 698 } 699 } 700 701 702 703 /** 704 * {@inheritDoc} 705 */ 706 @Override() 707 public String getControlName() 708 { 709 return INFO_CONTROL_NAME_GET_PWP_STATE_ISSUES_RESPONSE.get(); 710 } 711 712 713 714 /** 715 * {@inheritDoc} 716 */ 717 @Override() 718 public void toString(final StringBuilder buffer) 719 { 720 buffer.append("GetPasswordPolicyStateIssuesResponseControl(notices={ "); 721 722 final Iterator<PasswordPolicyStateAccountUsabilityNotice> noticeIterator = 723 notices.iterator(); 724 while (noticeIterator.hasNext()) 725 { 726 buffer.append(noticeIterator.next().toString()); 727 if (noticeIterator.hasNext()) 728 { 729 buffer.append(", "); 730 } 731 } 732 buffer.append("}, warnings={ "); 733 734 final Iterator<PasswordPolicyStateAccountUsabilityWarning> warningIterator = 735 warnings.iterator(); 736 while (warningIterator.hasNext()) 737 { 738 buffer.append(warningIterator.next().toString()); 739 if (warningIterator.hasNext()) 740 { 741 buffer.append(", "); 742 } 743 } 744 buffer.append("}, errors={ "); 745 746 final Iterator<PasswordPolicyStateAccountUsabilityError> errorIterator = 747 errors.iterator(); 748 while (errorIterator.hasNext()) 749 { 750 buffer.append(errorIterator.next().toString()); 751 if (errorIterator.hasNext()) 752 { 753 buffer.append(", "); 754 } 755 } 756 buffer.append('}'); 757 758 if (authFailureReason != null) 759 { 760 buffer.append(", authFailureReason="); 761 buffer.append(authFailureReason.toString()); 762 } 763 764 buffer.append(')'); 765 } 766}