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