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