001/* 002 * Copyright 2015-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2015-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2015-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.controls; 037 038 039 040import java.util.ArrayList; 041import java.util.Collections; 042import java.util.Iterator; 043import java.util.LinkedHashMap; 044import java.util.List; 045import java.util.Map; 046 047import com.unboundid.asn1.ASN1Element; 048import com.unboundid.asn1.ASN1Integer; 049import com.unboundid.asn1.ASN1OctetString; 050import com.unboundid.asn1.ASN1Sequence; 051import com.unboundid.ldap.sdk.BindResult; 052import com.unboundid.ldap.sdk.Control; 053import com.unboundid.ldap.sdk.DecodeableControl; 054import com.unboundid.ldap.sdk.JSONControlDecodeHelper; 055import com.unboundid.ldap.sdk.LDAPException; 056import com.unboundid.ldap.sdk.ResultCode; 057import com.unboundid.ldap.sdk.unboundidds.extensions. 058 PasswordPolicyStateAccountUsabilityError; 059import com.unboundid.ldap.sdk.unboundidds.extensions. 060 PasswordPolicyStateAccountUsabilityNotice; 061import com.unboundid.ldap.sdk.unboundidds.extensions. 062 PasswordPolicyStateAccountUsabilityWarning; 063import com.unboundid.util.Debug; 064import com.unboundid.util.NotMutable; 065import com.unboundid.util.NotNull; 066import com.unboundid.util.Nullable; 067import com.unboundid.util.StaticUtils; 068import com.unboundid.util.ThreadSafety; 069import com.unboundid.util.ThreadSafetyLevel; 070import com.unboundid.util.json.JSONArray; 071import com.unboundid.util.json.JSONField; 072import com.unboundid.util.json.JSONNumber; 073import com.unboundid.util.json.JSONObject; 074import com.unboundid.util.json.JSONString; 075import com.unboundid.util.json.JSONValue; 076 077import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 078 079 080 081/** 082 * This class provides an implementation of a response control that can be 083 * included in a bind response with information about any password policy state 084 * notices, warnings, and/or errors for the user. 085 * <BR> 086 * <BLOCKQUOTE> 087 * <B>NOTE:</B> This class, and other classes within the 088 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 089 * supported for use against Ping Identity, UnboundID, and 090 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 091 * for proprietary functionality or for external specifications that are not 092 * considered stable or mature enough to be guaranteed to work in an 093 * interoperable way with other types of LDAP servers. 094 * </BLOCKQUOTE> 095 * <BR> 096 * This control has an OID of 1.3.6.1.4.1.30221.2.5.47, a criticality of 097 * {@code false}, and a value with the following encoding: 098 * <PRE> 099 * GetPasswordPolicyStateIssuesResponse ::= SEQUENCE { 100 * notices [0] SEQUENCE OF SEQUENCE { 101 * type INTEGER, 102 * name OCTET STRING, 103 * message OCTET STRING OPTIONAL } OPTIONAL, 104 * warnings [1] SEQUENCE OF SEQUENCE { 105 * type INTEGER, 106 * name OCTET STRING, 107 * message OCTET STRING OPTIONAL } OPTIONAL, 108 * errors [2] SEQUENCE OF SEQUENCE { 109 * type INTEGER, 110 * name OCTET STRING, 111 * message OCTET STRING OPTIONAL } OPTIONAL, 112 * authFailureReason [3] SEQUENCE { 113 * type INTEGER, 114 * name OCTET STRING, 115 * message OCTET STRING OPTIONAL } OPTIONAL, 116 * ... } 117 * </PRE> 118 */ 119@NotMutable() 120@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 121public final class GetPasswordPolicyStateIssuesResponseControl 122 extends Control 123 implements DecodeableControl 124{ 125 /** 126 * The OID (1.3.6.1.4.1.30221.2.5.47) for the get password policy state issues 127 * response control. 128 */ 129 @NotNull public static final String 130 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID = 131 "1.3.6.1.4.1.30221.2.5.47"; 132 133 134 135 /** 136 * The BER type to use for the value sequence element that holds the set of 137 * account usability notices. 138 */ 139 private static final byte TYPE_NOTICES = (byte) 0xA0; 140 141 142 143 /** 144 * The BER type to use for the value sequence element that holds the set of 145 * account usability warnings. 146 */ 147 private static final byte TYPE_WARNINGS = (byte) 0xA1; 148 149 150 151 /** 152 * The BER type to use for the value sequence element that holds the set of 153 * account usability errors. 154 */ 155 private static final byte TYPE_ERRORS = (byte) 0xA2; 156 157 158 159 /** 160 * The BER type to use for the value sequence element that holds the 161 * authentication failure reason. 162 */ 163 private static final byte TYPE_AUTH_FAILURE_REASON = (byte) 0xA3; 164 165 166 167 /** 168 * The name of the field used to represent the authentication failure reason 169 * in the JSON representation of this control. 170 */ 171 @NotNull private static final String JSON_FIELD_AUTH_FAILURE_REASON = 172 "authentication-failure-reason"; 173 174 175 176 /** 177 * The name of the field used to represent the set of password policy state 178 * errors in the JSON representation of this control. 179 */ 180 @NotNull private static final String JSON_FIELD_ERRORS = "errors"; 181 182 183 184 /** 185 * The name of the field used to represent the ID of a password policy state 186 * issue or auth failure reason in the JSON representation of this control. 187 */ 188 @NotNull private static final String JSON_FIELD_ID = "id"; 189 190 191 192 /** 193 * The name of the field used to represent the message for a password policy 194 * state issue or auth failure reason in the JSON representation of this 195 * control. 196 */ 197 @NotNull private static final String JSON_FIELD_MESSAGE = "message"; 198 199 200 201 /** 202 * The name of the field used to represent the name of a password policy state 203 * issue or auth failure reason in the JSON representation of this control. 204 */ 205 @NotNull private static final String JSON_FIELD_NAME = "name"; 206 207 208 209 /** 210 * The name of the field used to represent the set of password policy state 211 * notices in the JSON representation of this control. 212 */ 213 @NotNull private static final String JSON_FIELD_NOTICES = "notices"; 214 215 216 217 /** 218 * The name of the field used to represent the set of password policy state 219 * warnings in the JSON representation of this control. 220 */ 221 @NotNull private static final String JSON_FIELD_WARNINGS = "warnings"; 222 223 224 225 /** 226 * The serial version UID for this serializable class. 227 */ 228 private static final long serialVersionUID = 7509027658735069270L; 229 230 231 232 // The authentication failure reason for the bind operation. 233 @Nullable private final AuthenticationFailureReason authFailureReason; 234 235 // The set of account usability errors. 236 @NotNull private final List<PasswordPolicyStateAccountUsabilityError> errors; 237 238 // The set of account usability notices. 239 @NotNull private final List<PasswordPolicyStateAccountUsabilityNotice> 240 notices; 241 242 // The set of account usability warnings. 243 @NotNull private final List<PasswordPolicyStateAccountUsabilityWarning> 244 warnings; 245 246 247 248 /** 249 * Creates a new empty control instance that is intended to be used only for 250 * decoding controls via the {@code DecodeableControl} interface. 251 */ 252 GetPasswordPolicyStateIssuesResponseControl() 253 { 254 authFailureReason = null; 255 notices = Collections.emptyList(); 256 warnings = Collections.emptyList(); 257 errors = Collections.emptyList(); 258 } 259 260 261 262 /** 263 * Creates a new instance of this control with the provided information. 264 * 265 * @param notices The set of password policy state usability notices to 266 * include. It may be {@code null} or empty if there are 267 * no notices. 268 * @param warnings The set of password policy state usability warnings to 269 * include. It may be {@code null} or empty if there are 270 * no warnings. 271 * @param errors The set of password policy state usability errors to 272 * include. It may be {@code null} or empty if there are 273 * no errors. 274 */ 275 public GetPasswordPolicyStateIssuesResponseControl( 276 @Nullable final List<PasswordPolicyStateAccountUsabilityNotice> notices, 277 @Nullable final List<PasswordPolicyStateAccountUsabilityWarning> 278 warnings, 279 @Nullable final List<PasswordPolicyStateAccountUsabilityError> errors) 280 { 281 this(notices, warnings, errors, null); 282 } 283 284 285 286 /** 287 * Creates a new instance of this control with the provided information. 288 * 289 * @param notices The set of password policy state usability 290 * notices to include. It may be {@code null} or 291 * empty if there are no notices. 292 * @param warnings The set of password policy state usability 293 * warnings to include. It may be {@code null} or 294 * empty if there are no warnings. 295 * @param errors The set of password policy state usability 296 * errors to include. It may be {@code null} or 297 * empty if there are no errors. 298 * @param authFailureReason The authentication failure reason for the bind 299 * operation. It may be {@code null} if there is 300 * no authentication failure reason. 301 */ 302 public GetPasswordPolicyStateIssuesResponseControl( 303 @Nullable final List<PasswordPolicyStateAccountUsabilityNotice> notices, 304 @Nullable final List<PasswordPolicyStateAccountUsabilityWarning> 305 warnings, 306 @Nullable final List<PasswordPolicyStateAccountUsabilityError> errors, 307 @Nullable final AuthenticationFailureReason authFailureReason) 308 { 309 super(GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID, false, 310 encodeValue(notices, warnings, errors, authFailureReason)); 311 312 this.authFailureReason = authFailureReason; 313 314 if (notices == null) 315 { 316 this.notices = Collections.emptyList(); 317 } 318 else 319 { 320 this.notices = Collections.unmodifiableList(new ArrayList<>(notices)); 321 } 322 323 if (warnings == null) 324 { 325 this.warnings = Collections.emptyList(); 326 } 327 else 328 { 329 this.warnings = Collections.unmodifiableList(new ArrayList<>(warnings)); 330 } 331 332 if (errors == null) 333 { 334 this.errors = Collections.emptyList(); 335 } 336 else 337 { 338 this.errors = Collections.unmodifiableList(new ArrayList<>(errors)); 339 } 340 } 341 342 343 344 /** 345 * Creates a new instance of this control that is decoded from the provided 346 * generic control. 347 * 348 * @param oid The OID for the control. 349 * @param isCritical Indicates whether this control should be marked 350 * critical. 351 * @param value The encoded value for the control. 352 * 353 * @throws LDAPException If a problem is encountered while attempting to 354 * decode the provided control as a get password 355 * policy state issues response control. 356 */ 357 public GetPasswordPolicyStateIssuesResponseControl(@NotNull final String oid, 358 final boolean isCritical, @Nullable final ASN1OctetString value) 359 throws LDAPException 360 { 361 super(oid, isCritical, value); 362 363 if (value == null) 364 { 365 throw new LDAPException(ResultCode.DECODING_ERROR, 366 ERR_GET_PWP_STATE_ISSUES_RESPONSE_NO_VALUE.get()); 367 } 368 369 AuthenticationFailureReason afr = null; 370 List<PasswordPolicyStateAccountUsabilityNotice> nList = 371 Collections.emptyList(); 372 List<PasswordPolicyStateAccountUsabilityWarning> wList = 373 Collections.emptyList(); 374 List<PasswordPolicyStateAccountUsabilityError> eList = 375 Collections.emptyList(); 376 377 try 378 { 379 for (final ASN1Element e : 380 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 381 { 382 switch (e.getType()) 383 { 384 case TYPE_NOTICES: 385 nList = new ArrayList<>(10); 386 for (final ASN1Element ne : 387 ASN1Sequence.decodeAsSequence(e).elements()) 388 { 389 final ASN1Element[] noticeElements = 390 ASN1Sequence.decodeAsSequence(ne).elements(); 391 final int type = ASN1Integer.decodeAsInteger( 392 noticeElements[0]).intValue(); 393 final String name = ASN1OctetString.decodeAsOctetString( 394 noticeElements[1]).stringValue(); 395 396 final String message; 397 if (noticeElements.length == 3) 398 { 399 message = ASN1OctetString.decodeAsOctetString( 400 noticeElements[2]).stringValue(); 401 } 402 else 403 { 404 message = null; 405 } 406 407 nList.add(new PasswordPolicyStateAccountUsabilityNotice(type, 408 name, message)); 409 } 410 nList = Collections.unmodifiableList(nList); 411 break; 412 413 case TYPE_WARNINGS: 414 wList = 415 new ArrayList<>(10); 416 for (final ASN1Element we : 417 ASN1Sequence.decodeAsSequence(e).elements()) 418 { 419 final ASN1Element[] warningElements = 420 ASN1Sequence.decodeAsSequence(we).elements(); 421 final int type = ASN1Integer.decodeAsInteger( 422 warningElements[0]).intValue(); 423 final String name = ASN1OctetString.decodeAsOctetString( 424 warningElements[1]).stringValue(); 425 426 final String message; 427 if (warningElements.length == 3) 428 { 429 message = ASN1OctetString.decodeAsOctetString( 430 warningElements[2]).stringValue(); 431 } 432 else 433 { 434 message = null; 435 } 436 437 wList.add(new PasswordPolicyStateAccountUsabilityWarning(type, 438 name, message)); 439 } 440 wList = Collections.unmodifiableList(wList); 441 break; 442 443 case TYPE_ERRORS: 444 eList = new ArrayList<>(10); 445 for (final ASN1Element ee : 446 ASN1Sequence.decodeAsSequence(e).elements()) 447 { 448 final ASN1Element[] errorElements = 449 ASN1Sequence.decodeAsSequence(ee).elements(); 450 final int type = ASN1Integer.decodeAsInteger( 451 errorElements[0]).intValue(); 452 final String name = ASN1OctetString.decodeAsOctetString( 453 errorElements[1]).stringValue(); 454 455 final String message; 456 if (errorElements.length == 3) 457 { 458 message = ASN1OctetString.decodeAsOctetString( 459 errorElements[2]).stringValue(); 460 } 461 else 462 { 463 message = null; 464 } 465 466 eList.add(new PasswordPolicyStateAccountUsabilityError(type, 467 name, message)); 468 } 469 eList = Collections.unmodifiableList(eList); 470 break; 471 472 case TYPE_AUTH_FAILURE_REASON: 473 final ASN1Element[] afrElements = 474 ASN1Sequence.decodeAsSequence(e).elements(); 475 final int afrType = 476 ASN1Integer.decodeAsInteger(afrElements[0]).intValue(); 477 final String afrName = ASN1OctetString.decodeAsOctetString( 478 afrElements[1]).stringValue(); 479 480 final String afrMessage; 481 if (afrElements.length == 3) 482 { 483 afrMessage = ASN1OctetString.decodeAsOctetString( 484 afrElements[2]).stringValue(); 485 } 486 else 487 { 488 afrMessage = null; 489 } 490 afr = new AuthenticationFailureReason(afrType, afrName, afrMessage); 491 break; 492 493 default: 494 throw new LDAPException(ResultCode.DECODING_ERROR, 495 ERR_GET_PWP_STATE_ISSUES_RESPONSE_UNEXPECTED_TYPE.get( 496 StaticUtils.toHex(e.getType()))); 497 } 498 } 499 } 500 catch (final LDAPException le) 501 { 502 Debug.debugException(le); 503 504 throw le; 505 } 506 catch (final Exception e) 507 { 508 Debug.debugException(e); 509 510 throw new LDAPException(ResultCode.DECODING_ERROR, 511 ERR_GET_PWP_STATE_ISSUES_RESPONSE_CANNOT_DECODE.get( 512 StaticUtils.getExceptionMessage(e)), 513 e); 514 } 515 516 authFailureReason = afr; 517 notices = nList; 518 warnings = wList; 519 errors = eList; 520 } 521 522 523 524 /** 525 * Encodes the provided information into an ASN.1 octet string suitable for 526 * use as the value of this control. 527 * 528 * @param notices The set of password policy state usability 529 * notices to include. It may be {@code null} or 530 * empty if there are no notices. 531 * @param warnings The set of password policy state usability 532 * warnings to include. It may be {@code null} or 533 * empty if there are no warnings. 534 * @param errors The set of password policy state usability 535 * errors to include. It may be {@code null} or 536 * empty if there are no errors. 537 * @param authFailureReason The authentication failure reason for the bind 538 * operation. It may be {@code null} if there is 539 * no authentication failure reason. 540 * 541 * @return The ASN.1 octet string containing the encoded control value. 542 */ 543 @NotNull() 544 private static ASN1OctetString encodeValue( 545 @Nullable final List<PasswordPolicyStateAccountUsabilityNotice> notices, 546 @Nullable final List<PasswordPolicyStateAccountUsabilityWarning> 547 warnings, 548 @Nullable final List<PasswordPolicyStateAccountUsabilityError> errors, 549 @Nullable final AuthenticationFailureReason authFailureReason) 550 { 551 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 552 if ((notices != null) && (! notices.isEmpty())) 553 { 554 final ArrayList<ASN1Element> noticeElements = 555 new ArrayList<>(notices.size()); 556 for (final PasswordPolicyStateAccountUsabilityNotice n : notices) 557 { 558 if (n.getMessage() == null) 559 { 560 noticeElements.add(new ASN1Sequence( 561 new ASN1Integer(n.getIntValue()), 562 new ASN1OctetString(n.getName()))); 563 } 564 else 565 { 566 noticeElements.add(new ASN1Sequence( 567 new ASN1Integer(n.getIntValue()), 568 new ASN1OctetString(n.getName()), 569 new ASN1OctetString(n.getMessage()))); 570 } 571 } 572 573 elements.add(new ASN1Sequence(TYPE_NOTICES, noticeElements)); 574 } 575 576 if ((warnings != null) && (! warnings.isEmpty())) 577 { 578 final ArrayList<ASN1Element> warningElements = 579 new ArrayList<>(warnings.size()); 580 for (final PasswordPolicyStateAccountUsabilityWarning w : warnings) 581 { 582 if (w.getMessage() == null) 583 { 584 warningElements.add(new ASN1Sequence( 585 new ASN1Integer(w.getIntValue()), 586 new ASN1OctetString(w.getName()))); 587 } 588 else 589 { 590 warningElements.add(new ASN1Sequence( 591 new ASN1Integer(w.getIntValue()), 592 new ASN1OctetString(w.getName()), 593 new ASN1OctetString(w.getMessage()))); 594 } 595 } 596 597 elements.add(new ASN1Sequence(TYPE_WARNINGS, warningElements)); 598 } 599 600 if ((errors != null) && (! errors.isEmpty())) 601 { 602 final ArrayList<ASN1Element> errorElements = 603 new ArrayList<>(errors.size()); 604 for (final PasswordPolicyStateAccountUsabilityError e : errors) 605 { 606 if (e.getMessage() == null) 607 { 608 errorElements.add(new ASN1Sequence( 609 new ASN1Integer(e.getIntValue()), 610 new ASN1OctetString(e.getName()))); 611 } 612 else 613 { 614 errorElements.add(new ASN1Sequence( 615 new ASN1Integer(e.getIntValue()), 616 new ASN1OctetString(e.getName()), 617 new ASN1OctetString(e.getMessage()))); 618 } 619 } 620 621 elements.add(new ASN1Sequence(TYPE_ERRORS, errorElements)); 622 } 623 624 if (authFailureReason != null) 625 { 626 if (authFailureReason.getMessage() == null) 627 { 628 elements.add(new ASN1Sequence(TYPE_AUTH_FAILURE_REASON, 629 new ASN1Integer(authFailureReason.getIntValue()), 630 new ASN1OctetString(authFailureReason.getName()))); 631 } 632 else 633 { 634 elements.add(new ASN1Sequence(TYPE_AUTH_FAILURE_REASON, 635 new ASN1Integer(authFailureReason.getIntValue()), 636 new ASN1OctetString(authFailureReason.getName()), 637 new ASN1OctetString(authFailureReason.getMessage()))); 638 } 639 } 640 641 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 642 } 643 644 645 646 /** 647 * {@inheritDoc} 648 */ 649 @Override() 650 @NotNull() 651 public GetPasswordPolicyStateIssuesResponseControl decodeControl( 652 @NotNull final String oid, final boolean isCritical, 653 @Nullable final ASN1OctetString value) 654 throws LDAPException 655 { 656 return new GetPasswordPolicyStateIssuesResponseControl(oid, isCritical, 657 value); 658 } 659 660 661 662 /** 663 * Retrieves the set of account usability notices for the user. 664 * 665 * @return The set of account usability notices for the user, or an empty 666 * list if there are no notices. 667 */ 668 @NotNull() 669 public List<PasswordPolicyStateAccountUsabilityNotice> getNotices() 670 { 671 return notices; 672 } 673 674 675 676 /** 677 * Retrieves the set of account usability warnings for the user. 678 * 679 * @return The set of account usability warnings for the user, or an empty 680 * list if there are no warnings. 681 */ 682 @NotNull() 683 public List<PasswordPolicyStateAccountUsabilityWarning> getWarnings() 684 { 685 return warnings; 686 } 687 688 689 690 /** 691 * Retrieves the set of account usability errors for the user. 692 * 693 * @return The set of account usability errors for the user, or an empty 694 * list if there are no errors. 695 */ 696 @NotNull() 697 public List<PasswordPolicyStateAccountUsabilityError> getErrors() 698 { 699 return errors; 700 } 701 702 703 704 /** 705 * Retrieves the authentication failure reason for the bind operation, if 706 * available. 707 * 708 * @return The authentication failure reason for the bind operation, or 709 * {@code null} if none was provided. 710 */ 711 @Nullable() 712 public AuthenticationFailureReason getAuthenticationFailureReason() 713 { 714 return authFailureReason; 715 } 716 717 718 719 /** 720 * Extracts a get password policy state issues response control from the 721 * provided bind result. 722 * 723 * @param bindResult The bind result from which to retrieve the get password 724 * policy state issues response control. 725 * 726 * @return The get password policy state issues response control contained in 727 * the provided bind result, or {@code null} if the bind result did 728 * not contain a get password policy state issues response control. 729 * 730 * @throws LDAPException If a problem is encountered while attempting to 731 * decode the get password policy state issues 732 * response control contained in the provided bind 733 * result. 734 */ 735 @Nullable() 736 public static GetPasswordPolicyStateIssuesResponseControl get( 737 @NotNull final BindResult bindResult) 738 throws LDAPException 739 { 740 final Control c = bindResult.getResponseControl( 741 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID); 742 if (c == null) 743 { 744 return null; 745 } 746 747 if (c instanceof GetPasswordPolicyStateIssuesResponseControl) 748 { 749 return (GetPasswordPolicyStateIssuesResponseControl) c; 750 } 751 else 752 { 753 return new GetPasswordPolicyStateIssuesResponseControl(c.getOID(), 754 c.isCritical(), c.getValue()); 755 } 756 } 757 758 759 760 /** 761 * Extracts a get password policy state issues response control from the 762 * provided LDAP exception. 763 * 764 * @param ldapException The LDAP exception from which to retrieve the get 765 * password policy state issues response control. 766 * 767 * @return The get password policy state issues response control contained in 768 * the provided LDAP exception, or {@code null} if the exception did 769 * not contain a get password policy state issues response control. 770 * 771 * @throws LDAPException If a problem is encountered while attempting to 772 * decode the get password policy state issues 773 * response control contained in the provided LDAP 774 * exception. 775 */ 776 @Nullable() 777 public static GetPasswordPolicyStateIssuesResponseControl get( 778 @NotNull final LDAPException ldapException) 779 throws LDAPException 780 { 781 final Control c = ldapException.getResponseControl( 782 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID); 783 if (c == null) 784 { 785 return null; 786 } 787 788 if (c instanceof GetPasswordPolicyStateIssuesResponseControl) 789 { 790 return (GetPasswordPolicyStateIssuesResponseControl) c; 791 } 792 else 793 { 794 return new GetPasswordPolicyStateIssuesResponseControl(c.getOID(), 795 c.isCritical(), c.getValue()); 796 } 797 } 798 799 800 801 /** 802 * {@inheritDoc} 803 */ 804 @Override() 805 @NotNull() 806 public String getControlName() 807 { 808 return INFO_CONTROL_NAME_GET_PWP_STATE_ISSUES_RESPONSE.get(); 809 } 810 811 812 813 /** 814 * Retrieves a representation of this get password policy state issues 815 * response control as a JSON object. The JSON object uses the following 816 * fields: 817 * <UL> 818 * <LI> 819 * {@code oid} -- A mandatory string field whose value is the object 820 * identifier for this control. For the get password policy state issues 821 * response control, the OID is "1.3.6.1.4.1.30221.2.5.47". 822 * </LI> 823 * <LI> 824 * {@code control-name} -- An optional string field whose value is a 825 * human-readable name for this control. This field is only intended for 826 * descriptive purposes, and when decoding a control, the {@code oid} 827 * field should be used to identify the type of control. 828 * </LI> 829 * <LI> 830 * {@code criticality} -- A mandatory Boolean field used to indicate 831 * whether this control is considered critical. 832 * </LI> 833 * <LI> 834 * {@code value-base64} -- An optional string field whose value is a 835 * base64-encoded representation of the raw value for this get password 836 * policy state issues response control. Exactly one of the 837 * {@code value-base64} and {@code value-json} fields must be present. 838 * </LI> 839 * <LI> 840 * {@code value-json} -- An optional JSON object field whose value is a 841 * user-friendly representation of the value for this get password policy 842 * state issues response control. Exactly one of the {@code value-base64} 843 * and {@code value-json} fields must be present, and if the 844 * {@code value-json} field is used, then it will use the following 845 * fields: 846 * <UL> 847 * <LI> 848 * {@code notices} -- An optional array field containing JSON objects 849 * with information about any 850 * {@link PasswordPolicyStateAccountUsabilityNotice} values for the 851 * user. Each JSON object will use the following fields: 852 * <UL> 853 * <LI> 854 * {@code id} -- An integer field whose value is a numeric 855 * identifier for the account usability notice. 856 * </LI> 857 * <LI> 858 * {@code name} -- A string field whose value is the name for the 859 * account usability notice. 860 * </LI> 861 * <LI> 862 * {@code message} -- An optional string field whose value is a 863 * human-readable message with additional information about the 864 * account usability notice. 865 * </LI> 866 * </UL> 867 * </LI> 868 * <LI> 869 * {@code warnings} -- An optional array field containing JSON objects 870 * with information about any 871 * {@link PasswordPolicyStateAccountUsabilityWarning} values for the 872 * user. Each JSON object will use the following fields: 873 * <UL> 874 * <LI> 875 * {@code id} -- An integer field whose value is a numeric 876 * identifier for the account usability warning. 877 * </LI> 878 * <LI> 879 * {@code name} -- A string field whose value is the name for the 880 * account usability warning. 881 * </LI> 882 * <LI> 883 * {@code message} -- An optional string field whose value is a 884 * human-readable message with additional information about the 885 * account usability warning. 886 * </LI> 887 * </UL> 888 * </LI> 889 * <LI> 890 * {@code errors} -- An optional array field containing JSON objects 891 * with information about any 892 * {@link PasswordPolicyStateAccountUsabilityError} values for the 893 * user. Each JSON object will use the following fields: 894 * <UL> 895 * <LI> 896 * {@code id} -- An integer field whose value is a numeric 897 * identifier for the account usability error. 898 * </LI> 899 * <LI> 900 * {@code name} -- A string field whose value is the name for the 901 * account usability error. 902 * </LI> 903 * <LI> 904 * {@code message} -- An optional string field whose value is a 905 * human-readable message with additional information about the 906 * account usability error. 907 * </LI> 908 * </UL> 909 * </LI> 910 * <LI> 911 * {@code authentication-failure-reason} -- An optional JSON object 912 * field that represents an {@link AuthenticationFailureReason} with 913 * information about the reason that the authentication attempt 914 * failed. If present, this JSON object will use the following 915 * fields: 916 * <UL> 917 * <LI> 918 * {@code id} -- An integer field whose value is a numeric 919 * identifier for the authentication failure reason. 920 * </LI> 921 * <LI> 922 * {@code name} -- A string field whose value is the name for the 923 * authentication failure reason. 924 * </LI> 925 * <LI> 926 * {@code message} -- An optional string field whose value is a 927 * human-readable message with additional information about the 928 * authentication failure. 929 * </LI> 930 * </UL> 931 * </LI> 932 * </UL> 933 * </LI> 934 * </UL> 935 * 936 * @return A JSON object that contains a representation of this control. 937 */ 938 @Override() 939 @NotNull() 940 public JSONObject toJSONControl() 941 { 942 final Map<String,JSONValue> valueFields = new LinkedHashMap<>(); 943 944 if (! notices.isEmpty()) 945 { 946 final List<JSONValue> arrayValues = new ArrayList<>(notices.size()); 947 for (final PasswordPolicyStateAccountUsabilityNotice notice : notices) 948 { 949 arrayValues.add(encodeItem(notice.getIntValue(), notice.getName(), 950 notice.getMessage())); 951 } 952 valueFields.put(JSON_FIELD_NOTICES, new JSONArray(arrayValues)); 953 } 954 955 if (! warnings.isEmpty()) 956 { 957 final List<JSONValue> arrayValues = new ArrayList<>(warnings.size()); 958 for (final PasswordPolicyStateAccountUsabilityWarning warning : warnings) 959 { 960 arrayValues.add(encodeItem(warning.getIntValue(), warning.getName(), 961 warning.getMessage())); 962 } 963 valueFields.put(JSON_FIELD_WARNINGS, new JSONArray(arrayValues)); 964 } 965 966 if (! errors.isEmpty()) 967 { 968 final List<JSONValue> arrayValues = new ArrayList<>(notices.size()); 969 for (final PasswordPolicyStateAccountUsabilityError error : errors) 970 { 971 arrayValues.add(encodeItem(error.getIntValue(), error.getName(), 972 error.getMessage())); 973 } 974 valueFields.put(JSON_FIELD_ERRORS, new JSONArray(arrayValues)); 975 } 976 977 if (authFailureReason != null) 978 { 979 valueFields.put(JSON_FIELD_AUTH_FAILURE_REASON, 980 encodeItem(authFailureReason.getIntValue(), 981 authFailureReason.getName(), authFailureReason.getMessage())); 982 } 983 984 return new JSONObject( 985 new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID, 986 GET_PASSWORD_POLICY_STATE_ISSUES_RESPONSE_OID), 987 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME, 988 INFO_CONTROL_NAME_GET_PWP_STATE_ISSUES_RESPONSE.get()), 989 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY, 990 isCritical()), 991 new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON, 992 new JSONObject(valueFields))); 993 } 994 995 996 997 /** 998 * Retrieves a JSON object that contains an encoded representation of a 999 * password policy state issue or authentication failure reason with the 1000 * provided information. 1001 * 1002 * @param id The ID for the item. 1003 * @param name The name for the item. It must not be {@code null}. 1004 * @param message The message for the item. It may be {@code null} if no 1005 * message is available. 1006 * 1007 * @return A JSON object that contains an encoded representation of the 1008 * provided information. 1009 */ 1010 @NotNull() 1011 private static JSONObject encodeItem(final int id, 1012 @NotNull final String name, 1013 @Nullable final String message) 1014 { 1015 final Map<String,JSONValue> fields = new LinkedHashMap<>(); 1016 fields.put(JSON_FIELD_ID, new JSONNumber(id)); 1017 fields.put(JSON_FIELD_NAME, new JSONString(name)); 1018 1019 if (message != null) 1020 { 1021 fields.put(JSON_FIELD_MESSAGE, new JSONString(message)); 1022 } 1023 1024 return new JSONObject(fields); 1025 } 1026 1027 1028 1029 /** 1030 * Attempts to decode the provided object as a JSON representation of a get 1031 * password policy state issues response control. 1032 * 1033 * @param controlObject The JSON object to be decoded. It must not be 1034 * {@code null}. 1035 * @param strict Indicates whether to use strict mode when decoding 1036 * the provided JSON object. If this is {@code true}, 1037 * then this method will throw an exception if the 1038 * provided JSON object contains any unrecognized 1039 * fields. If this is {@code false}, then unrecognized 1040 * fields will be ignored. 1041 * 1042 * @return The get password policy state issues response control that was 1043 * decoded from the provided JSON object. 1044 * 1045 * @throws LDAPException If the provided JSON object cannot be parsed as a 1046 * valid get password policy state issues response 1047 * control. 1048 */ 1049 @NotNull() 1050 public static GetPasswordPolicyStateIssuesResponseControl decodeJSONControl( 1051 @NotNull final JSONObject controlObject, 1052 final boolean strict) 1053 throws LDAPException 1054 { 1055 final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper( 1056 controlObject, strict, true, true); 1057 1058 final ASN1OctetString rawValue = jsonControl.getRawValue(); 1059 if (rawValue != null) 1060 { 1061 return new GetPasswordPolicyStateIssuesResponseControl( 1062 jsonControl.getOID(), jsonControl.getCriticality(), rawValue); 1063 } 1064 1065 1066 final JSONObject valueObject = jsonControl.getValueObject(); 1067 1068 final List<PasswordPolicyStateAccountUsabilityNotice> notices = 1069 new ArrayList<>(); 1070 final List<JSONValue> noticeValues = 1071 valueObject.getFieldAsArray(JSON_FIELD_NOTICES); 1072 if (noticeValues != null) 1073 { 1074 for (final JSONValue v : noticeValues) 1075 { 1076 if (v instanceof JSONObject) 1077 { 1078 final JSONObject o = (JSONObject) v; 1079 1080 final Integer id = o.getFieldAsInteger(JSON_FIELD_ID); 1081 if (id == null) 1082 { 1083 throw new LDAPException(ResultCode.DECODING_ERROR, 1084 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1085 controlObject.toSingleLineString(), 1086 JSON_FIELD_NOTICES, JSON_FIELD_ID)); 1087 } 1088 1089 final String name = o.getFieldAsString(JSON_FIELD_NAME); 1090 if (name == null) 1091 { 1092 throw new LDAPException(ResultCode.DECODING_ERROR, 1093 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1094 controlObject.toSingleLineString(), 1095 JSON_FIELD_NOTICES, JSON_FIELD_NAME)); 1096 } 1097 1098 final String message = o.getFieldAsString(JSON_FIELD_MESSAGE); 1099 notices.add(new PasswordPolicyStateAccountUsabilityNotice(id, name, 1100 message)); 1101 } 1102 else 1103 { 1104 throw new LDAPException(ResultCode.DECODING_ERROR, 1105 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_VALUE_NOT_OBJECT.get( 1106 controlObject.toSingleLineString(), 1107 JSON_FIELD_NOTICES)); 1108 } 1109 } 1110 } 1111 1112 1113 final List<PasswordPolicyStateAccountUsabilityWarning > warnings = 1114 new ArrayList<>(); 1115 final List<JSONValue> warningValues = 1116 valueObject.getFieldAsArray(JSON_FIELD_WARNINGS); 1117 if (warningValues != null) 1118 { 1119 for (final JSONValue v : warningValues) 1120 { 1121 if (v instanceof JSONObject) 1122 { 1123 final JSONObject o = (JSONObject) v; 1124 1125 final Integer id = o.getFieldAsInteger(JSON_FIELD_ID); 1126 if (id == null) 1127 { 1128 throw new LDAPException(ResultCode.DECODING_ERROR, 1129 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1130 controlObject.toSingleLineString(), 1131 JSON_FIELD_WARNINGS, JSON_FIELD_ID)); 1132 } 1133 1134 final String name = o.getFieldAsString(JSON_FIELD_NAME); 1135 if (name == null) 1136 { 1137 throw new LDAPException(ResultCode.DECODING_ERROR, 1138 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1139 controlObject.toSingleLineString(), 1140 JSON_FIELD_WARNINGS, JSON_FIELD_NAME)); 1141 } 1142 1143 final String message = o.getFieldAsString(JSON_FIELD_MESSAGE); 1144 warnings.add(new PasswordPolicyStateAccountUsabilityWarning(id, name, 1145 message)); 1146 } 1147 else 1148 { 1149 throw new LDAPException(ResultCode.DECODING_ERROR, 1150 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_VALUE_NOT_OBJECT.get( 1151 controlObject.toSingleLineString(), 1152 JSON_FIELD_WARNINGS)); 1153 } 1154 } 1155 } 1156 1157 1158 final List<PasswordPolicyStateAccountUsabilityError > errors = 1159 new ArrayList<>(); 1160 final List<JSONValue> errorValues = 1161 valueObject.getFieldAsArray(JSON_FIELD_ERRORS); 1162 if (errorValues != null) 1163 { 1164 for (final JSONValue v : errorValues) 1165 { 1166 if (v instanceof JSONObject) 1167 { 1168 final JSONObject o = (JSONObject) v; 1169 1170 final Integer id = o.getFieldAsInteger(JSON_FIELD_ID); 1171 if (id == null) 1172 { 1173 throw new LDAPException(ResultCode.DECODING_ERROR, 1174 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1175 controlObject.toSingleLineString(), 1176 JSON_FIELD_ERRORS, JSON_FIELD_ID)); 1177 } 1178 1179 final String name = o.getFieldAsString(JSON_FIELD_NAME); 1180 if (name == null) 1181 { 1182 throw new LDAPException(ResultCode.DECODING_ERROR, 1183 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1184 controlObject.toSingleLineString(), 1185 JSON_FIELD_ERRORS, JSON_FIELD_NAME)); 1186 } 1187 1188 final String message = o.getFieldAsString(JSON_FIELD_MESSAGE); 1189 errors.add(new PasswordPolicyStateAccountUsabilityError(id, name, 1190 message)); 1191 } 1192 else 1193 { 1194 throw new LDAPException(ResultCode.DECODING_ERROR, 1195 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_VALUE_NOT_OBJECT.get( 1196 controlObject.toSingleLineString(), 1197 JSON_FIELD_ERRORS)); 1198 } 1199 } 1200 } 1201 1202 1203 final AuthenticationFailureReason authFailureReason; 1204 final JSONObject authFailureReasonObject = 1205 valueObject.getFieldAsObject(JSON_FIELD_AUTH_FAILURE_REASON); 1206 if (authFailureReasonObject == null) 1207 { 1208 authFailureReason = null; 1209 } 1210 else 1211 { 1212 final Integer id = 1213 authFailureReasonObject.getFieldAsInteger(JSON_FIELD_ID); 1214 if (id == null) 1215 { 1216 throw new LDAPException(ResultCode.DECODING_ERROR, 1217 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1218 controlObject.toSingleLineString(), 1219 JSON_FIELD_AUTH_FAILURE_REASON, JSON_FIELD_ID)); 1220 } 1221 1222 final String name = 1223 authFailureReasonObject.getFieldAsString(JSON_FIELD_NAME); 1224 if (name == null) 1225 { 1226 throw new LDAPException(ResultCode.DECODING_ERROR, 1227 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_MISSING_ITEM_FIELD.get( 1228 controlObject.toSingleLineString(), 1229 JSON_FIELD_ERRORS, JSON_FIELD_NAME)); 1230 } 1231 1232 final String message = 1233 authFailureReasonObject.getFieldAsString(JSON_FIELD_MESSAGE); 1234 authFailureReason = new AuthenticationFailureReason(id, name, message); 1235 } 1236 1237 1238 if (strict) 1239 { 1240 final List<String> unrecognizedFields = 1241 JSONControlDecodeHelper.getControlObjectUnexpectedFields( 1242 valueObject, JSON_FIELD_NOTICES, JSON_FIELD_WARNINGS, 1243 JSON_FIELD_ERRORS, JSON_FIELD_AUTH_FAILURE_REASON); 1244 if (! unrecognizedFields.isEmpty()) 1245 { 1246 throw new LDAPException(ResultCode.DECODING_ERROR, 1247 ERR_GET_PWP_STATE_ISSUES_RESPONSE_JSON_CONTROL_UNRECOGNIZED_FIELD. 1248 get(controlObject.toSingleLineString(), 1249 unrecognizedFields.get(0))); 1250 } 1251 } 1252 1253 1254 return new GetPasswordPolicyStateIssuesResponseControl(notices, warnings, 1255 errors, authFailureReason); 1256 } 1257 1258 1259 1260 /** 1261 * {@inheritDoc} 1262 */ 1263 @Override() 1264 public void toString(@NotNull final StringBuilder buffer) 1265 { 1266 buffer.append("GetPasswordPolicyStateIssuesResponseControl(notices={ "); 1267 1268 final Iterator<PasswordPolicyStateAccountUsabilityNotice> noticeIterator = 1269 notices.iterator(); 1270 while (noticeIterator.hasNext()) 1271 { 1272 buffer.append(noticeIterator.next().toString()); 1273 if (noticeIterator.hasNext()) 1274 { 1275 buffer.append(", "); 1276 } 1277 } 1278 buffer.append("}, warnings={ "); 1279 1280 final Iterator<PasswordPolicyStateAccountUsabilityWarning> warningIterator = 1281 warnings.iterator(); 1282 while (warningIterator.hasNext()) 1283 { 1284 buffer.append(warningIterator.next().toString()); 1285 if (warningIterator.hasNext()) 1286 { 1287 buffer.append(", "); 1288 } 1289 } 1290 buffer.append("}, errors={ "); 1291 1292 final Iterator<PasswordPolicyStateAccountUsabilityError> errorIterator = 1293 errors.iterator(); 1294 while (errorIterator.hasNext()) 1295 { 1296 buffer.append(errorIterator.next().toString()); 1297 if (errorIterator.hasNext()) 1298 { 1299 buffer.append(", "); 1300 } 1301 } 1302 buffer.append('}'); 1303 1304 if (authFailureReason != null) 1305 { 1306 buffer.append(", authFailureReason="); 1307 buffer.append(authFailureReason.toString()); 1308 } 1309 1310 buffer.append(')'); 1311 } 1312}