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