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}