001/*
002 * Copyright 2008-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.List;
028
029import com.unboundid.asn1.ASN1Boolean;
030import com.unboundid.asn1.ASN1Element;
031import com.unboundid.asn1.ASN1Integer;
032import com.unboundid.asn1.ASN1OctetString;
033import com.unboundid.asn1.ASN1Sequence;
034import com.unboundid.ldap.sdk.Control;
035import com.unboundid.ldap.sdk.DecodeableControl;
036import com.unboundid.ldap.sdk.LDAPException;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.ldap.sdk.SearchResultEntry;
039import com.unboundid.util.Debug;
040import com.unboundid.util.NotMutable;
041import com.unboundid.util.StaticUtils;
042import com.unboundid.util.ThreadSafety;
043import com.unboundid.util.ThreadSafetyLevel;
044
045import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
046
047
048
049/**
050 * This class provides an implementation of the account usable response control,
051 * which may be returned with search result entries to provide information about
052 * the usability of the associated user accounts.
053 * <BR>
054 * <BLOCKQUOTE>
055 *   <B>NOTE:</B>  This class, and other classes within the
056 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
057 *   supported for use against Ping Identity, UnboundID, and
058 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
059 *   for proprietary functionality or for external specifications that are not
060 *   considered stable or mature enough to be guaranteed to work in an
061 *   interoperable way with other types of LDAP servers.
062 * </BLOCKQUOTE>
063 * <BR>
064 * Information that may be included in the account usable response control
065 * includes:
066 * <UL>
067 *   <LI>{@code accountIsActive} -- Indicates that the account is active and may
068 *       include the length of time in seconds until the password expires.</LI>
069 *   <LI>{@code accountIsInactive} -- Indicates that the account has been locked
070 *       or deactivated.</LI>
071 *   <LI>{@code mustChangePassword} -- Indicates that the user must change his
072 *       or her password before being allowed to perform any other
073 *       operations.</LI>
074 *   <LI>{@code passwordIsExpired} -- Indicates that the user's password has
075 *       expired.</LI>
076 *   <LI>{@code remainingGraceLogins} -- Indicates the number of grace logins
077 *       remaining for the user.</LI>
078 *   <LI>{@code secondsUntilUnlock} -- Indicates the length of time in seconds
079 *       until the account will be automatically unlocked.</LI>
080 * </UL>
081 * See the {@link AccountUsableRequestControl} documentation for an example
082 * demonstrating the use of the account usable request and response controls.
083 * <BR><BR>
084 * This control was designed by Sun Microsystems and is not based on any RFC or
085 * Internet draft.  The value of this control is encoded as follows:
086 * <BR><BR>
087 * <PRE>
088 * ACCOUNT_USABLE_RESPONSE ::= CHOICE {
089 *   isUsable     [0] INTEGER, -- Seconds until password expiration --
090 *   isNotUsable  [1] MORE_INFO }
091 *
092 * MORE_INFO ::= SEQUENCE {
093 *   accountIsInactive     [0] BOOLEAN DEFAULT FALSE,
094 *   mustChangePassword    [1] BOOLEAN DEFAULT FALSE,
095 *   passwordIsExpired     [2] BOOLEAN DEFAULT FALSE,
096 *   remainingGraceLogins  [3] INTEGER OPTIONAL,
097 *   secondsUntilUnlock    [4] INTEGER OPTIONAL }
098 * </PRE>
099 */
100@NotMutable()
101@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
102public final class AccountUsableResponseControl
103       extends Control
104       implements DecodeableControl
105{
106  /**
107   * The OID (1.3.6.1.4.1.42.2.27.9.5.8) for the account usable response
108   * control.
109   */
110  public static final String ACCOUNT_USABLE_RESPONSE_OID =
111       "1.3.6.1.4.1.42.2.27.9.5.8";
112
113
114
115  /**
116   * The BER type that will be used for the element that indicates the account
117   * is usable and provides the number of seconds until expiration.
118   */
119  private static final byte TYPE_SECONDS_UNTIL_EXPIRATION = (byte) 0x80;
120
121
122
123  /**
124   * The BER type that will be used for the element that indicates the account
125   * is not usable and provides additional information about the reason.
126   */
127  private static final byte TYPE_MORE_INFO = (byte) 0xA1;
128
129
130
131  /**
132   * The BER type that will be used for the element that indicates whether the
133   * account is inactive.
134   */
135  private static final byte TYPE_IS_INACTIVE = (byte) 0x80;
136
137
138
139  /**
140   * The BER type that will be used for the element that indicates whether the
141   * user must change their password.
142   */
143  private static final byte TYPE_MUST_CHANGE = (byte) 0x81;
144
145
146
147  /**
148   * The BER type that will be used for the element that indicates whether the
149   * password is expired.
150   */
151  private static final byte TYPE_IS_EXPIRED = (byte) 0x82;
152
153
154
155  /**
156   * The BER type that will be used for the element that provides the number of
157   * remaining grace logins.
158   */
159  private static final byte TYPE_REMAINING_GRACE_LOGINS = (byte) 0x83;
160
161
162
163  /**
164   * The BER type that will be used for the element that provides the number of
165   * seconds until the account is unlocked.
166   */
167  private static final byte TYPE_SECONDS_UNTIL_UNLOCK = (byte) 0x84;
168
169
170
171  /**
172   * The serial version UID for this serializable class.
173   */
174  private static final long serialVersionUID = -9150988495337467770L;
175
176
177
178  // Indicates whether the account has been inactivated.
179  private final boolean isInactive;
180
181  // Indicates whether the account is usable.
182  private final boolean isUsable;
183
184  // Indicates whether the user's password must be changed before other
185  // operations will be allowed.
186  private final boolean mustChangePassword;
187
188  // Indicates whether the user's password is expired.
189  private final boolean passwordIsExpired;
190
191  // The list of reasons that this account may be considered unusable.
192  private final List<String> unusableReasons;
193
194  // The number of grace logins remaining.
195  private final int remainingGraceLogins;
196
197  // The length of time in seconds until the password expires.
198  private final int secondsUntilExpiration;
199
200  // The length of time in seconds until the account is unlocked.
201  private final int secondsUntilUnlock;
202
203
204
205  /**
206   * Creates a new empty control instance that is intended to be used only for
207   * decoding controls via the {@code DecodeableControl} interface.
208   */
209  AccountUsableResponseControl()
210  {
211    isUsable               = false;
212    secondsUntilExpiration = 0;
213    isInactive             = false;
214    mustChangePassword     = false;
215    passwordIsExpired      = false;
216    remainingGraceLogins   = 0;
217    secondsUntilUnlock     = 0;
218    unusableReasons        = Collections.emptyList();
219  }
220
221
222
223  /**
224   * Creates a new account usable response control which indicates that the
225   * account is usable.
226   *
227   * @param  secondsUntilExpiration  The length of time in seconds until the
228   *                                 user's password expires, or -1 if password
229   *                                 expiration is not enabled for the user.
230   */
231  public AccountUsableResponseControl(final int secondsUntilExpiration)
232  {
233    super(ACCOUNT_USABLE_RESPONSE_OID, false,
234          encodeValue(secondsUntilExpiration));
235
236    isUsable                    = true;
237    this.secondsUntilExpiration = secondsUntilExpiration;
238    isInactive                  = false;
239    mustChangePassword          = false;
240    passwordIsExpired           = false;
241    remainingGraceLogins        = -1;
242    secondsUntilUnlock          = -1;
243    unusableReasons             = Collections.emptyList();
244  }
245
246
247
248  /**
249   * Creates a new account usable response control which indicates that the
250   * account is not usable.
251   *
252   * @param  isInactive            Indicates whether the user account has been
253   *                               inactivated.
254   * @param  mustChangePassword    Indicates whether the user is required to
255   *                               change his/her password before any other
256   *                               operations will be allowed.
257   * @param  passwordIsExpired     Indicates whether the user's password has
258   *                               expired.
259   * @param  remainingGraceLogins  The number of remaining grace logins for the
260   *                               user.
261   * @param  secondsUntilUnlock    The length of time in seconds until the
262   *                               user's account will be automatically
263   *                               unlocked.
264   */
265  public AccountUsableResponseControl(final boolean isInactive,
266                                      final boolean mustChangePassword,
267                                      final boolean passwordIsExpired,
268                                      final int remainingGraceLogins,
269                                      final int secondsUntilUnlock)
270  {
271    super(ACCOUNT_USABLE_RESPONSE_OID, false,
272          encodeValue(isInactive, mustChangePassword, passwordIsExpired,
273                      remainingGraceLogins, secondsUntilUnlock));
274
275    isUsable                  = false;
276    secondsUntilExpiration    = -1;
277    this.isInactive           = isInactive;
278    this.mustChangePassword   = mustChangePassword;
279    this.passwordIsExpired    = passwordIsExpired;
280    this.remainingGraceLogins = remainingGraceLogins;
281    this.secondsUntilUnlock   = secondsUntilUnlock;
282
283    final ArrayList<String> unusableList = new ArrayList<>(5);
284    if (isInactive)
285    {
286      unusableList.add(ERR_ACCT_UNUSABLE_INACTIVE.get());
287    }
288
289    if (mustChangePassword)
290    {
291      unusableList.add(ERR_ACCT_UNUSABLE_MUST_CHANGE_PW.get());
292    }
293
294    if (passwordIsExpired)
295    {
296      unusableList.add(ERR_ACCT_UNUSABLE_PW_EXPIRED.get());
297    }
298
299    if (remainingGraceLogins >= 0)
300    {
301      switch (remainingGraceLogins)
302      {
303        case 0:
304          unusableList.add(ERR_ACCT_UNUSABLE_REMAINING_GRACE_NONE.get());
305          break;
306        case 1:
307          unusableList.add(ERR_ACCT_UNUSABLE_REMAINING_GRACE_ONE.get());
308          break;
309        default:
310          unusableList.add(ERR_ACCT_UNUSABLE_REMAINING_GRACE_MULTIPLE.get(
311                                remainingGraceLogins));
312          break;
313      }
314    }
315
316    if (secondsUntilUnlock > 0)
317    {
318      unusableList.add(
319           ERR_ACCT_UNUSABLE_SECONDS_UNTIL_UNLOCK.get(secondsUntilUnlock));
320    }
321
322    unusableReasons = Collections.unmodifiableList(unusableList);
323  }
324
325
326
327  /**
328   * Creates a new account usable response control with the provided
329   * information.
330   *
331   * @param  oid         The OID for the control.
332   * @param  isCritical  Indicates whether the control should be marked
333   *                     critical.
334   * @param  value       The encoded value for the control.  This may be
335   *                     {@code null} if no value was provided.
336   *
337   * @throws  LDAPException  If the provided control cannot be decoded as an
338   *                         account usable response control.
339   */
340  public AccountUsableResponseControl(final String oid,
341                                      final boolean isCritical,
342                                      final ASN1OctetString value)
343         throws LDAPException
344  {
345    super(oid, isCritical,  value);
346
347    if (value == null)
348    {
349      throw new LDAPException(ResultCode.DECODING_ERROR,
350                              ERR_ACCOUNT_USABLE_RESPONSE_NO_VALUE.get());
351    }
352
353    final ASN1Element valueElement;
354    try
355    {
356      valueElement = ASN1Element.decode(value.getValue());
357    }
358    catch (final Exception e)
359    {
360      Debug.debugException(e);
361      throw new LDAPException(ResultCode.DECODING_ERROR,
362                     ERR_ACCOUNT_USABLE_RESPONSE_VALUE_NOT_ELEMENT.get(e), e);
363    }
364
365
366    final boolean decodedIsUsable;
367    boolean decodedIsInactive             = false;
368    boolean decodedMustChangePassword     = false;
369    boolean decodedPasswordIsExpired      = false;
370    int     decodedRemainingGraceLogins   = -1;
371    int     decodedSecondsUntilExpiration = -1;
372    int     decodedSecondsUntilUnlock     = -1;
373
374    final List<String> decodedUnusableReasons = new ArrayList<>(5);
375
376
377    final byte type = valueElement.getType();
378    if (type == TYPE_SECONDS_UNTIL_EXPIRATION)
379    {
380      decodedIsUsable = true;
381
382      try
383      {
384        decodedSecondsUntilExpiration =
385             ASN1Integer.decodeAsInteger(valueElement).intValue();
386        if (decodedSecondsUntilExpiration < 0)
387        {
388          decodedSecondsUntilExpiration = -1;
389        }
390      }
391      catch (final Exception e)
392      {
393        Debug.debugException(e);
394        throw new LDAPException(ResultCode.DECODING_ERROR,
395                       ERR_ACCOUNT_USABLE_RESPONSE_STE_NOT_INT.get(e), e);
396      }
397    }
398    else if (type == TYPE_MORE_INFO)
399    {
400      decodedIsUsable = false;
401
402      final ASN1Element[] elements;
403      try
404      {
405        elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
406      }
407      catch (final Exception e)
408      {
409        Debug.debugException(e);
410        throw new LDAPException(ResultCode.DECODING_ERROR,
411                       ERR_ACCOUNT_USABLE_RESPONSE_VALUE_NOT_SEQUENCE.get(e),
412                       e);
413      }
414
415      for (final ASN1Element element : elements)
416      {
417        switch (element.getType())
418        {
419          case TYPE_IS_INACTIVE:
420            try
421            {
422              decodedIsInactive =
423                   ASN1Boolean.decodeAsBoolean(element).booleanValue();
424              decodedUnusableReasons.add(ERR_ACCT_UNUSABLE_INACTIVE.get());
425            }
426            catch (final Exception e)
427            {
428              Debug.debugException(e);
429              throw new LDAPException(ResultCode.DECODING_ERROR,
430                   ERR_ACCOUNT_USABLE_RESPONSE_INACTIVE_NOT_BOOLEAN.get(e), e);
431            }
432            break;
433
434          case TYPE_MUST_CHANGE:
435            try
436            {
437              decodedMustChangePassword =
438                   ASN1Boolean.decodeAsBoolean(element).booleanValue();
439              decodedUnusableReasons.add(
440                   ERR_ACCT_UNUSABLE_MUST_CHANGE_PW.get());
441            }
442            catch (final Exception e)
443            {
444              Debug.debugException(e);
445              throw new LDAPException(ResultCode.DECODING_ERROR,
446                   ERR_ACCOUNT_USABLE_RESPONSE_MUST_CHANGE_NOT_BOOLEAN.get(e),
447                   e);
448            }
449            break;
450
451          case TYPE_IS_EXPIRED:
452            try
453            {
454              decodedPasswordIsExpired =
455                   ASN1Boolean.decodeAsBoolean(element).booleanValue();
456              decodedUnusableReasons.add(ERR_ACCT_UNUSABLE_PW_EXPIRED.get());
457            }
458            catch (final Exception e)
459            {
460              Debug.debugException(e);
461              throw new LDAPException(ResultCode.DECODING_ERROR,
462                   ERR_ACCOUNT_USABLE_RESPONSE_IS_EXP_NOT_BOOLEAN.get(e), e);
463            }
464            break;
465
466          case TYPE_REMAINING_GRACE_LOGINS:
467            try
468            {
469              decodedRemainingGraceLogins =
470                   ASN1Integer.decodeAsInteger(element).intValue();
471              if (decodedRemainingGraceLogins < 0)
472              {
473                decodedRemainingGraceLogins = -1;
474              }
475              else
476              {
477                switch (decodedRemainingGraceLogins)
478                {
479                  case 0:
480                    decodedUnusableReasons.add(
481                         ERR_ACCT_UNUSABLE_REMAINING_GRACE_NONE.get());
482                    break;
483                  case 1:
484                    decodedUnusableReasons.add(
485                         ERR_ACCT_UNUSABLE_REMAINING_GRACE_ONE.get());
486                    break;
487                  default:
488                    decodedUnusableReasons.add(
489                         ERR_ACCT_UNUSABLE_REMAINING_GRACE_MULTIPLE.get(
490                              decodedRemainingGraceLogins));
491                    break;
492                }
493              }
494            }
495            catch (final Exception e)
496            {
497              Debug.debugException(e);
498              throw new LDAPException(ResultCode.DECODING_ERROR,
499                   ERR_ACCOUNT_USABLE_RESPONSE_GRACE_LOGINS_NOT_INT.get(e), e);
500            }
501            break;
502
503          case TYPE_SECONDS_UNTIL_UNLOCK:
504            try
505            {
506              decodedSecondsUntilUnlock =
507                   ASN1Integer.decodeAsInteger(element).intValue();
508              if (decodedSecondsUntilUnlock < 0)
509              {
510                decodedSecondsUntilUnlock = -1;
511              }
512              else if (decodedSecondsUntilUnlock > 0)
513              {
514                decodedUnusableReasons.add(
515                     ERR_ACCT_UNUSABLE_SECONDS_UNTIL_UNLOCK.get(
516                          decodedSecondsUntilUnlock));
517              }
518            }
519            catch (final Exception e)
520            {
521              Debug.debugException(e);
522              throw new LDAPException(ResultCode.DECODING_ERROR,
523                   ERR_ACCOUNT_USABLE_RESPONSE_STU_NOT_INT.get(e), e);
524            }
525            break;
526
527          default:
528            throw new LDAPException(ResultCode.DECODING_ERROR,
529                 ERR_ACCOUNT_USABLE_RESPONSE_MORE_INFO_INVALID_TYPE.get(
530                      StaticUtils.toHex(element.getType())));
531        }
532      }
533    }
534    else
535    {
536      throw new LDAPException(ResultCode.DECODING_ERROR,
537           ERR_ACCOUNT_USABLE_RESPONSE_INVALID_TYPE.get(
538                StaticUtils.toHex(type)));
539    }
540
541    isUsable               = decodedIsUsable;
542    secondsUntilExpiration = decodedSecondsUntilExpiration;
543    isInactive             = decodedIsInactive;
544    mustChangePassword     = decodedMustChangePassword;
545    passwordIsExpired      = decodedPasswordIsExpired;
546    remainingGraceLogins   = decodedRemainingGraceLogins;
547    secondsUntilUnlock     = decodedSecondsUntilUnlock;
548    unusableReasons        =
549         Collections.unmodifiableList(decodedUnusableReasons);
550  }
551
552
553
554  /**
555   * Creates an ASN.1 octet string that may be used as the value of an account
556   * usable response control if the account is usable.
557   *
558   * @param  secondsUntilExpiration  The length of time in seconds until the
559   *                                 user's password expires, or -1 if password
560   *                                 expiration is not enabled for the user.
561   *
562   * @return  The ASN.1 octet string that may be used as the control value.
563   */
564  private static ASN1OctetString encodeValue(final int secondsUntilExpiration)
565  {
566    final ASN1Integer sueInteger =
567         new ASN1Integer(TYPE_SECONDS_UNTIL_EXPIRATION, secondsUntilExpiration);
568
569    return new ASN1OctetString(sueInteger.encode());
570  }
571
572
573
574  /**
575   * Creates an ASN.1 octet string that may be used of the value of an account
576   * usable response control if the account is not usable.
577   *
578   * @param  isInactive            Indicates whether the user account has been
579   *                               inactivated.
580   * @param  mustChangePassword    Indicates whether the user is required to
581   *                               change his/her password before any other
582   *                               operations will be allowed.
583   * @param  passwordIsExpired     Indicates whether the user's password has
584   *                               expired.
585   * @param  remainingGraceLogins  The number of remaining grace logins for the
586   *                               user.
587   * @param  secondsUntilUnlock    The length of time in seconds until the
588   *                               user's account will be automatically
589   *                               unlocked.
590   *
591   * @return  The ASN.1 octet string that may be used as the control value.
592   */
593  private static ASN1OctetString encodeValue(final boolean isInactive,
594                                             final boolean mustChangePassword,
595                                             final boolean passwordIsExpired,
596                                             final int remainingGraceLogins,
597                                             final int secondsUntilUnlock)
598  {
599    final ArrayList<ASN1Element> elements = new ArrayList<>(5);
600
601    if (isInactive)
602    {
603      elements.add(new ASN1Boolean(TYPE_IS_INACTIVE, true));
604    }
605
606    if (mustChangePassword)
607    {
608      elements.add(new ASN1Boolean(TYPE_MUST_CHANGE, true));
609    }
610
611    if (passwordIsExpired)
612    {
613      elements.add(new ASN1Boolean(TYPE_IS_EXPIRED, true));
614    }
615
616    if (remainingGraceLogins >= 0)
617    {
618      elements.add(new ASN1Integer(TYPE_REMAINING_GRACE_LOGINS,
619                                   remainingGraceLogins));
620    }
621
622    if (secondsUntilUnlock >= 0)
623    {
624      elements.add(new ASN1Integer(TYPE_SECONDS_UNTIL_UNLOCK,
625                                   secondsUntilUnlock));
626    }
627
628    final ASN1Sequence valueSequence =
629         new ASN1Sequence(TYPE_MORE_INFO, elements);
630    return new ASN1OctetString(valueSequence.encode());
631  }
632
633
634
635  /**
636   * {@inheritDoc}
637   */
638  @Override()
639  public AccountUsableResponseControl decodeControl(final String oid,
640                                                    final boolean isCritical,
641                                                    final ASN1OctetString value)
642         throws LDAPException
643  {
644    return new AccountUsableResponseControl(oid, isCritical, value);
645  }
646
647
648
649  /**
650   * Extracts an account usable response control from the provided search result
651   * entry.
652   *
653   * @param  entry  The search result entry from which to retrieve the account
654   *                usable response control.
655   *
656   * @return  The account usable response control contained in the provided
657   *          search result entry, or {@code null} if the entry did not contain
658   *          an account usable response control.
659   *
660   * @throws  LDAPException  If a problem is encountered while attempting to
661   *                         decode the account usable response control
662   *                         contained in the provided result.
663   */
664  public static AccountUsableResponseControl get(final SearchResultEntry entry)
665         throws LDAPException
666  {
667    final Control c = entry.getControl(ACCOUNT_USABLE_RESPONSE_OID);
668    if (c == null)
669    {
670      return null;
671    }
672
673    if (c instanceof AccountUsableResponseControl)
674    {
675      return (AccountUsableResponseControl) c;
676    }
677    else
678    {
679      return new AccountUsableResponseControl(c.getOID(), c.isCritical(),
680           c.getValue());
681    }
682  }
683
684
685
686  /**
687   * Indicates whether the associated user account is usable.
688   *
689   * @return  {@code true} if the user account is usable, or {@code false} if
690   *          not.
691   */
692  public boolean isUsable()
693  {
694    return isUsable;
695  }
696
697
698
699  /**
700   * Retrieves the list of reasons that this account may be unusable.
701   *
702   * @return  The list of reasons that this account may be unusable, or an empty
703   *          list if the account is usable or no reasons are available.
704   */
705  public List<String> getUnusableReasons()
706  {
707    return unusableReasons;
708  }
709
710
711
712  /**
713   * Retrieves the number of seconds until the user's password expires.  This
714   * will only available if the account is usable.
715   *
716   * @return  The number of seconds until the user's password expires, or -1 if
717   *          the user account is not usable, or if password expiration is not
718   *          enabled in the directory server.
719   */
720  public int getSecondsUntilExpiration()
721  {
722    return secondsUntilExpiration;
723  }
724
725
726
727  /**
728   * Indicates whether the user account has been inactivated by a server
729   * administrator.
730   *
731   * @return  {@code true} if the user account has been inactivated by a server
732   *          administrator, or {@code false} if not.
733   */
734  public boolean isInactive()
735  {
736    return isInactive;
737  }
738
739
740
741  /**
742   * Indicates whether the user must change his or her password before being
743   * allowed to perform any other operations.
744   *
745   * @return  {@code true} if the user must change his or her password before
746   *          being allowed to perform any other operations, or {@code false} if
747   *          not.
748   */
749  public boolean mustChangePassword()
750  {
751    return mustChangePassword;
752  }
753
754
755
756  /**
757   * Indicates whether the user's password is expired.
758   *
759   * @return  {@code true} if the user's password is expired, or {@code false}
760   *          if not.
761   */
762  public boolean passwordIsExpired()
763  {
764    return passwordIsExpired;
765  }
766
767
768
769  /**
770   * Retrieves the number of remaining grace logins for the user.  This will
771   * only be available if the user account is not usable.
772   *
773   * @return  The number of remaining grace logins for the user, or -1 if this
774   *          is not available (e.g., because the account is usable or grace
775   *          login functionality is disabled on the server).
776   */
777  public int getRemainingGraceLogins()
778  {
779    return remainingGraceLogins;
780  }
781
782
783
784  /**
785   * Retrieves the length of time in seconds until the user's account is
786   * automatically unlocked.  This will only be available if the user account is
787   * not usable.
788   *
789   * @return  The length of time in seconds until the user's account is
790   *          automatically unlocked, or -1 if this is not available (e.g.,
791   *          because the account is usable, or because the account is not
792   *          locked, or because automatic unlocking is disabled on the server).
793   */
794  public int getSecondsUntilUnlock()
795  {
796    return secondsUntilUnlock;
797  }
798
799
800
801  /**
802   * {@inheritDoc}
803   */
804  @Override()
805  public String getControlName()
806  {
807    return INFO_CONTROL_NAME_ACCOUNT_USABLE_RESPONSE.get();
808  }
809
810
811
812  /**
813   * {@inheritDoc}
814   */
815  @Override()
816  public void toString(final StringBuilder buffer)
817  {
818    buffer.append("AccountUsableResponseControl(isUsable=");
819    buffer.append(isUsable);
820
821    if (isUsable)
822    {
823      if (secondsUntilExpiration >= 0)
824      {
825        buffer.append(", secondsUntilExpiration=");
826        buffer.append(secondsUntilExpiration);
827      }
828    }
829    else
830    {
831      buffer.append(", isInactive=");
832      buffer.append(isInactive);
833      buffer.append(", mustChangePassword=");
834      buffer.append(mustChangePassword);
835      buffer.append(", passwordIsExpired=");
836      buffer.append(passwordIsExpired);
837
838      if (remainingGraceLogins >= 0)
839      {
840        buffer.append(", remainingGraceLogins=");
841        buffer.append(remainingGraceLogins);
842      }
843
844      if (secondsUntilUnlock >= 0)
845      {
846        buffer.append(", secondsUntilUnlock=");
847        buffer.append(secondsUntilUnlock);
848      }
849    }
850
851    buffer.append(')');
852  }
853}