mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
02.31.2006 e627deb0b7849bb8119b9566dd093b92ce9a2dbd
Add a new account expiration feature to the password policy.  This will make it
possible for accounts to be given an expiration time, after which it will not
be possible to authenticate as that user or target that user with the proxied
authorization control.

If the ds-pwp-account-expiration-time operational attribute is included in the
user's entry and it references a time in the past, then the user's account will
be considered expired. If it references a time in the future, then it will be
expired at that time. If this attribute is absent, then the account will not
expire.

OpenDS Issue Number: 543
8 files modified
259 ■■■■ changed files
opends/resource/schema/02-config.ldif 3 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/config/ConfigConstants.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java 12 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/BindOperation.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PasswordPolicyState.java 189 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperation.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 13 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -956,6 +956,9 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.277
  NAME 'ds-cfg-backend-import-pass-size' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.280
  NAME 'ds-pwp-account-expiration-time' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
  SINGLE-VALUE USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2932,6 +2932,16 @@
  /**
   * The name of the operational attribute that may appear in a user's entry to
   * indicate when that account will expire (and therefore may no longer be used
   * to authenticate).
   */
  public static final String OP_ATTR_ACCOUNT_EXPIRATION_TIME =
       NAME_PREFIX_PWP + "account-expiration-time";
  /**
   * The name of the operational attribute that will appear in an entry to
   * indicate when it was created.
   */
opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
@@ -367,9 +367,11 @@
      // processing.
      PasswordPolicyState pwpState = new PasswordPolicyState(userEntry, false,
                                                             false);
      if (pwpState.isDisabled() || pwpState.lockedDueToFailures() ||
      if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
          pwpState.lockedDueToFailures() ||
          pwpState.lockedDueToIdleInterval() ||
          pwpState.lockedDueToMaximumResetAge() || pwpState.isExpired())
          pwpState.lockedDueToMaximumResetAge() ||
          pwpState.isPasswordExpired())
      {
        int    msgID   = MSGID_PROXYAUTH1_UNUSABLE_ACCOUNT;
        String message = getMessage(msgID, String.valueOf(authzDN));
opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
@@ -319,9 +319,11 @@
          // processing.
          PasswordPolicyState pwpState =
               new PasswordPolicyState(userEntry, false, false);
          if (pwpState.isDisabled() || pwpState.lockedDueToFailures() ||
          if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
              pwpState.lockedDueToFailures() ||
              pwpState.lockedDueToIdleInterval() ||
              pwpState.lockedDueToMaximumResetAge() || pwpState.isExpired())
              pwpState.lockedDueToMaximumResetAge() ||
              pwpState.isPasswordExpired())
          {
            int    msgID   = MSGID_PROXYAUTH2_UNUSABLE_ACCOUNT;
            String message = getMessage(msgID, String.valueOf(authzDN));
@@ -376,9 +378,11 @@
        // processing.
        PasswordPolicyState pwpState =
             new PasswordPolicyState(userEntry, false, false);
        if (pwpState.isDisabled() || pwpState.lockedDueToFailures() ||
        if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
            pwpState.lockedDueToFailures() ||
            pwpState.lockedDueToIdleInterval() ||
            pwpState.lockedDueToMaximumResetAge() || pwpState.isExpired())
            pwpState.lockedDueToMaximumResetAge() ||
            pwpState.isPasswordExpired())
        {
          int    msgID   = MSGID_PROXYAUTH2_UNUSABLE_ACCOUNT;
          String message = getMessage(msgID, String.valueOf(authzDN));
opends/src/server/org/opends/server/core/BindOperation.java
@@ -1165,6 +1165,15 @@
              setAuthFailureReason(msgID, message);
              break bindProcessing;
            }
            else if (pwPolicyState.isAccountExpired())
            {
              int    msgID   = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED;
              String message = getMessage(msgID, String.valueOf(bindDN));
              setResultCode(ResultCode.INVALID_CREDENTIALS);
              setAuthFailureReason(msgID, message);
              break bindProcessing;
            }
            else if (pwPolicyState.lockedDueToFailures())
            {
              int    msgID   = MSGID_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED;
@@ -1211,7 +1220,7 @@
            // Determine whether the password is expired, or whether the user
            // should be warned about an upcoming expiration.
            if (pwPolicyState.isExpired())
            if (pwPolicyState.isPasswordExpired())
            {
              if (pwPolicyErrorType == null)
              {
@@ -1547,6 +1556,14 @@
              appendErrorMessage(getMessage(msgID, userDNString));
              break bindProcessing;
            }
            else if (pwPolicyState.isAccountExpired())
            {
              setResultCode(ResultCode.INVALID_CREDENTIALS);
              int msgID = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED;
              appendErrorMessage(getMessage(msgID, userDNString));
              break bindProcessing;
            }
            if (pwPolicyState.requireSecureAuthentication() &&
                (! clientConnection.isSecure()) &&
@@ -1605,7 +1622,7 @@
                break bindProcessing;
              }
              if (pwPolicyState.isExpired())
              if (pwPolicyState.isPasswordExpired())
              {
                if (pwPolicyErrorType == null)
                {
opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -102,11 +102,14 @@
  // successful.
  private boolean useGraceLogin;
  // Indicates whether the user's account is expired.
  private ConditionResult isAccountExpired;
  // Indicates whether the user's account is disabled.
  private ConditionResult isDisabled;
  // Indicates whether the user's password is expired.
  private ConditionResult isExpired;
  private ConditionResult isPasswordExpired;
  // Indicates whether the warning to send to the client would be the first
  // warning for the user.
@@ -213,7 +216,8 @@
    currentTime            = TimeThread.getTime();
    modifications          = new LinkedList<Modification>();
    isDisabled             = ConditionResult.UNDEFINED;
    isExpired              = ConditionResult.UNDEFINED;
    isAccountExpired       = ConditionResult.UNDEFINED;
    isPasswordExpired      = ConditionResult.UNDEFINED;
    isFirstWarning         = ConditionResult.UNDEFINED;
    isIdleLocked           = ConditionResult.UNDEFINED;
    isResetLocked          = ConditionResult.UNDEFINED;
@@ -1004,7 +1008,7 @@
            debugMessage(DebugLogCategory.PASSWORD_POLICY,
                         DebugLogSeverity.INFO, CLASS_NAME, "isDisabled",
                         "User " + userDNString +
                         " is not administratively disabled.");
                         " is administratively disabled.");
          }
          isDisabled = ConditionResult.TRUE;
@@ -1017,7 +1021,7 @@
            debugMessage(DebugLogCategory.PASSWORD_POLICY,
                         DebugLogSeverity.INFO, CLASS_NAME, "isDisabled",
                         "User " + userDNString +
                         " is administratively disabled.");
                         " is not administratively disabled.");
          }
          isDisabled = ConditionResult.FALSE;
@@ -1145,6 +1149,120 @@
  /**
   * Indicates whether the user's account is currently expired.
   *
   * @return  <CODE>true</CODE> if the user's account is expired, or
   *          <CODE>false</CODE> if not.
   */
  public boolean isAccountExpired()
  {
    assert debugEnter(CLASS_NAME, "isAccountExpired");
    if ((isAccountExpired == null) ||
        (isAccountExpired == ConditionResult.UNDEFINED))
    {
      AttributeType type =
           DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                            true);
      try
      {
        long expirationTime = getGeneralizedTime(type);
        if (expirationTime < 0)
        {
          // The user doesn't have an expiration time in their entry, so it
          // can't be expired.
          if (debug)
          {
            debugMessage(DebugLogCategory.PASSWORD_POLICY,
                         DebugLogSeverity.INFO, CLASS_NAME, "isAccountExpired",
                         "The account for user " + userDNString +
                         " is not expired because there is no expiration " +
                         "time in the user's entry.");
          }
          isAccountExpired = ConditionResult.FALSE;
          return false;
        }
        else if (expirationTime > currentTime)
        {
          // The user does have an expiration time, but it hasn't arrived yet.
          if (debug)
          {
            debugMessage(DebugLogCategory.PASSWORD_POLICY,
                         DebugLogSeverity.INFO, CLASS_NAME, "isAccountExpired",
                         "The account for user " + userDNString +
                         " is not expired because the expiration time has " +
                         "not yet arrived.");
          }
          isAccountExpired = ConditionResult.FALSE;
          return false;
        }
        else
        {
          // The user does have an expiration time, and it is in the past.
          if (debug)
          {
            debugMessage(DebugLogCategory.PASSWORD_POLICY,
                         DebugLogSeverity.INFO, CLASS_NAME, "isAccountExpired",
                         "The account for user " + userDNString +
                         " is expired because the expiration time in that " +
                         "account has passed.");
          }
          isAccountExpired = ConditionResult.TRUE;
          return true;
        }
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "isAccountExpired", e);
        if (debug)
        {
          debugMessage(DebugLogCategory.PASSWORD_POLICY,
                       DebugLogSeverity.WARNING, CLASS_NAME, "isAccountExpired",
                       "User " + userDNString +" is considered to have an " +
                       "expired account because an error occurred " +
                       "while attempting to make the determination:  " +
                       stackTraceToSingleLineString(e) + ".");
        }
        isAccountExpired = ConditionResult.TRUE;
        return true;
      }
    }
    if (isAccountExpired == ConditionResult.FALSE)
    {
      if (debug)
      {
        debugMessage(DebugLogCategory.PASSWORD_POLICY, DebugLogSeverity.INFO,
                     CLASS_NAME, "isAccountExpired",
                     "Returning stored result of false for user " +
                     userDNString);
      }
      return false;
    }
    else
    {
      if (debug)
      {
        debugMessage(DebugLogCategory.PASSWORD_POLICY, DebugLogSeverity.INFO,
                     CLASS_NAME, "isAccountExpired",
                     "Returning stored result of true for user " +
                     userDNString);
      }
      return true;
    }
  }
  /**
   * Retrieves the set of times of failed authentication attempts for the user.
   *
   * @return  The set of times of failed authentication attempts for the user.
@@ -2339,11 +2457,11 @@
      if (expirationTime == Long.MAX_VALUE)
      {
        expirationTime   = -1;
        shouldWarn       = ConditionResult.FALSE;
        isFirstWarning   = ConditionResult.FALSE;
        isExpired        = ConditionResult.FALSE;
        mayUseGraceLogin = ConditionResult.TRUE;
        expirationTime    = -1;
        shouldWarn        = ConditionResult.FALSE;
        isFirstWarning    = ConditionResult.FALSE;
        isPasswordExpired = ConditionResult.FALSE;
        mayUseGraceLogin  = ConditionResult.TRUE;
      }
      else if (checkWarning)
      {
@@ -2357,9 +2475,9 @@
          {
            // The warning time is in the future, so we know the password isn't
            // expired.
            shouldWarn     = ConditionResult.FALSE;
            isFirstWarning = ConditionResult.FALSE;
            isExpired      = ConditionResult.FALSE;
            shouldWarn        = ConditionResult.FALSE;
            isFirstWarning    = ConditionResult.FALSE;
            isPasswordExpired = ConditionResult.FALSE;
          }
          else
          {
@@ -2370,8 +2488,8 @@
            if (expirationTime > currentTime)
            {
              // The password is not expired but we should warn the user.
              shouldWarn = ConditionResult.TRUE;
              isExpired  = ConditionResult.FALSE;
              shouldWarn        = ConditionResult.TRUE;
              isPasswordExpired = ConditionResult.FALSE;
              if (warnedTime < 0)
              {
@@ -2399,32 +2517,32 @@
              // expired if the user has not yet seen a warning.
              if (passwordPolicy.expirePasswordsWithoutWarning())
              {
                shouldWarn     = ConditionResult.FALSE;
                isFirstWarning = ConditionResult.FALSE;
                isExpired      = ConditionResult.TRUE;
                shouldWarn        = ConditionResult.FALSE;
                isFirstWarning    = ConditionResult.FALSE;
                isPasswordExpired = ConditionResult.TRUE;
              }
              else if (warnedTime > 0)
              {
                expirationTime = warnedTime + (warningInterval*1000);
                if (expirationTime > currentTime)
                {
                  shouldWarn     = ConditionResult.TRUE;
                  isFirstWarning = ConditionResult.FALSE;
                  isExpired      = ConditionResult.FALSE;
                  shouldWarn        = ConditionResult.TRUE;
                  isFirstWarning    = ConditionResult.FALSE;
                  isPasswordExpired = ConditionResult.FALSE;
                }
                else
                {
                  shouldWarn     = ConditionResult.FALSE;
                  isFirstWarning = ConditionResult.FALSE;
                  isExpired      = ConditionResult.TRUE;
                  shouldWarn        = ConditionResult.FALSE;
                  isFirstWarning    = ConditionResult.FALSE;
                  isPasswordExpired = ConditionResult.TRUE;
                }
              }
              else
              {
                shouldWarn     = ConditionResult.TRUE;
                isFirstWarning = ConditionResult.TRUE;
                isExpired      = ConditionResult.FALSE;
                expirationTime = currentTime + (warningInterval*1000);
                shouldWarn        = ConditionResult.TRUE;
                isFirstWarning    = ConditionResult.TRUE;
                isPasswordExpired = ConditionResult.FALSE;
                expirationTime    = currentTime + (warningInterval*1000);
              }
            }
          }
@@ -2438,11 +2556,11 @@
          if (currentTime > expirationTime)
          {
            isExpired = ConditionResult.TRUE;
            isPasswordExpired = ConditionResult.TRUE;
          }
          else
          {
            isExpired = ConditionResult.FALSE;
            isPasswordExpired = ConditionResult.FALSE;
          }
        }
      }
@@ -2454,11 +2572,11 @@
        if (expirationTime < currentTime)
        {
          isExpired = ConditionResult.TRUE;
          isPasswordExpired = ConditionResult.TRUE;
        }
        else
        {
          isExpired = ConditionResult.FALSE;
          isPasswordExpired = ConditionResult.FALSE;
        }
      }
    }
@@ -2484,16 +2602,17 @@
   * @return  <CODE>true</CODE> if the user's password is currently expired, or
   *          <CODE>false</CODE> if not.
   */
  public boolean isExpired()
  public boolean isPasswordExpired()
  {
    assert debugEnter(CLASS_NAME, "isExpired");
    assert debugEnter(CLASS_NAME, "isPasswordExpired");
    if ((isExpired == null) || (isExpired == ConditionResult.UNDEFINED))
    if ((isPasswordExpired == null) ||
        (isPasswordExpired == ConditionResult.UNDEFINED))
    {
      getPasswordExpirationTime();
    }
    if (isExpired == ConditionResult.TRUE)
    if (isPasswordExpired == ConditionResult.TRUE)
    {
      return true;
    }
opends/src/server/org/opends/server/core/SearchOperation.java
@@ -827,12 +827,13 @@
        PasswordPolicyState pwpState = new PasswordPolicyState(entry, false,
                                                               false);
        boolean isInactive           = pwpState.isDisabled();
        boolean isInactive           = pwpState.isDisabled() ||
                                       pwpState.isAccountExpired();
        boolean isLocked             = pwpState.lockedDueToFailures() ||
                                       pwpState.lockedDueToMaximumResetAge() ||
                                       pwpState.lockedDueToIdleInterval();
        boolean isReset              = pwpState.mustChangePassword();
        boolean isExpired            = pwpState.isExpired();
        boolean isExpired            = pwpState.isPasswordExpired();
        if (isInactive || isLocked || isReset || isExpired)
        {
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5568,6 +5568,16 @@
  /**
   * The message ID for the message that will be used if a user tries to
   * authenticate using an expired account.  This takes a single argument, which
   * is the DN of the user.
   */
  public static final int MSGID_BIND_OPERATION_ACCOUNT_EXPIRED =
       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 531;
  /**
   * Associates a set of generic messages with the message IDs defined
   * in this class.
   */
@@ -7497,6 +7507,9 @@
    registerMessage(MSGID_BIND_OPERATION_ACCOUNT_DISABLED,
                    "Rejecting a bind request for user %s because the " +
                    "account has been administrative disabled.");
    registerMessage(MSGID_BIND_OPERATION_ACCOUNT_EXPIRED,
                    "Rejecting a bind request for user %s because the " +
                    "account has expired.");
    registerMessage(MSGID_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED,
                    "Rejecting a bind request for user %s because the " +
                    "account has been locked due to too many failed " +