From 5133f4340c33425a6a2cba4b8764d6e684b37da4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 27 Jun 2007 22:15:31 +0000
Subject: [PATCH] Add a new extended operation that can be used to interact with password policy state information for a user, including getting and setting various state variables (even those marked NO-USER-MODIFICATION, although these are typically intended for testing purposes only).  Users will be required to have the password-reset privilege in order to be able to use this extended operation, and access control will also come into play.

---
 opends/src/server/org/opends/server/core/PasswordPolicyState.java |  552 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 503 insertions(+), 49 deletions(-)

diff --git a/opends/src/server/org/opends/server/core/PasswordPolicyState.java b/opends/src/server/org/opends/server/core/PasswordPolicyState.java
index e37f904..d3a43fe 100644
--- a/opends/src/server/org/opends/server/core/PasswordPolicyState.java
+++ b/opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -31,6 +31,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -147,8 +148,8 @@
   // The set of grace login times for this user.
   private List<Long> graceLoginTimes = null;
 
-  // The time that the user's password should expire (or did expire).
-  private long expirationTime = Long.MIN_VALUE;
+  // The time that the user's account should expire (or did expire).
+  private long accountExpirationTime = Long.MIN_VALUE;
 
   // The time that the user's entry was locked due to too many authentication
   // failures.
@@ -157,6 +158,9 @@
   // The time that the user last authenticated to the Directory Server.
   private long lastLoginTime = Long.MIN_VALUE;
 
+  // The time that the user's password should expire (or did expire).
+  private long passwordExpirationTime = Long.MIN_VALUE;
+
   // The last required change time with which the user complied.
   private long requiredChangeTime = Long.MIN_VALUE;
 
@@ -187,13 +191,42 @@
                              boolean debug)
        throws DirectoryException
   {
+    this(userEntry, updateEntry, TimeThread.getTime(), debug);
+  }
+
+
+
+  /**
+   * Creates a new password policy state object with the provided information.
+   * Note that this version of the constructor should only be used for testing
+   * purposes when the tests should be evaluated with a fixed time rather than
+   * the actual current time.  For all other purposes, the other constructor
+   * should be used.
+   *
+   * @param  userEntry    The entry with the user account.
+   * @param  updateEntry  Indicates whether changes should update the provided
+   *                      user entry directly or whether they should be
+   *                      collected as a set of modifications.
+   * @param  currentTime  The time to use as the current time for all
+   *                      time-related determinations.
+   * @param  debug        Indicates whether to enable debugging for the
+   *                      operations performed.
+   *
+   * @throws  DirectoryException  If a problem occurs while attempting to
+   *                              determine the password policy for the user or
+   *                              perform any other state initialization.
+   */
+  public PasswordPolicyState(Entry userEntry, boolean updateEntry,
+                             long currentTime, boolean debug)
+       throws DirectoryException
+  {
     this.userEntry   = userEntry;
     this.updateEntry = updateEntry;
     this.debug       = debug;
+    this.currentTime = currentTime;
 
     userDNString     = userEntry.getDN().toString();
     passwordPolicy   = getPasswordPolicyInternal(this.userEntry, this.debug);
-    currentTime      = TimeThread.getTime();
 
     // Get the password changed time for the user.
     AttributeType type
@@ -636,6 +669,30 @@
 
 
   /**
+   * Retrieves the time that the password was last changed.
+   *
+   * @return  The time that the password was last changed.
+   */
+  public long getPasswordChangedTime()
+  {
+    return passwordChangedTime;
+  }
+
+
+
+  /**
+   * Retrieves the time that this password policy state object was created.
+   *
+   * @return  The time that this password policy state object was created.
+   */
+  public long getCurrentTime()
+  {
+    return currentTime;
+  }
+
+
+
+  /**
    * Retrieves the set of values for the password attribute from the user entry.
    *
    * @return  The set of values for the password attribute from the user entry.
@@ -664,6 +721,20 @@
    */
   public void setPasswordChangedTime()
   {
+    setPasswordChangedTime(currentTime);
+  }
+
+
+
+  /**
+   * Sets a new value for the password changed time equal to the specified time.
+   * This method should generally only be used for testing purposes, since the
+   * variant that uses the current time is preferred almost everywhere else.
+   *
+   * @param  passwordChangedTime  The time to use
+   */
+  public void setPasswordChangedTime(long passwordChangedTime)
+  {
     if (debug)
     {
       if (debugEnabled())
@@ -675,9 +746,9 @@
 
     // passwordChangedTime is computed in the constructor from values in the
     // entry.
-    if (passwordChangedTime != currentTime)
+    if (this.passwordChangedTime != passwordChangedTime)
     {
-      passwordChangedTime = currentTime;
+      this.passwordChangedTime = passwordChangedTime;
 
       AttributeType type =
            DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC);
@@ -693,11 +764,11 @@
       values.add(new AttributeValue(type, timeValue));
 
       Attribute a = new Attribute(type, OP_ATTR_PWPOLICY_CHANGED_TIME, values);
-      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
-      attrList.add(a);
 
       if (updateEntry)
       {
+        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+        attrList.add(a);
         userEntry.putAttribute(type, attrList);
       }
       else
@@ -710,6 +781,57 @@
 
 
   /**
+   * Removes the password changed time value from the user's entry.  This should
+   * only be used for testing purposes, as it can really mess things up if you
+   * don't know what you're doing.
+   */
+  public void clearPasswordChangedTime()
+  {
+    if (debug)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Clearing password changed time for user %s",
+                         userDNString);
+      }
+    }
+
+    AttributeType type =
+         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC,
+                                       true);
+    if (updateEntry)
+    {
+      userEntry.removeAttribute(type);
+    }
+    else
+    {
+      Attribute a = new Attribute(type);
+      modifications.add(new Modification(ModificationType.REPLACE, a, true));
+    }
+
+
+    // Fall back to using the entry creation time as the password changed time,
+    // if it's defined.  Otherwise, use a value of zero.
+    AttributeType createTimeType =
+         DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true);
+    try
+    {
+      passwordChangedTime = getGeneralizedTime(createTimeType);
+      if (passwordChangedTime <= 0)
+      {
+        passwordChangedTime = 0;
+      }
+    }
+    catch (Exception e)
+    {
+      passwordChangedTime = 0;
+    }
+  }
+
+
+
+
+  /**
    * Indicates whether the user account has been administratively disabled.
    *
    * @return  <CODE>true</CODE> if the user account has been administratively
@@ -876,10 +998,9 @@
          DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
                                           true);
 
-    long expirationTime;
     try
     {
-      expirationTime = getGeneralizedTime(type);
+      accountExpirationTime = getGeneralizedTime(type);
      }
     catch (Exception e)
     {
@@ -900,7 +1021,7 @@
       return true;
     }
 
-    if (expirationTime > currentTime)
+    if (accountExpirationTime > currentTime)
     {
       // The user does have an expiration time, but it hasn't arrived yet.
       isAccountExpired = ConditionResult.FALSE;
@@ -913,7 +1034,7 @@
         }
       }
     }
-    else if (expirationTime >= 0)
+    else if (accountExpirationTime >= 0)
     {
       // The user does have an expiration time, and it is in the past.
       isAccountExpired = ConditionResult.TRUE;
@@ -948,6 +1069,109 @@
 
 
   /**
+   * Retrieves the time at which the user's account will expire.
+   *
+   * @return  The time at which the user's account will expire, or -1 if it is
+   *          not configured with an expiration time.
+   */
+  public long getAccountExpirationTime()
+  {
+    if (accountExpirationTime == Long.MIN_VALUE)
+    {
+      isAccountExpired();
+    }
+
+    return accountExpirationTime;
+  }
+
+
+
+  /**
+   * Sets the user's account expiration time to the specified value.
+   *
+   * @param  accountExpirationTime  The time that the user's account should
+   *                                expire.
+   */
+  public void setAccountExpirationTime(long accountExpirationTime)
+  {
+    if (accountExpirationTime < 0)
+    {
+      clearAccountExpirationTime();
+    }
+    else
+    {
+      String timeStr = GeneralizedTimeSyntax.format(accountExpirationTime);
+
+      if (debug)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugInfo("Setting account expiration time for user %s to %s",
+                    userDNString, timeStr);
+        }
+      }
+
+      this.accountExpirationTime = accountExpirationTime;
+      AttributeType type =
+           DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
+                                            true);
+
+      LinkedHashSet<AttributeValue> values =
+           new LinkedHashSet<AttributeValue>(1);
+      values.add(new AttributeValue(type, timeStr));
+
+      Attribute a = new Attribute(type, OP_ATTR_ACCOUNT_EXPIRATION_TIME,
+                                  values);
+
+      if (updateEntry)
+      {
+        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+        attrList.add(a);
+        userEntry.putAttribute(type, attrList);
+      }
+      else
+      {
+        modifications.add(new Modification(ModificationType.REPLACE, a, true));
+      }
+    }
+  }
+
+
+
+  /**
+   * Clears the user's account expiration time.
+   */
+  public void clearAccountExpirationTime()
+  {
+    if (debug)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Clearing account expiration time for user %s",
+                  userDNString);
+      }
+    }
+
+    accountExpirationTime = -1;
+
+    AttributeType type =
+         DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME,
+                                          true);
+
+    if (updateEntry)
+    {
+      userEntry.removeAttribute(type);
+    }
+    else
+    {
+      modifications.add(new Modification(ModificationType.REPLACE,
+                                         new Attribute(type), true));
+    }
+  }
+
+
+
+  /**
    * Retrieves the set of times of failed authentication attempts for the user.
    * If authentication failure time expiration is enabled, and there are expired
    * times in the entry, these times are removed from the instance field and an
@@ -957,7 +1181,7 @@
    *          which will be an empty list in the case of no valid (unexpired)
    *          times in the entry.
    */
-  private List<Long> getAuthFailureTimes()
+  public List<Long> getAuthFailureTimes()
   {
     if (authFailureTimes != null)
     {
@@ -1185,7 +1409,75 @@
 
     // Now check to see if there have been sufficient failures to lock the
     // account.
-    if (passwordPolicy.getLockoutFailureCount() <= failureTimes.size())
+    int lockoutCount = passwordPolicy.getLockoutFailureCount();
+    if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size()))
+    {
+      setFailureLockedTime(highestFailureTime);
+      if (debug)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugInfo("Locking user account %s due to too many failures.",
+                    userDNString);
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * Explicitly specifies the auth failure times for the associated user.  This
+   * should generally only be used for testing purposes.  Note that it will also
+   * set or clear the locked time as appropriate.
+   *
+   * @param  authFailureTimes  The set of auth failure times to use for the
+   *                           account.  An empty list or {@code null} will
+   *                           clear the account of any existing failures.
+   */
+  public void setAuthFailureTimes(List<Long> authFailureTimes)
+  {
+    if ((authFailureTimes == null) || authFailureTimes.isEmpty())
+    {
+      clearAuthFailureTimes();
+      clearFailureLockedTime();
+      return;
+    }
+
+    long highestFailureTime = -1;
+    for (Long l : authFailureTimes)
+    {
+      highestFailureTime = Math.max(l, highestFailureTime);
+    }
+
+    AttributeType type =
+         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC,
+                                          true);
+
+    LinkedHashSet<AttributeValue> values =
+           new LinkedHashSet<AttributeValue>(authFailureTimes.size());
+    for (Long l : authFailureTimes)
+    {
+      values.add(new AttributeValue(type, GeneralizedTimeSyntax.format(l)));
+    }
+
+    Attribute a = new Attribute(type, OP_ATTR_PWPOLICY_FAILURE_TIME, values);
+
+    if (updateEntry)
+    {
+      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+      attrList.add(a);
+      userEntry.putAttribute(type, attrList);
+    }
+    else
+    {
+      modifications.add(new Modification(ModificationType.REPLACE, a, true));
+    }
+
+    // Now check to see if there have been sufficient failures to lock the
+    // account.
+    int lockoutCount = passwordPolicy.getLockoutFailureCount();
+    if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size()))
     {
       setFailureLockedTime(highestFailureTime);
       if (debug)
@@ -1686,6 +1978,20 @@
    */
   public void setLastLoginTime()
   {
+    setLastLoginTime(currentTime);
+  }
+
+
+
+  /**
+   * Updates the user entry to use the specified last login time.  This should
+   * be used primarily for testing purposes, as the variant that uses the
+   * current time should be used most of the time.
+   *
+   * @param  lastLoginTime  The last login time to set in the user entry.
+   */
+  public void setLastLoginTime(long lastLoginTime)
+  {
     AttributeType type = passwordPolicy.getLastLoginTimeAttribute();
     String format = passwordPolicy.getLastLoginTimeFormat();
 
@@ -1698,7 +2004,8 @@
     try
     {
       SimpleDateFormat dateFormat = new SimpleDateFormat(format);
-      timestamp = dateFormat.format(TimeThread.getDate());
+      timestamp = dateFormat.format(new Date(lastLoginTime));
+      this.lastLoginTime = dateFormat.parse(timestamp).getTime();
     }
     catch (Exception e)
     {
@@ -1764,6 +2071,38 @@
 
 
   /**
+   * Clears the last login time from the user's entry.  This should generally be
+   * used only for testing purposes.
+   */
+  public void clearLastLoginTime()
+  {
+    if (debug)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Clearing last login time for user %s", userDNString);
+      }
+    }
+
+    lastLoginTime = -1;
+
+    AttributeType type =
+         DirectoryServer.getAttributeType(OP_ATTR_LAST_LOGIN_TIME, true);
+
+    if (updateEntry)
+    {
+      userEntry.removeAttribute(type);
+    }
+    else
+    {
+      modifications.add(new Modification(ModificationType.REPLACE,
+                                         new Attribute(type), true));
+    }
+  }
+
+
+
+  /**
    * Indicates whether the user's account is currently locked because it has
    * been idle for too long.
    *
@@ -2108,9 +2447,9 @@
    */
   public long getPasswordExpirationTime()
   {
-    if (expirationTime == Long.MIN_VALUE)
+    if (passwordExpirationTime == Long.MIN_VALUE)
     {
-      expirationTime = Long.MAX_VALUE;
+      passwordExpirationTime = Long.MAX_VALUE;
 
       boolean checkWarning = false;
 
@@ -2118,9 +2457,9 @@
       if (maxAge > 0)
       {
         long expTime = passwordChangedTime + (1000L*maxAge);
-        if (expTime < expirationTime)
+        if (expTime < passwordExpirationTime)
         {
-          expirationTime = expTime;
+          passwordExpirationTime = expTime;
           checkWarning   = true;
         }
       }
@@ -2129,9 +2468,9 @@
       if (mustChangePassword() && (maxResetAge > 0))
       {
         long expTime = passwordChangedTime + (1000L*maxResetAge);
-        if (expTime < expirationTime)
+        if (expTime < passwordExpirationTime)
         {
-          expirationTime = expTime;
+          passwordExpirationTime = expTime;
           checkWarning   = false;
         }
       }
@@ -2141,20 +2480,20 @@
       {
         long reqChangeTime = getRequiredChangeTime();
         if ((reqChangeTime != mustChangeTime) &&
-            (mustChangeTime < expirationTime))
+            (mustChangeTime < passwordExpirationTime))
         {
-          expirationTime = mustChangeTime;
+          passwordExpirationTime = mustChangeTime;
           checkWarning   = true;
         }
       }
 
-      if (expirationTime == Long.MAX_VALUE)
+      if (passwordExpirationTime == Long.MAX_VALUE)
       {
-        expirationTime    = -1;
-        shouldWarn        = ConditionResult.FALSE;
-        isFirstWarning    = ConditionResult.FALSE;
-        isPasswordExpired = ConditionResult.FALSE;
-        mayUseGraceLogin  = ConditionResult.TRUE;
+        passwordExpirationTime = -1;
+        shouldWarn             = ConditionResult.FALSE;
+        isFirstWarning         = ConditionResult.FALSE;
+        isPasswordExpired      = ConditionResult.FALSE;
+        mayUseGraceLogin       = ConditionResult.TRUE;
       }
       else if (checkWarning)
       {
@@ -2163,7 +2502,8 @@
         int warningInterval = passwordPolicy.getWarningInterval();
         if (warningInterval > 0)
         {
-          long shouldWarnTime = expirationTime - (warningInterval*1000L);
+          long shouldWarnTime =
+                    passwordExpirationTime - (warningInterval*1000L);
           if (shouldWarnTime > currentTime)
           {
             // The warning time is in the future, so we know the password isn't
@@ -2178,7 +2518,7 @@
             // expired.
             long warnedTime = getWarnedTime();
 
-            if (expirationTime > currentTime)
+            if (passwordExpirationTime > currentTime)
             {
               // The password is not expired but we should warn the user.
               shouldWarn        = ConditionResult.TRUE;
@@ -2191,7 +2531,8 @@
 
                 if (! passwordPolicy.expirePasswordsWithoutWarning())
                 {
-                  expirationTime = currentTime + (warningInterval*1000L);
+                  passwordExpirationTime =
+                       currentTime + (warningInterval*1000L);
                 }
               }
               else
@@ -2200,7 +2541,7 @@
 
                 if (! passwordPolicy.expirePasswordsWithoutWarning())
                 {
-                  expirationTime = warnedTime + (warningInterval*1000L);
+                  passwordExpirationTime = warnedTime + (warningInterval*1000L);
                 }
               }
             }
@@ -2216,8 +2557,8 @@
               }
               else if (warnedTime > 0)
               {
-                expirationTime = warnedTime + (warningInterval*1000L);
-                if (expirationTime > currentTime)
+                passwordExpirationTime = warnedTime + (warningInterval*1000L);
+                if (passwordExpirationTime > currentTime)
                 {
                   shouldWarn        = ConditionResult.TRUE;
                   isFirstWarning    = ConditionResult.FALSE;
@@ -2232,10 +2573,10 @@
               }
               else
               {
-                shouldWarn        = ConditionResult.TRUE;
-                isFirstWarning    = ConditionResult.TRUE;
-                isPasswordExpired = ConditionResult.FALSE;
-                expirationTime    = currentTime + (warningInterval*1000L);
+                shouldWarn             = ConditionResult.TRUE;
+                isFirstWarning         = ConditionResult.TRUE;
+                isPasswordExpired      = ConditionResult.FALSE;
+                passwordExpirationTime = currentTime + (warningInterval*1000L);
               }
             }
           }
@@ -2247,7 +2588,7 @@
           shouldWarn     = ConditionResult.FALSE;
           isFirstWarning = ConditionResult.FALSE;
 
-          if (currentTime > expirationTime)
+          if (currentTime > passwordExpirationTime)
           {
             isPasswordExpired = ConditionResult.TRUE;
           }
@@ -2263,7 +2604,7 @@
         shouldWarn       = ConditionResult.FALSE;
         isFirstWarning   = ConditionResult.FALSE;
 
-        if (expirationTime < currentTime)
+        if (passwordExpirationTime < currentTime)
         {
           isPasswordExpired = ConditionResult.TRUE;
         }
@@ -2279,11 +2620,11 @@
       if (debugEnabled())
       {
         TRACER.debugInfo("Returning password expiration time of %d for user " +
-            "%s.", expirationTime, userDNString);
+            "%s.", passwordExpirationTime, userDNString);
       }
     }
 
-    return expirationTime;
+    return passwordExpirationTime;
   }
 
 
@@ -2538,6 +2879,24 @@
    */
   public void setRequiredChangeTime()
   {
+    long requiredChangeByTimePolicy = passwordPolicy.getRequireChangeByTime();
+    if (requiredChangeByTimePolicy > 0)
+    {
+      setRequiredChangeTime(requiredChangeByTimePolicy);
+    }
+  }
+
+
+
+  /**
+   * Updates the user entry with a timestamp indicating that the password has
+   * been changed in accordance with the require change time.
+   *
+   * @param  requiredChangeTime  The timestamp to use for the required change
+   *                             time value.
+   */
+  public void setRequiredChangeTime(long requiredChangeTime)
+  {
     if (debug)
     {
       if (debugEnabled())
@@ -2547,16 +2906,14 @@
       }
     }
 
-    long requiredChangeByTimePolicy = passwordPolicy.getRequireChangeByTime();
-    if (getRequiredChangeTime() != requiredChangeByTimePolicy)
+    if (getRequiredChangeTime() != requiredChangeTime)
     {
       AttributeType type = DirectoryServer.getAttributeType(
                                OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true);
 
       LinkedHashSet<AttributeValue> values =
            new LinkedHashSet<AttributeValue>(1);
-      String timeValue =
-           GeneralizedTimeSyntax.format(requiredChangeByTimePolicy);
+      String timeValue = GeneralizedTimeSyntax.format(requiredChangeTime);
       values.add(new AttributeValue(type, timeValue));
 
       Attribute a = new Attribute(type,
@@ -2579,6 +2936,36 @@
 
 
   /**
+   * Updates the user entry to remove any timestamp indicating that the password
+   * has been changed in accordance with the required change time.
+   */
+  public void clearRequiredChangeTime()
+  {
+    if (debug)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Clearing required change time for user %s",
+                         userDNString);
+      }
+    }
+
+    AttributeType type = DirectoryServer.getAttributeType(
+                             OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true);
+    if (updateEntry)
+    {
+      userEntry.removeAttribute(type);
+    }
+    else
+    {
+      modifications.add(new Modification(ModificationType.REPLACE,
+                                         new Attribute(type), true));
+    }
+  }
+
+
+
+  /**
    * Retrieves the time that the user was first warned about an upcoming
    * expiration.
    *
@@ -2632,22 +3019,37 @@
    */
   public void setWarnedTime()
   {
+    setWarnedTime(currentTime);
+  }
+
+
+
+  /**
+   * Updates the user entry to set the warned time to the specified time.  This
+   * method should generally only be used for testing purposes, since the
+   * variant that uses the current time is preferred almost everywhere else.
+   *
+   * @param  warnedTime  The value to use for the warned time.
+   */
+  public void setWarnedTime(long warnedTime)
+  {
     long warnTime = getWarnedTime();
-    if (warnTime == currentTime)
+    if (warnTime == warnedTime)
     {
       if (debug)
       {
         if (debugEnabled())
         {
           TRACER.debugInfo("Not updating warned time for user %s because " +
-              "the warned time is the same as the current time.", userDNString);
+              "the warned time is the same as the specified time.",
+              userDNString);
         }
       }
 
       return;
     }
 
-    warnedTime = currentTime;
+    this.warnedTime = warnedTime;
 
     AttributeType type =
          DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true);
@@ -2879,6 +3281,58 @@
 
 
   /**
+   * Specifies the set of grace login use times for the associated user.  If
+   * the provided list is empty or {@code null}, then the set will be cleared.
+   *
+   * @param  graceLoginTimes  The grace login use times for the associated user.
+   */
+  public void setGraceLoginTimes(List<Long> graceLoginTimes)
+  {
+    if ((graceLoginTimes == null) || graceLoginTimes.isEmpty())
+    {
+      clearGraceLoginTimes();
+      return;
+    }
+
+
+    if (debug)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugInfo("Updating grace login times for user %s",
+                         userDNString);
+      }
+    }
+
+
+    AttributeType type =
+         DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC,
+                                          true);
+    LinkedHashSet<AttributeValue> values =
+         new LinkedHashSet<AttributeValue>(graceLoginTimes.size());
+    for (Long l : graceLoginTimes)
+    {
+      values.add(new AttributeValue(type, GeneralizedTimeSyntax.format(l)));
+    }
+    Attribute a =
+         new Attribute(type, OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME, values);
+
+    if (updateEntry)
+    {
+      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+      attrList.add(a);
+
+      userEntry.putAttribute(type, attrList);
+    }
+    else
+    {
+      modifications.add(new Modification(ModificationType.REPLACE, a, true));
+    }
+  }
+
+
+
+  /**
    * Updates the user entry to remove any record of previous grace logins.
    */
   public void clearGraceLoginTimes()

--
Gitblit v1.10.0