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 }