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 }