From e4976b4d76f6cacc919d7ee986a8ac8fe58c5bd0 Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Sat, 17 Feb 2007 15:45:52 +0000
Subject: [PATCH] [issue 1215] Prevent operations referencing an inconsistent Password Policy https://opends.dev.java.net/issues/show_bug.cgi?id=1215

---
 opends/src/server/org/opends/server/core/DirectoryServer.java                                |  108 ++-
 opends/src/server/org/opends/server/core/PasswordPolicyConfig.java                           |  522 ++++++++++++++++++++
 opends/src/server/org/opends/server/plugins/PasswordPolicyImportPlugin.java                  |    4 
 opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java                    |   44 -
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java |   70 ++
 opends/src/server/org/opends/server/core/PasswordPolicy.java                                 |  764 ++++------------------------
 6 files changed, 789 insertions(+), 723 deletions(-)

diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 1791dba..557ddd4 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -153,6 +153,7 @@
 import org.opends.server.types.WritabilityMode;
 import org.opends.server.util.MultiOutputStream;
 import org.opends.server.util.TimeThread;
+import org.opends.server.util.Validator;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.args.ArgumentParser;
 import org.opends.server.util.args.BooleanArgument;
@@ -296,7 +297,7 @@
   // The set of password policies registered with the Directory Server, as a
   // mapping between the DN of the associated configuration entry and the policy
   // implementation.
-  private ConcurrentHashMap<DN,PasswordPolicy> passwordPolicies;
+  private ConcurrentHashMap<DN,PasswordPolicyConfig> passwordPolicies;
 
   // The set of password validators registered with the Directory Server, as a
   // mapping between the DN of the associated configuration entry and the
@@ -454,7 +455,7 @@
   private PasswordGeneratorConfigManager passwordGeneratorConfigManager;
 
   // The default password policy for the Directory Server.
-  private PasswordPolicy defaultPasswordPolicy;
+  private PasswordPolicyConfig defaultPasswordPolicyConfig;
 
   // The configuration handler used to manage the password policies.
   private PasswordPolicyConfigManager passwordPolicyConfigManager;
@@ -625,9 +626,9 @@
     directoryServer.rootDNs = new CopyOnWriteArraySet<DN>();
     directoryServer.alternateRootBindDNs = new ConcurrentHashMap<DN,DN>();
     directoryServer.passwordPolicies =
-         new ConcurrentHashMap<DN,PasswordPolicy>();
+         new ConcurrentHashMap<DN,PasswordPolicyConfig>();
     directoryServer.defaultPasswordPolicyDN = null;
-    directoryServer.defaultPasswordPolicy = null;
+    directoryServer.defaultPasswordPolicyConfig = null;
     directoryServer.monitorProviders =
          new ConcurrentHashMap<String,MonitorProvider>();
     directoryServer.backends = new TreeMap<String,Backend>();
@@ -4640,17 +4641,28 @@
 
 
   /**
-   * Retrieves the set of password policies defined in the Directory Server as a
-   * mapping between the DN of the associated configuration entry and the
-   * corresponding policy.
+   * Retrieves the set of password policies registered with the Directory
+   * Server. The references returned are to the actual password policy objects
+   * currently in use by the directory server and the referenced objects must
+   * not be modified.
    *
-   * @return  The set of password policies defined in the Directory Server.
+   * @return  The set of password policies registered with the Directory Server.
    */
-  public static ConcurrentHashMap<DN,PasswordPolicy> getPasswordPolicies()
+  public static PasswordPolicy[] getPasswordPolicies()
   {
     assert debugEnter(CLASS_NAME, "getPasswordPolicies");
 
-    return directoryServer.passwordPolicies;
+    // The password policy objects are returned in an array to prevent the
+    // caller from modifying the map structure.
+    PasswordPolicyConfig[] values = directoryServer.passwordPolicies.values()
+                                          .toArray(new PasswordPolicyConfig[0]);
+    PasswordPolicy[] policies = new PasswordPolicy[values.length];
+    for( int i = 0 ; i < values.length; ++i)
+    {
+      policies[i] = values[i].getPolicy();
+    }
+
+    return policies;
   }
 
 
@@ -4669,8 +4681,11 @@
   {
     assert debugEnter(CLASS_NAME, "getPasswordPolicy",
                       String.valueOf(configEntryDN));
+    Validator.ensureNotNull(configEntryDN);
 
-    return directoryServer.passwordPolicies.get(configEntryDN);
+    PasswordPolicyConfig config
+            = directoryServer.passwordPolicies.get(configEntryDN);
+    return (null == config) ? null : config.getPolicy();
   }
 
 
@@ -4689,8 +4704,11 @@
   {
     assert debugEnter(CLASS_NAME, "registerPasswordPolicy",
                       String.valueOf(configEntryDN), String.valueOf(policy));
+    Validator.ensureNotNull(configEntryDN, policy);
 
-    directoryServer.passwordPolicies.put(configEntryDN, policy);
+    PasswordPolicyConfig config = new PasswordPolicyConfig(policy);
+
+    directoryServer.passwordPolicies.put(configEntryDN, config);
   }
 
 
@@ -4706,8 +4724,16 @@
   {
     assert debugEnter(CLASS_NAME, "deregisterPasswordPolicy",
                       String.valueOf(configEntryDN));
+    Validator.ensureNotNull(configEntryDN);
 
-    directoryServer.passwordPolicies.remove(configEntryDN);
+    if (directoryServer.defaultPasswordPolicyDN.equals(configEntryDN))
+    {
+      directoryServer.defaultPasswordPolicyConfig = null;
+    }
+
+    PasswordPolicyConfig config
+            = directoryServer.passwordPolicies.remove(configEntryDN);
+    if (null != config) config.finalizePasswordPolicyConfig();
   }
 
 
@@ -4730,7 +4756,10 @@
 
   /**
    * Specifies the DN of the configuration entry for the default password policy
-   * for the Directory Server.
+   * for the Directory Server. This routine does not check the registered
+   * password policies for the specified DN, since in the case of server
+   * initialization, the password policy entries will not yet have been loaded
+   * from the configuration backend.
    *
    * @param  defaultPasswordPolicyDN  The DN of the configuration entry for the
    *                                  default password policy for the Directory
@@ -4742,50 +4771,38 @@
                       String.valueOf(defaultPasswordPolicyDN));
 
     directoryServer.defaultPasswordPolicyDN = defaultPasswordPolicyDN;
-    directoryServer.defaultPasswordPolicy   = null;
+    directoryServer.defaultPasswordPolicyConfig = null;
   }
 
 
 
   /**
-   * Retrieves the default password policy for the Directory Server.
+   * Retrieves the default password policy for the Directory Server. This method
+   * is equivalent to invoking <CODE>getPasswordPolicy</CODE> on the DN returned
+   * from <CODE>DirectoryServer.getDefaultPasswordPolicyDN()</CODE>.
    *
    * @return  The default password policy for the Directory Server.
    */
   public static PasswordPolicy getDefaultPasswordPolicy()
   {
     assert debugEnter(CLASS_NAME, "getDefaultPasswordPolicy");
+    assert null != directoryServer.passwordPolicies.get(
+                                       directoryServer.defaultPasswordPolicyDN)
+            : "Internal Error: no default password policy defined." ;
 
-    if ((directoryServer.defaultPasswordPolicy == null) &&
+    if ((directoryServer.defaultPasswordPolicyConfig == null) &&
         (directoryServer.defaultPasswordPolicyDN != null))
     {
-      directoryServer.defaultPasswordPolicy =
+      directoryServer.defaultPasswordPolicyConfig =
            directoryServer.passwordPolicies.get(
-                directoryServer.defaultPasswordPolicyDN);
+                                       directoryServer.defaultPasswordPolicyDN);
     }
-
-    return directoryServer.defaultPasswordPolicy;
-  }
-
-
-
-  /**
-   * Specifies the default password policy for the Directory Server.  It will
-   * still be necessary to register this policy with the set of defined password
-   * policies.
-   *
-   * @param  defaultPasswordPolicy  The default password policy for the
-   *                                Directory Server.
-   */
-  public static void setDefaultPasswordPolicy(PasswordPolicy
-                                                   defaultPasswordPolicy)
-  {
-    assert debugEnter(CLASS_NAME, "setDefaultPasswordPolicy",
-                      String.valueOf(defaultPasswordPolicy));
-
-    directoryServer.defaultPasswordPolicy = defaultPasswordPolicy;
-    directoryServer.defaultPasswordPolicyDN =
-         defaultPasswordPolicy.getConfigurableComponentEntryDN();
+    assert directoryServer.passwordPolicies.get(
+                                       directoryServer.defaultPasswordPolicyDN)
+                == directoryServer.defaultPasswordPolicyConfig
+           : "Internal Error: inconsistency between defaultPasswordPolicyConfig"
+             + " cache and value in passwordPolicies map.";
+    return directoryServer.defaultPasswordPolicyConfig.getPolicy();
   }
 
 
@@ -7289,6 +7306,13 @@
     }
 
 
+    // Finalize the password policy map.
+    for (DN configEntryDN : directoryServer.passwordPolicies.keySet())
+    {
+      DirectoryServer.deregisterPasswordPolicy(configEntryDN);
+    }
+
+
     // Perform any necessary cleanup work for the group manager.
     directoryServer.groupManager.finalizeGroupManager();
 
diff --git a/opends/src/server/org/opends/server/core/PasswordPolicy.java b/opends/src/server/org/opends/server/core/PasswordPolicy.java
index 5464268..12fc83e 100644
--- a/opends/src/server/org/opends/server/core/PasswordPolicy.java
+++ b/opends/src/server/org/opends/server/core/PasswordPolicy.java
@@ -29,7 +29,6 @@
 
 
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -41,12 +40,10 @@
 import java.util.concurrent.CopyOnWriteArraySet;
 
 import org.opends.server.api.AccountStatusNotificationHandler;
-import org.opends.server.api.ConfigurableComponent;
 import org.opends.server.api.PasswordGenerator;
 import org.opends.server.api.PasswordStorageScheme;
 import org.opends.server.api.PasswordValidator;
 import org.opends.server.config.BooleanConfigAttribute;
-import org.opends.server.config.ConfigAttribute;
 import org.opends.server.config.ConfigEntry;
 import org.opends.server.config.ConfigException;
 import org.opends.server.config.DNConfigAttribute;
@@ -57,10 +54,8 @@
 import org.opends.server.schema.GeneralizedTimeSyntax;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.ByteString;
-import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DN;
 import org.opends.server.types.InitializationException;
-import org.opends.server.types.ResultCode;
 
 import static org.opends.server.config.ConfigConstants.*;
 import static org.opends.server.loggers.Debug.*;
@@ -77,7 +72,6 @@
  * Server password policy.
  */
 public class PasswordPolicy
-       implements ConfigurableComponent
 {
   /**
    * The fully-qualified name of this class for debugging purposes.
@@ -213,6 +207,7 @@
     passwordAttribute                = null;
     authPasswordSyntax               = false;
     lastLoginTimeAttribute           = null;
+    previousLastLoginTimeFormats     = new CopyOnWriteArrayList<String>();
     allowExpiredPasswordChanges      = DEFAULT_PWPOLICY_ALLOW_EXPIRED_CHANGES;
     allowMultiplePasswordValues      =
          DEFAULT_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES;
@@ -260,8 +255,6 @@
     deprecatedStorageSchemes = new CopyOnWriteArraySet<String>();
 
     passwordValidators = new ConcurrentHashMap<DN,PasswordValidator>();
-
-    previousLastLoginTimeFormats = new CopyOnWriteArrayList<String>();
   }
 
 
@@ -287,65 +280,7 @@
   public PasswordPolicy(ConfigEntry configEntry)
          throws ConfigException, InitializationException
   {
-    this();
-
-    assert debugConstructor(CLASS_NAME, String.valueOf(configEntry));
-
-
-    this.configEntryDN = configEntry.getDN();
-    initializePasswordPolicyConfig(configEntry, this);
-
-
-    // Ensure that the password attribute was included in the configuration
-    // entry, since it is required.
-    if (passwordAttribute == null)
-    {
-      int    msgID   = MSGID_PWPOLICY_NO_PASSWORD_ATTRIBUTE;
-      String message = getMessage(msgID, String.valueOf(configEntryDN));
-      throw new ConfigException(msgID, message);
-    }
-
-
-    // Ensure that at least one default password storage scheme was included in
-    // the configuration entry, since it is required.
-    if (defaultStorageSchemes.isEmpty())
-    {
-      int    msgID   = MSGID_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES;
-      String message = getMessage(msgID, String.valueOf(configEntryDN));
-      throw new ConfigException(msgID, message);
-    }
-
-
-    DirectoryServer.registerConfigurableComponent(this);
-  }
-
-
-
-  /**
-   * Initializes the provided password policy with the information contained in
-   * the given configuration entry.
-   *
-   * @param  configEntry  The configuration entry to use to obtain the settings
-   *                      for this password policy.
-   * @param  policy       The password policy to be initialized.
-   *
-   * @throws  ConfigException  If the provided entry does not contain a valid
-   *                           password policy configuration.
-   *
-   * @throws  InitializationException  If an error occurs while initializing the
-   *                                   password policy that is not related to
-   *                                   the server configuration.
-   */
-  private static void initializePasswordPolicyConfig(ConfigEntry configEntry,
-                                                     PasswordPolicy policy)
-         throws ConfigException, InitializationException
-  {
-    assert debugEnter(CLASS_NAME, "initializePasswordPolicyConfig",
-                      String.valueOf(configEntry));
-
-
-    DN configEntryDN = configEntry.getDN();
-
+    this(); // Initialize fields to default values.
 
     // Create a list of units and values that we can use to represent time
     // periods.
@@ -362,6 +297,8 @@
     timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
 
 
+    this.configEntryDN = configEntry.getDN();
+
     // Get the password attribute.  If specified, it must have either the
     // user password or auth password syntax.
     int msgID = MSGID_PWPOLICY_DESCRIPTION_PW_ATTR;
@@ -372,16 +309,9 @@
     {
       StringConfigAttribute pwAttrAttr =
            (StringConfigAttribute) configEntry.getConfigAttribute(pwAttrStub);
-      if (pwAttrAttr == null)
-      {
-        msgID = MSGID_PWPOLICY_NO_PASSWORD_ATTRIBUTE;
-        String message = getMessage(msgID, String.valueOf(configEntryDN));
-        throw new ConfigException(msgID, message);
-      }
-      else
+      if (pwAttrAttr != null)
       {
         String lowerName = toLowerCase(pwAttrAttr.pendingValue());
-
         AttributeType pwAttrType = DirectoryServer.getAttributeType(lowerName);
         if (pwAttrType == null)
         {
@@ -394,13 +324,13 @@
         String syntaxOID = pwAttrType.getSyntaxOID();
         if (syntaxOID.equals(SYNTAX_AUTH_PASSWORD_OID))
         {
-          policy.passwordAttribute  = pwAttrType;
-          policy.authPasswordSyntax = true;
+          this.passwordAttribute  = pwAttrType;
+          this.authPasswordSyntax = true;
         }
         else if (syntaxOID.equals(SYNTAX_USER_PASSWORD_OID))
         {
-          policy.passwordAttribute  = pwAttrType;
-          policy.authPasswordSyntax = false;
+          this.passwordAttribute  = pwAttrType;
+          this.authPasswordSyntax = false;
         }
         else
         {
@@ -413,7 +343,7 @@
           msgID = MSGID_PWPOLICY_INVALID_PASSWORD_ATTRIBUTE_SYNTAX;
           String message = getMessage(msgID, String.valueOf(configEntryDN),
                                       String.valueOf(pwAttrAttr.pendingValue()),
-                                      String.valueOf(syntaxOID));
+                                      String.valueOf(syntax));
           throw new ConfigException(msgID, message);
         }
       }
@@ -424,7 +354,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_PASSWORD_ATTRIBUTE;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -457,7 +387,7 @@
         for (String schemeName : defaultSchemeAttr.pendingValues())
         {
           PasswordStorageScheme scheme;
-          if (policy.authPasswordSyntax)
+          if (this.authPasswordSyntax)
           {
             scheme = DirectoryServer.getAuthPasswordStorageScheme(schemeName);
           }
@@ -480,7 +410,7 @@
           }
         }
 
-        policy.defaultStorageSchemes =
+        this.defaultStorageSchemes =
              new CopyOnWriteArrayList<PasswordStorageScheme>(schemes);
       }
     }
@@ -490,7 +420,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_DEFAULT_STORAGE_SCHEMES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -511,14 +441,14 @@
            configEntry.getConfigAttribute(deprecatedSchemeStub);
       if (deprecatedSchemeAttr != null)
       {
-        policy.deprecatedStorageSchemes =
+        this.deprecatedStorageSchemes =
              new CopyOnWriteArraySet<String>(
                       deprecatedSchemeAttr.pendingValues());
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_DEPRECATED_STORAGE_SCHEMES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -555,7 +485,7 @@
           validators.put(validatorDN, validator);
         }
 
-        policy.passwordValidators = validators;
+        this.passwordValidators = validators;
       }
     }
     catch (ConfigException ce)
@@ -564,7 +494,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_PASSWORD_VALIDATORS;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -601,7 +531,7 @@
           handlers.put(handlerDN, handler);
         }
 
-        policy.notificationHandlers = handlers;
+        this.notificationHandlers = handlers;
       }
     }
     catch (ConfigException ce)
@@ -610,7 +540,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_NOTIFICATION_HANDLERS;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -631,12 +561,12 @@
            configEntry.getConfigAttribute(userChangeStub);
       if (userChangeAttr != null)
       {
-        policy.allowUserPasswordChanges = userChangeAttr.pendingValue();
+        this.allowUserPasswordChanges = userChangeAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_USER_PW_CHANGES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -657,12 +587,12 @@
            configEntry.getConfigAttribute(requirePWStub);
       if (requirePWAttr != null)
       {
-        policy.requireCurrentPassword = requirePWAttr.pendingValue();
+        this.requireCurrentPassword = requirePWAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_CURRENT_PW;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -683,12 +613,12 @@
            configEntry.getConfigAttribute(forceChangeOnAddStub);
       if (forceChangeOnAddAttr != null)
       {
-        policy.forceChangeOnAdd = forceChangeOnAddAttr.pendingValue();
+        this.forceChangeOnAdd = forceChangeOnAddAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FORCE_CHANGE_ON_ADD;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -709,12 +639,12 @@
            configEntry.getConfigAttribute(forceChangeOnResetStub);
       if (forceChangeAttr != null)
       {
-        policy.forceChangeOnReset = forceChangeAttr.pendingValue();
+        this.forceChangeOnReset = forceChangeAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FORCE_CHANGE_ON_RESET;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -735,13 +665,13 @@
            configEntry.getConfigAttribute(validateResetStub);
       if (validateResetAttr != null)
       {
-        policy.skipValidationForAdministrators =
+        this.skipValidationForAdministrators =
              validateResetAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_SKIP_ADMIN_VALIDATION;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -771,8 +701,8 @@
           throw new ConfigException(msgID, message);
         }
 
-        policy.passwordGeneratorDN = generatorAttr.pendingValue();
-        policy.passwordGenerator   = generator;
+        this.passwordGeneratorDN = generatorAttr.pendingValue();
+        this.passwordGenerator   = generator;
       }
     }
     catch (ConfigException ce)
@@ -781,7 +711,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_PASSWORD_GENERATOR;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -802,12 +732,12 @@
            configEntry.getConfigAttribute(secureAuthStub);
       if (secureAuthAttr != null)
       {
-        policy.requireSecureAuthentication = secureAuthAttr.pendingValue();
+        this.requireSecureAuthentication = secureAuthAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_SECURE_AUTH;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -829,12 +759,12 @@
            configEntry.getConfigAttribute(secureChangeStub);
       if (secureChangeAttr != null)
       {
-        policy.requireSecurePasswordChanges = secureChangeAttr.pendingValue();
+        this.requireSecurePasswordChanges = secureChangeAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_SECURE_CHANGES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -855,12 +785,12 @@
            configEntry.getConfigAttribute(allowMultiplePWStub);
       if (allowMultiplePWAttr != null)
       {
-        policy.allowMultiplePasswordValues = allowMultiplePWAttr.pendingValue();
+        this.allowMultiplePasswordValues = allowMultiplePWAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_MULTIPLE_PW_VALUES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -881,12 +811,12 @@
            configEntry.getConfigAttribute(preEncodedStub);
       if (preEncodedAttr != null)
       {
-        policy.allowPreEncodedPasswords = preEncodedAttr.pendingValue();
+        this.allowPreEncodedPasswords = preEncodedAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_PREENCODED;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -908,12 +838,12 @@
            configEntry.getConfigAttribute(minAgeStub);
       if (minAgeAttr != null)
       {
-        policy.minimumPasswordAge = (int) minAgeAttr.pendingCalculatedValue();
+        this.minimumPasswordAge = (int) minAgeAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MIN_AGE;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -935,12 +865,12 @@
            configEntry.getConfigAttribute(maxAgeStub);
       if (maxAgeAttr != null)
       {
-        policy.maximumPasswordAge = (int) maxAgeAttr.pendingCalculatedValue();
+        this.maximumPasswordAge = (int) maxAgeAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MAX_AGE;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -962,13 +892,13 @@
            configEntry.getConfigAttribute(maxResetStub);
       if (maxResetAttr != null)
       {
-        policy.maximumPasswordResetAge =
+        this.maximumPasswordResetAge =
              (int) maxResetAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MAX_RESET_AGE;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -990,12 +920,12 @@
            configEntry.getConfigAttribute(warningStub);
       if (warningAttr != null)
       {
-        policy.warningInterval = (int) warningAttr.pendingCalculatedValue();
+        this.warningInterval = (int) warningAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_WARNING_INTERVAL;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1016,13 +946,13 @@
            configEntry.getConfigAttribute(expireWithoutWarningStub);
       if (expireWithoutWarningAttr != null)
       {
-        policy.expirePasswordsWithoutWarning =
+        this.expirePasswordsWithoutWarning =
              expireWithoutWarningAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_EXPIRE_WITHOUT_WARNING;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1033,8 +963,8 @@
 
     // If the expire without warning option is disabled, then there must be a
     // warning interval.
-    if ((! policy.expirePasswordsWithoutWarning()) &&
-        (policy.getWarningInterval() <= 0))
+    if ((! this.expirePasswordsWithoutWarning()) &&
+        (this.getWarningInterval() <= 0))
     {
       msgID = MSGID_PWPOLICY_MUST_HAVE_WARNING_IF_NOT_EXPIRE_WITHOUT_WARNING;
       String message = getMessage(msgID, String.valueOf(configEntryDN));
@@ -1054,13 +984,13 @@
            configEntry.getConfigAttribute(allowExpiredChangesStub);
       if (allowExpiredChangesAttr != null)
       {
-        policy.allowExpiredPasswordChanges =
+        this.allowExpiredPasswordChanges =
              allowExpiredChangesAttr.pendingValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_EXPIRED_CHANGES;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1081,12 +1011,12 @@
            (IntegerConfigAttribute) configEntry.getConfigAttribute(graceStub);
       if (graceAttr != null)
       {
-        policy.graceLoginCount = graceAttr.pendingIntValue();
+        this.graceLoginCount = graceAttr.pendingIntValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_GRACE_LOGIN_COUNT;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1108,12 +1038,12 @@
            configEntry.getConfigAttribute(failureCountStub);
       if (failureCountAttr != null)
       {
-        policy.lockoutFailureCount = failureCountAttr.pendingIntValue();
+        this.lockoutFailureCount = failureCountAttr.pendingIntValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LOCKOUT_FAILURE_COUNT;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1135,13 +1065,13 @@
            configEntry.getConfigAttribute(lockoutDurationStub);
       if (lockoutDurationAttr != null)
       {
-        policy.lockoutDuration =
+        this.lockoutDuration =
              (int) lockoutDurationAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LOCKOUT_DURATION;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1164,13 +1094,13 @@
            configEntry.getConfigAttribute(failureExpirationStub);
       if (failureExpirationAttr != null)
       {
-        policy.lockoutFailureExpirationInterval =
+        this.lockoutFailureExpirationInterval =
              (int) failureExpirationAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FAILURE_EXPIRATION;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1201,21 +1131,21 @@
 
         if (syntax == null)
         {
-          policy.requireChangeByTime =
+          this.requireChangeByTime =
                GeneralizedTimeSyntax.decodeGeneralizedTimeValue(valueString);
         }
         else
         {
           valueString =
                syntax.getEqualityMatchingRule().normalizeValue(valueString);
-          policy.requireChangeByTime =
+          this.requireChangeByTime =
                GeneralizedTimeSyntax.decodeGeneralizedTimeValue(valueString);
         }
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_CHANGE_BY_TIME;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1250,7 +1180,7 @@
           throw new ConfigException(msgID, message);
         }
 
-        policy.lastLoginTimeAttribute = attrType;
+        this.lastLoginTimeAttribute = attrType;
       }
     }
     catch (ConfigException ce)
@@ -1259,7 +1189,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LAST_LOGIN_TIME_ATTR;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1285,12 +1215,11 @@
 
         try
         {
-          SimpleDateFormat format = new SimpleDateFormat(formatString);
-          policy.lastLoginTimeFormat = formatString;
+          new SimpleDateFormat(formatString);
         }
         catch (Exception e)
         {
-          assert debugException(CLASS_NAME, "initializePasswordPolicyConfig",
+          assert debugException(CLASS_NAME, "PasswordPolicy",
                                 e);
 
           msgID = MSGID_PWPOLICY_INVALID_LAST_LOGIN_TIME_FORMAT;
@@ -1298,6 +1227,8 @@
                                       String.valueOf(formatString));
           throw new ConfigException(msgID, message);
         }
+
+        this.lastLoginTimeFormat = formatString;
       }
     }
     catch (ConfigException ce)
@@ -1306,7 +1237,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LAST_LOGIN_TIME_FORMAT;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1334,12 +1265,11 @@
         {
           try
           {
-            SimpleDateFormat format = new SimpleDateFormat(s);
+            new SimpleDateFormat(s);
           }
           catch (Exception e)
           {
-            assert debugException(CLASS_NAME, "initializePasswordPolicyConfig",
-                                  e);
+            assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
             msgID = MSGID_PWPOLICY_INVALID_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
             String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1348,7 +1278,7 @@
           }
         }
 
-        policy.previousLastLoginTimeFormats =
+        this.previousLastLoginTimeFormats =
              new CopyOnWriteArrayList<String>(formatStrings);
       }
     }
@@ -1358,7 +1288,7 @@
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
@@ -1380,19 +1310,55 @@
            configEntry.getConfigAttribute(idleIntervalStub);
       if (idleIntervalAttr != null)
       {
-        policy.idleLockoutInterval =
+        this.idleLockoutInterval =
              (int) idleIntervalAttr.pendingCalculatedValue();
       }
     }
     catch (Exception e)
     {
-      assert debugException(CLASS_NAME, "initializePasswordPolicyConfig", e);
+      assert debugException(CLASS_NAME, "PasswordPolicy", e);
 
       msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_IDLE_LOCKOUT_INTERVAL;
       String message = getMessage(msgID, String.valueOf(configEntryDN),
                                   stackTraceToSingleLineString(e));
       throw new InitializationException(msgID, message, e);
     }
+
+
+    /*
+     *  Holistic validation.
+     */
+
+    // Ensure that the password attribute was included in the configuration
+    // entry, since it is required.
+    if (passwordAttribute == null)
+    {
+      msgID = MSGID_PWPOLICY_NO_PASSWORD_ATTRIBUTE;
+      String message = getMessage(msgID, String.valueOf(configEntryDN));
+      throw new ConfigException(msgID, message);
+    }
+
+    // Ensure that at least one default password storage scheme was included in
+    // the configuration entry, since it is required.
+    if (defaultStorageSchemes.isEmpty())
+    {
+      msgID = MSGID_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES;
+      String message = getMessage(msgID, String.valueOf(configEntryDN));
+      throw new ConfigException(msgID, message);
+    }
+  }
+
+
+
+  /**
+   * Retrieves the DN of the configuration entry to which this password policy
+   * corresponds.
+   *
+   * @return  The DN of the configuration entry.
+   */
+  public DN getConfigEntryDN()
+  {
+    return configEntryDN;
   }
 
 
@@ -2074,506 +2040,6 @@
 
 
   /**
-   * Retrieves the DN of the configuration entry with which this component is
-   * associated.
-   *
-   * @return  The DN of the configuration entry with which this component is
-   *          associated.
-   */
-  public DN getConfigurableComponentEntryDN()
-  {
-    assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
-
-    return configEntryDN;
-  }
-
-
-
-  /**
-   * Retrieves the set of configuration attributes that are associated with this
-   * configurable component.
-   *
-   * @return  The set of configuration attributes that are associated with this
-   *          configurable component.
-   */
-  public List<ConfigAttribute> getConfigurationAttributes()
-  {
-    assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
-
-
-    // Create a list of units and values that we can use to represent time
-    // periods.
-    LinkedHashMap<String,Double> timeUnits = new LinkedHashMap<String,Double>();
-    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
-    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
-    timeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
-    timeUnits.put(TIME_UNIT_MINUTES_FULL, 60D);
-    timeUnits.put(TIME_UNIT_HOURS_ABBR, (double) (60 * 60));
-    timeUnits.put(TIME_UNIT_HOURS_FULL, (double) (60 * 60));
-    timeUnits.put(TIME_UNIT_DAYS_ABBR, (double) (60 * 60 * 24));
-    timeUnits.put(TIME_UNIT_DAYS_FULL, (double) (60 * 60 * 24));
-    timeUnits.put(TIME_UNIT_WEEKS_ABBR, (double) (60 * 60 * 24 * 7));
-    timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
-
-
-    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
-
-
-    int msgID = MSGID_PWPOLICY_DESCRIPTION_PW_ATTR;
-    String pwAttr;
-    if (passwordAttribute == null)
-    {
-      pwAttr = null;
-    }
-    else
-    {
-      pwAttr = passwordAttribute.getNameOrOID();
-    }
-    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_PASSWORD_ATTRIBUTE,
-                                           getMessage(msgID), false, false,
-                                           false, pwAttr));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_DEFAULT_STORAGE_SCHEMES;
-    ArrayList<String> schemes = new ArrayList<String>();
-    if (defaultStorageSchemes != null)
-    {
-      for (PasswordStorageScheme s : defaultStorageSchemes)
-      {
-        schemes.add(s.getStorageSchemeName());
-      }
-    }
-    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEFAULT_SCHEME,
-                                           getMessage(msgID), false, true,
-                                           false, schemes));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_DEPRECATED_STORAGE_SCHEMES;
-    schemes = new ArrayList<String>();
-    if (deprecatedStorageSchemes != null)
-    {
-      schemes.addAll(deprecatedStorageSchemes);
-    }
-    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEPRECATED_SCHEME,
-                                           getMessage(msgID), false, true,
-                                           false, schemes));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_VALIDATORS;
-    ArrayList<DN> validatorDNs = new ArrayList<DN>();
-    if (passwordValidators != null)
-    {
-      validatorDNs.addAll(passwordValidators.keySet());
-    }
-    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_VALIDATOR,
-                                       getMessage(msgID), false, true, false,
-                                       validatorDNs));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_NOTIFICATION_HANDLERS;
-    ArrayList<DN> handlerDNs = new ArrayList<DN>();
-    if (notificationHandlers != null)
-    {
-      handlerDNs.addAll(notificationHandlers.keySet());
-    }
-    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_NOTIFICATION_HANDLER,
-                                       getMessage(msgID), false, true, false,
-                                       handlerDNs));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_USER_PW_CHANGES;
-    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_USER_CHANGE,
-                                            getMessage(msgID), false,
-                                            allowUserPasswordChanges));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CURRENT_PW;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_REQUIRE_CURRENT_PASSWORD,
-                          getMessage(msgID), false, requireCurrentPassword));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_ADD;
-    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_ADD,
-                                            getMessage(msgID), false,
-                                            forceChangeOnAdd));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_RESET;
-    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_RESET,
-                                            getMessage(msgID), false,
-                                            forceChangeOnReset));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_SKIP_ADMIN_VALIDATION;
-    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_SKIP_ADMIN_VALIDATION,
-                                            getMessage(msgID), false,
-                                            skipValidationForAdministrators));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_GENERATOR;
-    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_GENERATOR,
-                                       getMessage(msgID), false, false, false,
-                                       passwordGeneratorDN));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_AUTH;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_REQUIRE_SECURE_AUTHENTICATION,
-                          getMessage(msgID), false,
-                          requireSecureAuthentication));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_CHANGES;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_REQUIRE_SECURE_PASSWORD_CHANGES,
-                          getMessage(msgID), false,
-                          requireSecurePasswordChanges));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_MULTIPLE_PW_VALUES;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES,
-                          getMessage(msgID), false,
-                          allowMultiplePasswordValues));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_PREENCODED;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_ALLOW_PRE_ENCODED_PASSWORDS,
-                          getMessage(msgID), false, allowPreEncodedPasswords));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_MIN_AGE;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_MINIMUM_PASSWORD_AGE,
-                          getMessage(msgID), false, timeUnits, true, 0, true,
-                          Integer.MAX_VALUE, minimumPasswordAge,
-                          TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_AGE;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_AGE,
-                          getMessage(msgID), false, timeUnits, true, 0, true,
-                          Integer.MAX_VALUE, maximumPasswordAge,
-                          TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_RESET_AGE;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_RESET_AGE,
-                          getMessage(msgID), false, timeUnits, true, 0, true,
-                          Integer.MAX_VALUE, maximumPasswordResetAge,
-                          TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_WARNING_INTERVAL;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_WARNING_INTERVAL, getMessage(msgID),
-                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
-                          warningInterval, TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_EXPIRE_WITHOUT_WARNING;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_EXPIRE_WITHOUT_WARNING,
-                          getMessage(msgID), false,
-                          expirePasswordsWithoutWarning));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_EXPIRED_CHANGES;
-    attrList.add(new BooleanConfigAttribute(
-                          ATTR_PWPOLICY_ALLOW_EXPIRED_CHANGES,
-                          getMessage(msgID), false,
-                          allowExpiredPasswordChanges));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_GRACE_LOGIN_COUNT;
-    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_GRACE_LOGIN_COUNT,
-                                            getMessage(msgID), false, false,
-                                            false, true, 0, true,
-                                            Integer.MAX_VALUE,
-                                            graceLoginCount));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_FAILURE_COUNT;
-    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_LOCKOUT_FAILURE_COUNT,
-                                            getMessage(msgID), false, false,
-                                            false, true, 0, true,
-                                            Integer.MAX_VALUE,
-                                            lockoutFailureCount));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_DURATION;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_LOCKOUT_DURATION, getMessage(msgID),
-                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
-                          lockoutDuration, TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_FAILURE_EXPIRATION;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_LOCKOUT_FAILURE_EXPIRATION_INTERVAL,
-                          getMessage(msgID), false, timeUnits, true, 0, true,
-                          Integer.MAX_VALUE, lockoutFailureExpirationInterval,
-                          TIME_UNIT_SECONDS_FULL));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CHANGE_BY_TIME;
-    String timeStr = null;
-    if (requireChangeByTime > 0)
-    {
-      timeStr = GeneralizedTimeSyntax.createGeneralizedTimeValue(
-                     requireChangeByTime).getStringValue();
-    }
-    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_REQUIRE_CHANGE_BY_TIME,
-                                           getMessage(msgID), false, false,
-                                           false, timeStr));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_ATTR;
-    String loginTimeAttr;
-    if (lastLoginTimeAttribute == null)
-    {
-      loginTimeAttr = null;
-    }
-    else
-    {
-      loginTimeAttr = lastLoginTimeAttribute.getNameOrOID();
-    }
-    attrList.add(new StringConfigAttribute(
-                          ATTR_PWPOLICY_LAST_LOGIN_TIME_ATTRIBUTE,
-                          getMessage(msgID), false, false, false,
-                          loginTimeAttr));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_FORMAT;
-    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_LAST_LOGIN_TIME_FORMAT,
-                                           getMessage(msgID), false, false,
-                                           false, lastLoginTimeFormat));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
-    ArrayList<String> previousFormats = new ArrayList<String>();
-    if (previousLastLoginTimeFormats != null)
-    {
-      previousFormats.addAll(previousLastLoginTimeFormats);
-    }
-    attrList.add(new StringConfigAttribute(
-                          ATTR_PWPOLICY_PREVIOUS_LAST_LOGIN_TIME_FORMAT,
-                          getMessage(msgID), false, false, false,
-                          previousFormats));
-
-
-    msgID = MSGID_PWPOLICY_DESCRIPTION_IDLE_LOCKOUT_INTERVAL;
-    attrList.add(new IntegerWithUnitConfigAttribute(
-                          ATTR_PWPOLICY_IDLE_LOCKOUT_INTERVAL,
-                          getMessage(msgID), false, timeUnits, true, 0, true,
-                          Integer.MAX_VALUE,  idleLockoutInterval,
-                          TIME_UNIT_SECONDS_FULL));
-
-
-    return attrList;
-  }
-
-
-
-  /**
-   * Indicates whether the provided configuration entry has an acceptable
-   * configuration for this component.  If it does not, then detailed
-   * information about the problem(s) should be added to the provided list.
-   *
-   * @param  configEntry          The configuration entry for which to make the
-   *                              determination.
-   * @param  unacceptableReasons  A list that can be used to hold messages about
-   *                              why the provided entry does not have an
-   *                              acceptable configuration.
-   *
-   * @return  <CODE>true</CODE> if the provided entry has an acceptable
-   *          configuration for this component, or <CODE>false</CODE> if not.
-   */
-  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
-                                            List<String> unacceptableReasons)
-  {
-    assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
-                      String.valueOf(configEntry), "java.util.List<String>");
-
-
-    PasswordPolicy p = new PasswordPolicy();
-
-    try
-    {
-      initializePasswordPolicyConfig(configEntry, p);
-    }
-    catch (ConfigException ce)
-    {
-      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ce);
-
-      unacceptableReasons.add(ce.getMessage());
-      return false;
-    }
-    catch (InitializationException ie)
-    {
-      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ie);
-
-      unacceptableReasons.add(ie.getMessage());
-      return false;
-    }
-
-
-    // The provided config entry must at least specify the password attribute
-    // and at least one default storage scheme.
-    if (p.passwordAttribute == null)
-    {
-      int    msgID   = MSGID_PWPOLICY_NO_PASSWORD_ATTRIBUTE;
-      String message = getMessage(msgID, String.valueOf(configEntryDN));
-      unacceptableReasons.add(message);
-      return false;
-    }
-
-    if ((p.defaultStorageSchemes == null) ||
-        p.defaultStorageSchemes.isEmpty())
-    {
-      int    msgID   = MSGID_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES;
-      String message = getMessage(msgID, String.valueOf(configEntryDN));
-      unacceptableReasons.add(message);
-      return false;
-    }
-
-
-    // If we made it here, then the configuration is acceptable.
-    return true;
-  }
-
-
-
-  /**
-   * Makes a best-effort attempt to apply the configuration contained in the
-   * provided entry.  Information about the result of this processing should be
-   * added to the provided message list.  Information should always be added to
-   * this list if a configuration change could not be applied.  If detailed
-   * results are requested, then information about the changes applied
-   * successfully (and optionally about parameters that were not changed) should
-   * also be included.
-   *
-   * @param  configEntry      The entry containing the new configuration to
-   *                          apply for this component.
-   * @param  detailedResults  Indicates whether detailed information about the
-   *                          processing should be added to the list.
-   *
-   * @return  Information about the result of the configuration update.
-   */
-  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
-                                                  boolean detailedResults)
-  {
-    assert debugEnter(CLASS_NAME, "applyNewConfiguration",
-                      String.valueOf(configEntry),
-                      String.valueOf(detailedResults));
-
-
-    ResultCode        resultCode          = ResultCode.SUCCESS;
-    boolean           adminActionRequired = false;
-    ArrayList<String> messages            = new ArrayList<String>();
-    PasswordPolicy    p                   = new PasswordPolicy();
-
-    try
-    {
-      initializePasswordPolicyConfig(configEntry, p);
-    }
-    catch (ConfigException ce)
-    {
-      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ce);
-
-      resultCode = DirectoryServer.getServerErrorResultCode();
-      messages.add(ce.getMessage());
-
-      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    }
-    catch (InitializationException ie)
-    {
-      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ie);
-
-      resultCode = DirectoryServer.getServerErrorResultCode();
-      messages.add(ie.getMessage());
-
-      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    }
-
-
-    // The provided config entry must at least specify the password attribute
-    // and at least one default storage scheme.
-    if (p.passwordAttribute == null)
-    {
-      resultCode = DirectoryServer.getServerErrorResultCode();
-
-      int    msgID   = MSGID_PWPOLICY_NO_PASSWORD_ATTRIBUTE;
-      messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
-
-      return new ConfigChangeResult(resultCode, adminActionRequired,
-                                    messages);
-    }
-
-    if ((p.defaultStorageSchemes == null) ||
-        p.defaultStorageSchemes.isEmpty())
-    {
-      resultCode = DirectoryServer.getServerErrorResultCode();
-
-      int    msgID   = MSGID_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES;
-      messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
-
-      return new ConfigChangeResult(resultCode, adminActionRequired,
-                                    messages);
-    }
-
-
-    // If we've made it here, then everything is acceptable.  Apply the new
-    // configuration.
-    passwordAttribute                = p.passwordAttribute;
-    authPasswordSyntax               = p.authPasswordSyntax;
-    lastLoginTimeAttribute           = p.lastLoginTimeAttribute;
-    allowMultiplePasswordValues      = p.allowMultiplePasswordValues;
-    allowPreEncodedPasswords         = p.allowPreEncodedPasswords;
-    allowUserPasswordChanges         = p.allowUserPasswordChanges;
-    expirePasswordsWithoutWarning    = p.expirePasswordsWithoutWarning;
-    allowExpiredPasswordChanges      = p.allowExpiredPasswordChanges;
-    forceChangeOnAdd                 = p.forceChangeOnAdd;
-    forceChangeOnReset               = p.forceChangeOnReset;
-    requireCurrentPassword           = p.requireCurrentPassword;
-    requireSecureAuthentication      = p.requireSecureAuthentication;
-    requireSecurePasswordChanges     = p.requireSecurePasswordChanges;
-    skipValidationForAdministrators  = p.skipValidationForAdministrators;
-    graceLoginCount                  = p.graceLoginCount;
-    idleLockoutInterval              = p.idleLockoutInterval;
-    lockoutDuration                  = p.lockoutDuration;
-    lockoutFailureCount              = p.lockoutFailureCount;
-    lockoutFailureExpirationInterval = p.lockoutFailureExpirationInterval;
-    minimumPasswordAge               = p.minimumPasswordAge;
-    maximumPasswordAge               = p.maximumPasswordAge;
-    maximumPasswordResetAge          = p.maximumPasswordResetAge;
-    warningInterval                  = p.warningInterval;
-    requireChangeByTime              = p.requireChangeByTime;
-    lastLoginTimeFormat              = p.lastLoginTimeFormat;
-    previousLastLoginTimeFormats     = p.previousLastLoginTimeFormats;
-    passwordGenerator                = p.passwordGenerator;
-    passwordGeneratorDN              = p.passwordGeneratorDN;
-    notificationHandlers             = p.notificationHandlers;
-    defaultStorageSchemes            = p.defaultStorageSchemes;
-    deprecatedStorageSchemes         = p.deprecatedStorageSchemes;
-    passwordValidators               = p.passwordValidators;
-
-    if (detailedResults)
-    {
-      int msgID = MSGID_PWPOLICY_UPDATED_POLICY;
-      messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
-    }
-
-    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
-  }
-
-
-
-  /**
    * Retrieves a string representation of this password policy.
    *
    * @return  A string representation of this password policy.
diff --git a/opends/src/server/org/opends/server/core/PasswordPolicyConfig.java b/opends/src/server/org/opends/server/core/PasswordPolicyConfig.java
new file mode 100644
index 0000000..e079456
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/PasswordPolicyConfig.java
@@ -0,0 +1,522 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.server.api.ConfigurableComponent;
+import org.opends.server.api.PasswordStorageScheme;
+import org.opends.server.config.BooleanConfigAttribute;
+import org.opends.server.config.ConfigAttribute;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.DNConfigAttribute;
+import org.opends.server.config.IntegerConfigAttribute;
+import org.opends.server.config.IntegerWithUnitConfigAttribute;
+import org.opends.server.config.StringConfigAttribute;
+import org.opends.server.schema.GeneralizedTimeSyntax;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.CoreMessages.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.util.ServerConstants.*;
+
+/**
+ This class is the interface between the password policy configurable component
+ and a password policy state object. When a password policy entry is added to
+ the configuration, an instance of this class is created and registered to
+ manage subsequent modification to that configuration entry, including
+ valiadating any proposed modification and applying an accepted modification.
+ */
+public class PasswordPolicyConfig
+        implements ConfigurableComponent
+{
+  /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+       "org.opends.server.core.PasswordPolicyConfig";
+
+  /**
+   * The password policy object corresponding to the configuration entry. The
+   * policy referenced by this field is assumed to be valid, hence any
+   * changes resulting from a modification of the configuration entry must be
+   * applied to a newly allocated instance and validated before updating this
+   * reference to point to the new policy instance.
+   */
+  private PasswordPolicy currentPolicy;
+
+
+  /**
+   * Creates a new password policy configurable component to manage the provided
+   * password policy object.
+   *
+   * @param policy The password policy object this object will manage.
+   */
+  public PasswordPolicyConfig(PasswordPolicy policy)
+  {
+    assert debugConstructor(CLASS_NAME, String.valueOf(policy));
+
+    this.currentPolicy = policy;
+    DirectoryServer.registerConfigurableComponent(this);
+  }
+
+
+
+  /**
+   * Finalize a password policy configuration handler.
+   */
+  public void finalizePasswordPolicyConfig()
+  {
+    assert debugEnter(CLASS_NAME, "finalizePasswordPolicyConfig");
+
+    DirectoryServer.deregisterConfigurableComponent(this);
+  }
+
+
+
+  /**
+   * Retrieves the DN of the configuration entry with which this component is
+   * associated.
+   *
+   * @return  The DN of the configuration entry with which this component is
+   *          associated.
+   */
+  public DN getConfigurableComponentEntryDN()
+  {
+    assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
+
+    return currentPolicy.getConfigEntryDN();
+  }
+
+
+
+  /**
+   * Retrieves the set of configuration attributes that are associated with this
+   * configurable component.
+   *
+   * @return  The set of configuration attributes that are associated with this
+   *          configurable component.
+   */
+  public List<ConfigAttribute> getConfigurationAttributes()
+  {
+    assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
+
+
+    // Create a list of units and values that we can use to represent time
+    // periods.
+    LinkedHashMap<String,Double> timeUnits = new LinkedHashMap<String,Double>();
+    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
+    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
+    timeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
+    timeUnits.put(TIME_UNIT_MINUTES_FULL, 60D);
+    timeUnits.put(TIME_UNIT_HOURS_ABBR, (double) (60 * 60));
+    timeUnits.put(TIME_UNIT_HOURS_FULL, (double) (60 * 60));
+    timeUnits.put(TIME_UNIT_DAYS_ABBR, (double) (60 * 60 * 24));
+    timeUnits.put(TIME_UNIT_DAYS_FULL, (double) (60 * 60 * 24));
+    timeUnits.put(TIME_UNIT_WEEKS_ABBR, (double) (60 * 60 * 24 * 7));
+    timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
+
+
+    PasswordPolicy policy = this.currentPolicy; // this field is volatile
+
+    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
+
+    int msgID = MSGID_PWPOLICY_DESCRIPTION_PW_ATTR;
+    String pwAttr = (policy.getPasswordAttribute() == null)
+                    ? null
+                    : policy.getPasswordAttribute().getNameOrOID();
+    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_PASSWORD_ATTRIBUTE,
+                                           getMessage(msgID), false, false,
+                                           false, pwAttr));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_DEFAULT_STORAGE_SCHEMES;
+    ArrayList<String> schemes = new ArrayList<String>();
+    for (PasswordStorageScheme s : policy.getDefaultStorageSchemes())
+    {
+      schemes.add(s.getStorageSchemeName());
+    }
+    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEFAULT_SCHEME,
+                                           getMessage(msgID), false, true,
+                                           false, schemes));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_DEPRECATED_STORAGE_SCHEMES;
+    ArrayList<String> deprecatedSchemes = new ArrayList<String>();
+    deprecatedSchemes.addAll(policy.getDeprecatedStorageSchemes());
+    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEPRECATED_SCHEME,
+                                           getMessage(msgID), false, true,
+                                           false, deprecatedSchemes));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_VALIDATORS;
+    ArrayList<DN> validatorDNs = new ArrayList<DN>();
+    validatorDNs.addAll(policy.getPasswordValidators().keySet());
+    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_VALIDATOR,
+                                       getMessage(msgID), false, true, false,
+                                       validatorDNs));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_NOTIFICATION_HANDLERS;
+    ArrayList<DN> handlerDNs = new ArrayList<DN>();
+    handlerDNs.addAll(policy.getAccountStatusNotificationHandlers().keySet());
+    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_NOTIFICATION_HANDLER,
+                                       getMessage(msgID), false, true, false,
+                                       handlerDNs));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_USER_PW_CHANGES;
+    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_USER_CHANGE,
+                                            getMessage(msgID), false,
+                                            policy.allowUserPasswordChanges()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CURRENT_PW;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_REQUIRE_CURRENT_PASSWORD,
+                             getMessage(msgID), false,
+                             policy.requireCurrentPassword()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_ADD;
+    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_ADD,
+                                            getMessage(msgID), false,
+                                            policy.forceChangeOnAdd()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_RESET;
+    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_RESET,
+                                            getMessage(msgID), false,
+                                            policy.forceChangeOnReset()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_SKIP_ADMIN_VALIDATION;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_SKIP_ADMIN_VALIDATION,
+                             getMessage(msgID), false,
+                             policy.skipValidationForAdministrators()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_GENERATOR;
+    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_GENERATOR,
+                                       getMessage(msgID), false, false, false,
+                                       policy.getPasswordGeneratorDN()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_AUTH;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_REQUIRE_SECURE_AUTHENTICATION,
+                             getMessage(msgID), false,
+                             policy.requireSecureAuthentication()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_CHANGES;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_REQUIRE_SECURE_PASSWORD_CHANGES,
+                             getMessage(msgID), false,
+                             policy.requireSecurePasswordChanges()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_MULTIPLE_PW_VALUES;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES,
+                             getMessage(msgID), false,
+                             policy.allowMultiplePasswordValues()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_PREENCODED;
+    attrList.add(new BooleanConfigAttribute(
+                             ATTR_PWPOLICY_ALLOW_PRE_ENCODED_PASSWORDS,
+                             getMessage(msgID), false,
+                             policy.allowPreEncodedPasswords()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_MIN_AGE;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_MINIMUM_PASSWORD_AGE,
+                          getMessage(msgID), false, timeUnits, true, 0, true,
+                          Integer.MAX_VALUE, policy.getMinimumPasswordAge(),
+                          TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_AGE;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_AGE,
+                          getMessage(msgID), false, timeUnits, true, 0, true,
+                          Integer.MAX_VALUE, policy.getMaximumPasswordAge(),
+                          TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_RESET_AGE;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_RESET_AGE,
+                          getMessage(msgID), false, timeUnits, true, 0, true,
+                          Integer.MAX_VALUE,
+                          policy.getMaximumPasswordResetAge(),
+                          TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_WARNING_INTERVAL;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_WARNING_INTERVAL, getMessage(msgID),
+                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
+                          policy.getWarningInterval(), TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_EXPIRE_WITHOUT_WARNING;
+    attrList.add(new BooleanConfigAttribute(
+                          ATTR_PWPOLICY_EXPIRE_WITHOUT_WARNING,
+                          getMessage(msgID), false,
+                          policy.expirePasswordsWithoutWarning()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_EXPIRED_CHANGES;
+    attrList.add(new BooleanConfigAttribute(
+                          ATTR_PWPOLICY_ALLOW_EXPIRED_CHANGES,
+                          getMessage(msgID), false,
+                          policy.allowExpiredPasswordChanges()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_GRACE_LOGIN_COUNT;
+    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_GRACE_LOGIN_COUNT,
+                                            getMessage(msgID), false, false,
+                                            false, true, 0, true,
+                                            Integer.MAX_VALUE,
+                                            policy.getGraceLoginCount()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_FAILURE_COUNT;
+    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_LOCKOUT_FAILURE_COUNT,
+                                            getMessage(msgID), false, false,
+                                            false, true, 0, true,
+                                            Integer.MAX_VALUE,
+                                            policy.getLockoutFailureCount()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_DURATION;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_LOCKOUT_DURATION, getMessage(msgID),
+                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
+                          policy.getLockoutDuration(), TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_FAILURE_EXPIRATION;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_LOCKOUT_FAILURE_EXPIRATION_INTERVAL,
+                          getMessage(msgID), false, timeUnits, true, 0, true,
+                          Integer.MAX_VALUE,
+                          policy.getLockoutFailureExpirationInterval(),
+                          TIME_UNIT_SECONDS_FULL));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CHANGE_BY_TIME;
+    String timeStr = null;
+    if (policy.getRequireChangeByTime() > 0)
+    {
+      timeStr = GeneralizedTimeSyntax.createGeneralizedTimeValue(
+                     policy.getRequireChangeByTime()).getStringValue();
+    }
+    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_REQUIRE_CHANGE_BY_TIME,
+                                           getMessage(msgID), false, false,
+                                           false, timeStr));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_ATTR;
+    String loginTimeAttr = (policy.getLastLoginTimeAttribute() == null)
+                           ? null
+                           : policy.getLastLoginTimeAttribute().getNameOrOID();
+    attrList.add(new StringConfigAttribute(
+                          ATTR_PWPOLICY_LAST_LOGIN_TIME_ATTRIBUTE,
+                          getMessage(msgID), false, false, false,
+                          loginTimeAttr));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_FORMAT;
+    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_LAST_LOGIN_TIME_FORMAT,
+                                           getMessage(msgID), false, false,
+                                           false,
+                                           policy.getLastLoginTimeFormat()));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
+    ArrayList<String> previousFormats = new ArrayList<String>();
+    previousFormats.addAll(policy.getPreviousLastLoginTimeFormats());
+    attrList.add(new StringConfigAttribute(
+                          ATTR_PWPOLICY_PREVIOUS_LAST_LOGIN_TIME_FORMAT,
+                          getMessage(msgID), false, false, false,
+                          previousFormats));
+
+
+    msgID = MSGID_PWPOLICY_DESCRIPTION_IDLE_LOCKOUT_INTERVAL;
+    attrList.add(new IntegerWithUnitConfigAttribute(
+                          ATTR_PWPOLICY_IDLE_LOCKOUT_INTERVAL,
+                          getMessage(msgID), false, timeUnits, true, 0, true,
+                          Integer.MAX_VALUE,  policy.getIdleLockoutInterval(),
+                          TIME_UNIT_SECONDS_FULL));
+
+    return attrList;
+  }
+
+
+
+  /**
+   * Indicates whether the provided configuration entry has an acceptable
+   * configuration for this component.  If it does not, then detailed
+   * information about the problem(s) should be added to the provided list.
+   *
+   * @param  configEntry          The configuration entry for which to make the
+   *                              determination.
+   * @param  unacceptableReasons  A list that can be used to hold messages about
+   *                              why the provided entry does not have an
+   *                              acceptable configuration.
+   *
+   * @return  <CODE>true</CODE> if the provided entry has an acceptable
+   *          configuration for this component, or <CODE>false</CODE> if not.
+   */
+  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
+                                            List<String> unacceptableReasons)
+  {
+    assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
+                      String.valueOf(configEntry), "java.util.List<String>");
+
+    assert configEntry.getDN().equals(this.currentPolicy.getConfigEntryDN() )
+            : "Internal Error: mismatch between DN of configuration entry and"
+              + "DN of current password policy." ;
+
+    try
+    {
+      new PasswordPolicy(configEntry);
+    }
+    catch (ConfigException ce)
+    {
+      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ce);
+
+      unacceptableReasons.add(ce.getMessage());
+      return false;
+    }
+    catch (InitializationException ie)
+    {
+      assert debugException(CLASS_NAME, "hasAcceptableConfiguration", ie);
+
+      unacceptableReasons.add(ie.getMessage());
+      return false;
+    }
+
+    // If we made it here, then the configuration is acceptable.
+    return true;
+  }
+
+
+
+  /**
+   * Makes a best-effort attempt to apply the configuration contained in the
+   * provided entry.  Information about the result of this processing should be
+   * added to the provided message list.  Information should always be added to
+   * this list if a configuration change could not be applied.  If detailed
+   * results are requested, then information about the changes applied
+   * successfully (and optionally about parameters that were not changed) should
+   * also be included.
+   *
+   * @param  configEntry      The entry containing the new configuration to
+   *                          apply for this component.
+   * @param  detailedResults  Indicates whether detailed information about the
+   *                          processing should be added to the list.
+   *
+   * @return  Information about the result of the configuration update.
+   */
+  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
+                                                  boolean detailedResults)
+  {
+    assert debugEnter(CLASS_NAME, "applyNewConfiguration",
+                      String.valueOf(configEntry),
+                      String.valueOf(detailedResults));
+
+    assert configEntry.getDN().equals(this.currentPolicy.getConfigEntryDN() )
+            : "Internal Error: mismatch between DN of configuration entry and"
+              + "DN of current password policy." ;
+
+    PasswordPolicy p;
+
+    try
+    {
+      p = new PasswordPolicy(configEntry);
+    }
+    catch (ConfigException ce)
+    {
+      assert debugException(CLASS_NAME, "applyNewConfiguration", ce);
+      ArrayList<String> messages = new ArrayList<String>();
+      messages.add(ce.getMessage());
+      return new ConfigChangeResult(
+              DirectoryServer.getServerErrorResultCode(),
+              /*adminActionRequired*/ true, messages);
+    }
+    catch (InitializationException ie)
+    {
+      assert debugException(CLASS_NAME, "applyNewConfiguration", ie);
+      ArrayList<String> messages = new ArrayList<String>();
+      messages.add(ie.getMessage());
+      return new ConfigChangeResult(
+              DirectoryServer.getServerErrorResultCode(),
+              /*adminActionRequired*/ true, messages);
+    }
+
+    // If we've made it here, then everything is acceptable.  Apply the new
+    // configuration.
+    ArrayList<String> messages = new ArrayList<String>();
+    if (detailedResults)
+    {
+      int msgID = MSGID_PWPOLICY_UPDATED_POLICY;
+      messages.add(getMessage(msgID, String.valueOf(p.getConfigEntryDN())));
+    }
+
+    this.currentPolicy = p;
+
+    return new ConfigChangeResult(ResultCode.SUCCESS,
+                                  /*adminActionRequired*/ false, messages);
+  }
+
+
+  /**
+   * Retrieves the PasswordPolicy object representing the configuration entry
+   * managed by this object.
+   *
+   * @return The PasswordPolicy object.
+   */
+  public PasswordPolicy getPolicy()
+  {
+    return currentPolicy;
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java b/opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java
index f890bf0..c8fad01 100644
--- a/opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java
+++ b/opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -136,8 +136,7 @@
 
 
     // Get the DN of the default password policy from the core configuration.
-    DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
-    if (defaultPolicyDN == null)
+    if( null == DirectoryServer.getDefaultPasswordPolicyDN())
     {
       int    msgID   = MSGID_CONFIG_PWPOLICY_NO_DEFAULT_POLICY;
       String message = getMessage(msgID);
@@ -147,24 +146,12 @@
 
     // Iterate through the child entries and process them as password policy
     // configuration entries.
-    boolean defaultExists = false;
     for (ConfigEntry childEntry : baseEntry.getChildren().values())
     {
-      boolean isDefault = defaultPolicyDN.equals(childEntry.getDN());
-      if (isDefault)
-      {
-        defaultExists = true;
-      }
-
       try
       {
         PasswordPolicy policy = new PasswordPolicy(childEntry);
         DirectoryServer.registerPasswordPolicy(childEntry.getDN(), policy);
-
-        if (isDefault)
-        {
-          DirectoryServer.setDefaultPasswordPolicy(policy);
-        }
       }
       catch (ConfigException ce)
       {
@@ -190,10 +177,12 @@
     }
 
 
-    // If we didn't find the default password policy, then fail.
-    if (! defaultExists)
+    // If the entry specified by the default password policy DN has not been
+    // registered, then fail.
+    if (null == DirectoryServer.getDefaultPasswordPolicy())
     {
       int    msgID   = MSGID_CONFIG_PWPOLICY_MISSING_DEFAULT_POLICY;
+      DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
       String message = getMessage(msgID, String.valueOf(defaultPolicyDN));
       throw new ConfigException(msgID, message);
     }
@@ -225,7 +214,7 @@
     // entry.  If so, then it's acceptable.
     try
     {
-      PasswordPolicy policy = new PasswordPolicy(configEntry);
+      new PasswordPolicy(configEntry);
     }
     catch (ConfigException ce)
     {
@@ -338,6 +327,9 @@
 
 
     // We'll allow the policy to be removed as long as it isn't the default.
+    // FIXME: something like a referential integrity check is needed to ensure
+    //  a policy is not removed when referenced by a user entry (either
+    // directly or via a virtual attribute).
     DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
     if ((defaultPolicyDN != null) &&
         defaultPolicyDN.equals(configEntry.getDN()))
@@ -370,6 +362,9 @@
 
 
     // We'll allow the policy to be removed as long as it isn't the default.
+    // FIXME: something like a referential integrity check is needed to ensure
+    //  a policy is not removed when referenced by a user entry (either
+    // directly or via a virtual attribute).
     ArrayList<String> messages = new ArrayList<String>(1);
     DN policyDN = configEntry.getDN();
     DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
@@ -381,15 +376,12 @@
       return new ConfigChangeResult(ResultCode.CONSTRAINT_VIOLATION, false,
                                     messages);
     }
-    else
-    {
-      DirectoryServer.deregisterPasswordPolicy(policyDN);
 
-      int msgID = MSGID_CONFIG_PWPOLICY_REMOVED_POLICY;
-      messages.add(getMessage(msgID, String.valueOf(policyDN)));
+    DirectoryServer.deregisterPasswordPolicy(policyDN);
 
-      return new ConfigChangeResult(ResultCode.SUCCESS, false, messages);
-    }
+    int msgID = MSGID_CONFIG_PWPOLICY_REMOVED_POLICY;
+    messages.add(getMessage(msgID, String.valueOf(policyDN)));
+
+    return new ConfigChangeResult(ResultCode.SUCCESS, false, messages);
   }
 }
-
diff --git a/opends/src/server/org/opends/server/plugins/PasswordPolicyImportPlugin.java b/opends/src/server/org/opends/server/plugins/PasswordPolicyImportPlugin.java
index 8145935..318a3c7 100644
--- a/opends/src/server/org/opends/server/plugins/PasswordPolicyImportPlugin.java
+++ b/opends/src/server/org/opends/server/plugins/PasswordPolicyImportPlugin.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.plugins;
 
@@ -107,7 +107,7 @@
     // "final".
     authPasswordSchemes = new HashMap<AttributeType,PasswordStorageScheme[]>();
     userPasswordSchemes = new HashMap<AttributeType,PasswordStorageScheme[]>();
-    for (PasswordPolicy p : DirectoryServer.getPasswordPolicies().values())
+    for (PasswordPolicy p : DirectoryServer.getPasswordPolicies())
     {
       AttributeType t = p.getPasswordAttribute();
       if (p.usesAuthPasswordSyntax())
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
index 7860581..4872d19 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.core;
 
@@ -30,7 +30,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
 
@@ -42,7 +41,6 @@
 import org.opends.server.api.PasswordStorageScheme;
 import org.opends.server.config.ConfigEntry;
 import org.opends.server.config.ConfigException;
-import org.opends.server.core.ModifyOperation;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
@@ -2113,7 +2111,7 @@
     ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
     ConfigEntry configEntry = new ConfigEntry(e, parentEntry);
 
-    PasswordPolicy p = new PasswordPolicy(configEntry);
+    new PasswordPolicy(configEntry);
   }
 
 
@@ -2211,6 +2209,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     defaultSchemes = p.getDefaultStorageSchemes();
     assertNotNull(defaultSchemes);
     assertFalse(defaultSchemes.isEmpty());
@@ -2254,6 +2253,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     defaultSchemes = p.getDefaultStorageSchemes();
     assertNotNull(defaultSchemes);
     assertFalse(defaultSchemes.isEmpty());
@@ -2295,6 +2295,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.isDefaultStorageScheme("BASE64"));
     assertFalse(p.isDefaultStorageScheme("SSHA"));
     p.toString();
@@ -2335,6 +2336,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.isDefaultStorageScheme("MD5"));
     assertFalse(p.isDefaultStorageScheme("SHA1"));
     p.toString();
@@ -2378,6 +2380,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     deprecatedSchemes = p.getDeprecatedStorageSchemes();
     assertNotNull(deprecatedSchemes);
     assertFalse(deprecatedSchemes.isEmpty());
@@ -2421,6 +2424,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     deprecatedSchemes = p.getDeprecatedStorageSchemes();
     assertNotNull(deprecatedSchemes);
     assertFalse(deprecatedSchemes.isEmpty());
@@ -2461,6 +2465,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.isDeprecatedStorageScheme("BASE64"));
     p.toString();
 
@@ -2499,6 +2504,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.isDeprecatedStorageScheme("MD5"));
     p.toString();
 
@@ -2540,6 +2546,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNotNull(p.getPasswordValidators());
     assertFalse(p.getPasswordValidators().isEmpty());
     p.toString();
@@ -2583,6 +2590,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNotNull(p.getPasswordValidators());
     assertFalse(p.getPasswordValidators().isEmpty());
     p.toString();
@@ -2625,6 +2633,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNotNull(p.getAccountStatusNotificationHandlers());
     assertFalse(p.getAccountStatusNotificationHandlers().isEmpty());
     p.toString();
@@ -2668,6 +2677,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNotNull(p.getAccountStatusNotificationHandlers());
     assertFalse(p.getAccountStatusNotificationHandlers().isEmpty());
     p.toString();
@@ -2706,6 +2716,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertFalse(p.allowUserPasswordChanges());
     p.toString();
 
@@ -2744,6 +2755,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertFalse(p.allowUserPasswordChanges());
     p.toString();
 
@@ -2782,6 +2794,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.requireCurrentPassword());
     p.toString();
 
@@ -2820,6 +2833,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.allowUserPasswordChanges());
     p.toString();
 
@@ -2858,6 +2872,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.forceChangeOnAdd());
     p.toString();
 
@@ -2896,6 +2911,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.forceChangeOnAdd());
     p.toString();
 
@@ -2934,6 +2950,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.forceChangeOnReset());
     p.toString();
 
@@ -2972,6 +2989,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.forceChangeOnReset());
     p.toString();
 
@@ -3010,6 +3028,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.skipValidationForAdministrators());
     p.toString();
 
@@ -3048,6 +3067,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.skipValidationForAdministrators());
     p.toString();
 
@@ -3089,6 +3109,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNull(p.getPasswordGeneratorDN());
     p.toString();
 
@@ -3130,6 +3151,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNull(p.getPasswordGeneratorDN());
     p.toString();
 
@@ -3171,6 +3193,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNull(p.getPasswordGenerator());
     p.toString();
 
@@ -3212,6 +3235,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNull(p.getPasswordGenerator());
     p.toString();
 
@@ -3250,6 +3274,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.requireSecureAuthentication());
     p.toString();
 
@@ -3288,6 +3313,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.requireSecureAuthentication());
     p.toString();
 
@@ -3326,6 +3352,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.requireSecurePasswordChanges());
     p.toString();
 
@@ -3364,6 +3391,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.requireSecurePasswordChanges());
     p.toString();
 
@@ -3402,6 +3430,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.allowMultiplePasswordValues());
     p.toString();
 
@@ -3440,6 +3469,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.allowMultiplePasswordValues());
     p.toString();
 
@@ -3478,6 +3508,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.allowPreEncodedPasswords());
     p.toString();
 
@@ -3516,6 +3547,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.allowPreEncodedPasswords());
     p.toString();
 
@@ -3554,6 +3586,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getMinimumPasswordAge(), (24*60*60));
     p.toString();
 
@@ -3592,6 +3625,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getMinimumPasswordAge(), (24*60*60));
     p.toString();
 
@@ -3630,6 +3664,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getMaximumPasswordAge(), (90*60*60*24));
     p.toString();
 
@@ -3668,6 +3703,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getMaximumPasswordAge(), (90*60*60*24));
     p.toString();
 
@@ -3706,6 +3742,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getMaximumPasswordResetAge(), (24*60*60));
     p.toString();
 
@@ -3744,6 +3781,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getMaximumPasswordResetAge(), (24*60*60));
     p.toString();
 
@@ -3782,6 +3820,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getWarningInterval(), (24*60*60));
     p.toString();
 
@@ -3820,6 +3859,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getWarningInterval(), (24*60*60));
     p.toString();
 
@@ -3858,6 +3898,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.expirePasswordsWithoutWarning());
     p.toString();
 
@@ -3896,6 +3937,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.expirePasswordsWithoutWarning());
     p.toString();
 
@@ -3934,6 +3976,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertTrue(p.allowExpiredPasswordChanges());
     p.toString();
 
@@ -3972,6 +4015,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertTrue(p.allowExpiredPasswordChanges());
     p.toString();
 
@@ -4010,6 +4054,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getGraceLoginCount(), 3);
     p.toString();
 
@@ -4048,6 +4093,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getGraceLoginCount(), 3);
     p.toString();
 
@@ -4086,6 +4132,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getLockoutFailureCount(), 3);
     p.toString();
 
@@ -4124,6 +4171,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getLockoutFailureCount(), 3);
     p.toString();
 
@@ -4162,6 +4210,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getLockoutDuration(), (15*60));
     p.toString();
 
@@ -4200,6 +4249,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getLockoutDuration(), (15*60));
     p.toString();
 
@@ -4238,6 +4288,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getLockoutFailureExpirationInterval(), (10*60));
     p.toString();
 
@@ -4276,6 +4327,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getLockoutFailureExpirationInterval(), (10*60));
     p.toString();
 
@@ -4315,6 +4367,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getRequireChangeByTime(), 1000);
     p.toString();
 
@@ -4353,6 +4406,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getRequireChangeByTime(), 1000);
     p.toString();
 
@@ -4391,6 +4445,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNotNull(p.getLastLoginTimeAttribute());
     p.toString();
 
@@ -4429,6 +4484,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNotNull(p.getLastLoginTimeAttribute());
     p.toString();
 
@@ -4467,6 +4523,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getLastLoginTimeFormat(), "yyyyMMdd");
     p.toString();
 
@@ -4505,6 +4562,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getLastLoginTimeFormat(), "yyyyMMdd");
     p.toString();
 
@@ -4544,6 +4602,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertNotNull(p.getPreviousLastLoginTimeFormats());
     assertFalse(p.getPreviousLastLoginTimeFormats().isEmpty());
     p.toString();
@@ -4584,6 +4643,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertNotNull(p.getPreviousLastLoginTimeFormats());
     assertFalse(p.getPreviousLastLoginTimeFormats().isEmpty());
     p.toString();
@@ -4622,6 +4682,7 @@
          conn.processModify(DN.decode(dnStr), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getDefaultPasswordPolicy();
     assertEquals(p.getIdleLockoutInterval(), (90*60*60*24));
     p.toString();
 
@@ -4660,6 +4721,7 @@
     ModifyOperation modifyOperation = conn.processModify(dn, mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
 
+    p = DirectoryServer.getPasswordPolicy(dn);
     assertEquals(p.getIdleLockoutInterval(), (90*60*60*24));
     p.toString();
 

--
Gitblit v1.10.0