From 019158fc70a39418454962d9ec15f54feacea231 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sun, 24 Jun 2007 00:55:50 +0000
Subject: [PATCH] Migrate the root DN configuration to the admin framework.

---
 opends/src/admin/defn/org/opends/server/admin/std/RootDNConfiguration.xml     |  214 ++++++++
 opends/src/admin/defn/org/opends/server/admin/std/RootDNUserConfiguration.xml |   75 +++
 opends/src/server/org/opends/server/core/RootPrivilegeChangeListener.java     |  190 +++++++
 opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml       |   11 
 opends/src/server/org/opends/server/core/RootDNConfigManager.java             |  943 ++++++++-------------------------------
 5 files changed, 688 insertions(+), 745 deletions(-)

diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index 7397292..5d5834f 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -229,6 +229,17 @@
       </cli:relation>
     </adm:profile>
   </adm:relation>
+  <adm:relation name="root-dn">
+    <adm:one-to-one />
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>cn=Root DNs,cn=config</ldap:rdn-sequence>
+    </adm:profile>
+    <adm:profile name="cli">
+      <cli:relation>
+        <cli:default-property name="default-root-privilege-name" />
+      </cli:relation>
+    </adm:profile>
+  </adm:relation>
   <adm:relation name="root-dse-backend">
     <adm:one-to-one />
     <adm:profile name="ldap">
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootDNConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootDNConfiguration.xml
new file mode 100644
index 0000000..355b606
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RootDNConfiguration.xml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+! 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 2007 Sun Microsystems, Inc.
+! -->
+
+<adm:managed-object name="root-dn" plural-name="root-dns"
+package="org.opends.server.admin.std"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    This class defines the parent entry for all root DN users in the server.  It
+    also defines the set of privileges that root users will automatically
+    inherit.
+  </adm:synopsis>
+
+  <adm:tag name="core"/>
+
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:oid>1.3.6.1.4.1.26027.1.2.82</ldap:oid>
+      <ldap:name>ds-cfg-root-dn-base</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:relation name="root-dn-user">
+    <adm:one-to-many />
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence />
+      <ldap:naming-attribute>
+        cn
+      </ldap:naming-attribute>
+    </adm:profile>
+  </adm:relation>
+
+  <adm:property name="default-root-privilege-name" mandatory="false"
+    multi-valued="true">
+    <adm:synopsis>
+      Specifies the names of the privileges that root users will be granted by
+      default.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>bypass-acl</adm:value>
+        <adm:value>modify-acl</adm:value>
+        <adm:value>config-read</adm:value>
+        <adm:value>config-write</adm:value>
+        <adm:value>ldif-import</adm:value>
+        <adm:value>ldif-export</adm:value>
+        <adm:value>backend-backup</adm:value>
+        <adm:value>backend-restore</adm:value>
+        <adm:value>server-shutdown</adm:value>
+        <adm:value>server-restart</adm:value>
+        <adm:value>disconnect-client</adm:value>
+        <adm:value>cancel-request</adm:value>
+        <adm:value>password-reset</adm:value>
+        <adm:value>update-schema</adm:value>
+        <adm:value>privilege-change</adm:value>
+        <adm:value>unindexed-search</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="bypass-acl">
+          <adm:synopsis>
+            Allows the associated user to bypass access control checks performed
+            by the server.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="modify-acl">
+          <adm:synopsis>
+            Allows the associated user to modify the server's access control
+            configuration.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="config-read">
+          <adm:synopsis>
+            Allows the associated user to read the server configuration.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="config-write">
+          <adm:synopsis>
+            Allows the associated user to update the server configuration.  The
+            config-read privilege is also required.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="jmx-read">
+          <adm:synopsis>
+            Allows the associated user to perform JMX read operations.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="jmx-write">
+          <adm:synopsis>
+            Allows the associated user to perform JMX write operations.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="jmx-notify">
+          <adm:synopsis>
+            Allows the associated user to subscribe to receive JMX
+            notifications.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="ldif-import">
+          <adm:synopsis>
+            Allows the user to request that the server process LDIF import
+            tasks.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="ldif-export">
+          <adm:synopsis>
+            Allows the user to request that the server process LDIF export
+            tasks.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="backend-backup">
+          <adm:synopsis>
+            Allows the user to request that the server process backup tasks.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="backend-restore">
+          <adm:synopsis>
+            Allows the user to request that the server process restore tasks.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="server-shutdown">
+          <adm:synopsis>
+            Allows the user to request that the server shut down.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="server-restart">
+          <adm:synopsis>
+            Allows the user to request that the server perform an in-core
+            restart.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="proxied-auth">
+          <adm:synopsis>
+            Allows the user to use the proxied authorization control, or to
+            perform a bind that specifies an alternate authorization identity.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="disconnect-client">
+          <adm:synopsis>
+            Allows the user to terminate other client connections.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="cancel-request">
+          <adm:synopsis>
+            Allows the user to cancel operations in progress on other client
+            connections.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="password-reset">
+          <adm:synopsis>
+            Allows the user to reset user passwords.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="data-sync">
+          <adm:synopsis>
+            Allows the user to participate in data synchronization.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="update-schema">
+          <adm:synopsis>
+            Allows the user to make changes to the server schema.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="privilege-change">
+          <adm:synopsis>
+            Allows the user to make changes to the set of defined root
+            privileges, as well as to grant and revoke privileges for users.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="unindexed-search">
+          <adm:synopsis>
+            Allows the user to request that the server process a search that
+            cannot be optimized using server indexes.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.308</ldap:oid>
+        <ldap:name>ds-cfg-default-root-privilege-name</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+</adm:managed-object>
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootDNUserConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootDNUserConfiguration.xml
new file mode 100644
index 0000000..8b6bd90
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RootDNUserConfiguration.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+! 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 2007 Sun Microsystems, Inc.
+! -->
+
+<adm:managed-object name="root-dn-user" plural-name="root-dn-users"
+package="org.opends.server.admin.std"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    This class defines a root user, which is automatically granted a set of
+    privileges in the server and may be given some special abilities not
+    available to non-root users (e.g., the ability to bind to the server in
+    lockdown mode).
+  </adm:synopsis>
+
+  <adm:tag name="core"/>
+
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:oid>1.3.6.1.4.1.26027.1.2.41</ldap:oid>
+      <ldap:name>ds-cfg-root-dn</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:property name="alternate-bind-dn" mandatory="false" multi-valued="true">
+    <adm:synopsis>
+      Specifies one or more alternate DNs that may be used to bind to the server
+      as this root user.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          This root user will only be allowed to bind using the DN of the
+          associated configuration entry.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:dn />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.17</ldap:oid>
+        <ldap:name>ds-cfg-alternate-bind-dn</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+</adm:managed-object>
+
diff --git a/opends/src/server/org/opends/server/core/RootDNConfigManager.java b/opends/src/server/org/opends/server/core/RootDNConfigManager.java
index 1c11aef..3b4da18 100644
--- a/opends/src/server/org/opends/server/core/RootDNConfigManager.java
+++ b/opends/src/server/org/opends/server/core/RootDNConfigManager.java
@@ -29,73 +29,49 @@
 
 
 import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.opends.server.api.ConfigAddListener;
-import org.opends.server.api.ConfigChangeListener;
-import org.opends.server.api.ConfigDeleteListener;
-import org.opends.server.api.ConfigHandler;
-import org.opends.server.api.ConfigurableComponent;
-import org.opends.server.config.ConfigAttribute;
-import org.opends.server.config.ConfigEntry;
+import org.opends.server.admin.server.ConfigurationAddListener;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.admin.std.server.RootDNCfg;
+import org.opends.server.admin.std.server.RootDNUserCfg;
+import org.opends.server.admin.server.ServerManagementContext;
 import org.opends.server.config.ConfigException;
-import org.opends.server.config.DNConfigAttribute;
-import org.opends.server.config.MultiChoiceConfigAttribute;
 import org.opends.server.types.ConfigChangeResult;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.DN;
-import org.opends.server.types.ErrorLogCategory;
-import org.opends.server.types.ErrorLogSeverity;
 import org.opends.server.types.InitializationException;
 import org.opends.server.types.Privilege;
 import org.opends.server.types.ResultCode;
 
-import static org.opends.server.config.ConfigConstants.*;
-import org.opends.server.types.DebugLogLevel;
-import static org.opends.server.loggers.ErrorLogger.*;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
 import static org.opends.server.messages.ConfigMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
 
 
 
 /**
- * This class defines a utility that will be used to manage the set of root DNs
- * defined in the Directory Server.  The root DN accounts will exist below
- * "cn=Root DNs,cn=config" and must have the ds-cfg-root-dn objectclass.  They
- * may optionally have a ds-cfg-bind-dn that may be used to provide an alternate
- * DN for use when binding to the server.
+ * This class defines a utility that will be used to manage the set of root
+ * users defined in the Directory Server.  It will handle both the
+ * "cn=Root DNs,cn=config" entry itself (through the root privilege change
+ * listener), and all of its children.
  */
 public class RootDNConfigManager
-       implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener,
-                  ConfigurableComponent
+       implements ConfigurationChangeListener<RootDNUserCfg>,
+                  ConfigurationAddListener<RootDNUserCfg>,
+                  ConfigurationDeleteListener<RootDNUserCfg>
+
 {
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
+  // A mapping between the actual root DNs and their alternate bind DNs.
+  private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs;
 
-
-
-
-  // A mapping between each root DN user entry and a list of the alternate
-  // bind DNs for that user.
-  private ConcurrentHashMap<DN,List<DN>> bindMappings;
-
-  // The DN of the entry that serves as the base for the root DN
-  // configuration entries.
-  private DN rootDNConfigBaseDN;
-
-  // The set of privileges that will be automatically inherited by root users.
-  private LinkedHashSet<Privilege> rootPrivileges;
+  // The root privilege change listener that will handle changes to the
+  // "cn=Root DNs,cn=config" entry itself.
+  private RootPrivilegeChangeListener rootPrivilegeChangeListener;
 
 
 
@@ -104,560 +80,197 @@
    */
   public RootDNConfigManager()
   {
-    bindMappings = new ConcurrentHashMap<DN,List<DN>>();
+    alternateBindDNs = new ConcurrentHashMap<DN,HashSet<DN>>();
+    rootPrivilegeChangeListener = new RootPrivilegeChangeListener();
   }
 
 
 
   /**
-   * Initializes all root DNs currently defined in the Directory Server
-   * configuration.  This should only be called at Directory Server startup.
+   * Initializes all of the root users currently defined in the Directory Server
+   * configuration, as well as the set of privileges that root users will
+   * inherit by default.
    *
-   * @throws  ConfigException  If a configuration problem causes the root DN
-   *                           initialization process to fail.
+   * @throws  ConfigException  If a configuration problem causes the identity
+   *                           mapper initialization process to fail.
    *
    * @throws  InitializationException  If a problem occurs while initializing
-   *                                   the root DNs that is not related to the
-   *                                   server configuration.
+   *                                   the identity mappers that is not related
+   *                                   to the server configuration.
    */
   public void initializeRootDNs()
          throws ConfigException, InitializationException
   {
-    // First, get the base configuration entry for the root DNs.
-    ConfigHandler configHandler = DirectoryServer.getConfigHandler();
-    ConfigEntry   baseEntry;
-    try
+    // Get the root configuration object.
+    ServerManagementContext managementContext =
+         ServerManagementContext.getInstance();
+    RootCfg rootConfiguration =
+         managementContext.getRootConfiguration();
+
+
+    // Get the root DN configuration object, use it to set the default root
+    // privileges, and register a change listener for it.
+    RootDNCfg rootDNCfg = rootConfiguration.getRootDN();
+    rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg);
+    rootDNCfg.addChangeListener(rootPrivilegeChangeListener);
+
+
+    // Register as an add and delete listener for new root DN users.
+    rootDNCfg.addRootDNUserAddListener(this);
+    rootDNCfg.addRootDNUserDeleteListener(this);
+
+
+    // Get the set of root users defined below "cn=Root DNs,cn=config".  For
+    // each one, register as a change listener, and get the set of alternate
+    // bind DNs.
+    for (String name : rootDNCfg.listRootDNUsers())
     {
-      rootDNConfigBaseDN = DN.decode(DN_ROOT_DN_CONFIG_BASE);
-      baseEntry = configHandler.getConfigEntry(rootDNConfigBaseDN);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
+      RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name);
+      rootUserCfg.addChangeListener(this);
+      DirectoryServer.registerRootDN(rootUserCfg.dn());
+
+      HashSet<DN> altBindDNs = new HashSet<DN>();
+      for (DN alternateBindDN : rootUserCfg.getAlternateBindDN())
       {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      int    msgID   = MSGID_CONFIG_ROOTDN_CANNOT_GET_BASE;
-      String message = getMessage(msgID, String.valueOf(e));
-      throw new ConfigException(msgID, message, e);
-    }
-
-    if (baseEntry == null)
-    {
-      int    msgID   = MSGID_CONFIG_ROOTDN_BASE_DOES_NOT_EXIST;
-      String message = getMessage(msgID);
-      throw new ConfigException(msgID, message);
-    }
-
-
-    // Get the set of privileges that root users should have by default.
-    rootPrivileges = new LinkedHashSet<Privilege>(
-                              Privilege.getDefaultRootPrivileges());
-
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE;
-    MultiChoiceConfigAttribute rootPrivStub =
-         new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                        getMessage(msgID), false, true, false,
-                                        Privilege.getPrivilegeNames());
-    try
-    {
-      MultiChoiceConfigAttribute rootPrivAttr =
-           (MultiChoiceConfigAttribute)
-           baseEntry.getConfigAttribute(rootPrivStub);
-      if (rootPrivAttr != null)
-      {
-        ArrayList<Privilege> privList = new ArrayList<Privilege>();
-        for (String value : rootPrivAttr.activeValues())
+        try
         {
-          String privName = toLowerCase(value);
-          Privilege p = Privilege.privilegeForName(privName);
-          if (p == null)
-          {
-            msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE;
-            String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                        String.valueOf(rootDNConfigBaseDN),
-                                        String.valueOf(value));
-            logError(ErrorLogCategory.CONFIGURATION,
-                     ErrorLogSeverity.SEVERE_WARNING, message, msgID);
-          }
-          else
-          {
-            privList.add(p);
-          }
+          altBindDNs.add(alternateBindDN);
+          DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(),
+                                                  alternateBindDN);
         }
-
-        rootPrivileges = new LinkedHashSet<Privilege>(privList);
-      }
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES;
-      String message = getMessage(msgID, getExceptionMessage(e));
-      throw new InitializationException(msgID, message, e);
-    }
-
-
-    // Register with the configuration base entry as an add and delete listener.
-    // So that we will be notified of attempts to create or remove root DN
-    // entries.  Also, register with the server as a configurable component so
-    // that we can detect and apply any changes to the root
-    baseEntry.registerAddListener(this);
-    baseEntry.registerDeleteListener(this);
-    DirectoryServer.registerConfigurableComponent(this);
-
-
-    // See if the base entry has any children.  If not, then we don't need to
-    // do anything else.
-    if (! baseEntry.hasChildren())
-    {
-      return;
-    }
-
-
-    // Iterate through the child entries and process them as root DN entries.
-    for (ConfigEntry childEntry : baseEntry.getChildren().values())
-    {
-      StringBuilder unacceptableReason = new StringBuilder();
-      if (! configAddIsAcceptable(childEntry, unacceptableReason))
-      {
-        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
-                 MSGID_CONFIG_ROOTDN_ENTRY_UNACCEPTABLE,
-                 String.valueOf(childEntry.getDN()),
-                 unacceptableReason.toString());
-        continue;
-      }
-
-      try
-      {
-        ConfigChangeResult result = applyConfigurationAdd(childEntry);
-        if (result.getResultCode() != ResultCode.SUCCESS)
+        catch (DirectoryException de)
         {
-          StringBuilder buffer = new StringBuilder();
-
-          List<String> resultMessages = result.getMessages();
-          if ((resultMessages == null) || (resultMessages.isEmpty()))
-          {
-            buffer.append(getMessage(MSGID_CONFIG_UNKNOWN_UNACCEPTABLE_REASON));
-          }
-          else
-          {
-            Iterator<String> iterator = resultMessages.iterator();
-
-            buffer.append(iterator.next());
-            while (iterator.hasNext())
-            {
-              buffer.append(EOL);
-              buffer.append(iterator.next());
-            }
-          }
-
-          logError(ErrorLogCategory.CONFIGURATION,
-                   ErrorLogSeverity.SEVERE_ERROR,
-                   MSGID_CONFIG_ROOTDN_CANNOT_CREATE,
-                   String.valueOf(childEntry.getDN()), buffer.toString());
+          throw new InitializationException(de.getMessageID(),
+                                            de.getErrorMessage(), de);
         }
       }
-      catch (Exception e)
-      {
-        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
-                 MSGID_CONFIG_PWGENERATOR_CANNOT_CREATE_GENERATOR,
-                 childEntry.getDN().toString(), String.valueOf(e));
-      }
+
+      alternateBindDNs.put(rootUserCfg.dn(), altBindDNs);
     }
   }
 
 
 
   /**
-   * Retrieves the set of privileges that should automatically be granted to
-   * root users when they authenticate.
+   * Retrieves the set of privileges that will be granted to root users by
+   * default.
    *
-   * @return  The set of privileges that should automatically be granted to root
-   *          users when they authenticate.
+   * @return  The set of privileges that will be granted to root users by
+   *          default.
    */
   public Set<Privilege> getRootPrivileges()
   {
-    return rootPrivileges;
+    return rootPrivilegeChangeListener.getDefaultRootPrivileges();
   }
 
 
 
   /**
-   * Indicates whether the configuration entry that will result from a proposed
-   * modification is acceptable to this change listener.
-   *
-   * @param  configEntry         The configuration entry that will result from
-   *                             the requested update.
-   * @param  unacceptableReason  A buffer to which this method can append a
-   *                             human-readable message explaining why the
-   *                             proposed change is not acceptable.
-   *
-   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
-   *          configuration, or <CODE>false</CODE> if it does not.
+   * {@inheritDoc}
    */
-  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
-                                          StringBuilder unacceptableReason)
+  public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration,
+                                              List<String> unacceptableReasons)
   {
-    // Make sure that the entry has an appropriate objectclass for a root DN.
-    if (! configEntry.hasObjectClass(OC_ROOT_DN))
+    // The new root user must not have an alternate bind DN that is already
+    // in use.
+    boolean configAcceptable = true;
+    for (DN altBindDN : configuration.getAlternateBindDN())
     {
-      int    msgID   = MSGID_CONFIG_ROOTDN_INVALID_OBJECTCLASS;
-      String message = getMessage(msgID, configEntry.getDN().toString());
-      unacceptableReason.append(message);
-      return false;
-    }
-
-
-    // See if the entry has any alternate DNs.  If so, then make sure they are
-    // valid.
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN;
-    DNConfigAttribute alternateDNsStub =
-         new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID),
-                               false, true, false);
-    try
-    {
-      DNConfigAttribute alternateDNsAttr =
-           (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub);
-
-      if (alternateDNsAttr != null)
+      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
+      if (existingRootDN != null)
       {
-        // There were alternate DNs provided, so see if there are any duplicate
-        // values that are already registered for a different root DN.
-        for (DN alternateBindDN : alternateDNsAttr.pendingValues())
-        {
-          DN rootDN = DirectoryServer.getActualRootBindDN(alternateBindDN);
-          if ((rootDN != null) && (! rootDN.equals(configEntry.getDN())))
-          {
-            msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING;
-            String message = getMessage(msgID, String.valueOf(alternateBindDN),
-                                        String.valueOf(configEntry.getDN()),
-                                        String.valueOf(rootDN));
-            unacceptableReason.append(message);
-            return false;
-          }
-        }
+        int    msgID   = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING;
+        String message = getMessage(msgID, String.valueOf(altBindDN),
+                                    String.valueOf(configuration.dn()),
+                                    String.valueOf(existingRootDN));
+        unacceptableReasons.add(message);
+
+        configAcceptable = false;
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
 
-      msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS;
-      String message = getMessage(msgID, String.valueOf(configEntry.getDN()),
-                                  getExceptionMessage(e));
-      unacceptableReason.append(message);
-      return false;
-    }
-
-
-    // If we've gotten here then the root DN entry appears to be acceptable.
-    return true;
+    return configAcceptable;
   }
 
 
 
   /**
-   * Attempts to apply a new configuration to this Directory Server component
-   * based on the provided changed entry.
-   *
-   * @param  configEntry  The configuration entry that containing the updated
-   *                      configuration for this component.
-   *
-   * @return  Information about the result of processing the configuration
-   *          change.
+   * {@inheritDoc}
    */
-  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry)
+  public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration)
   {
-    DN                configEntryDN       = configEntry.getDN();
+    configuration.addChangeListener(this);
+
     ResultCode        resultCode          = ResultCode.SUCCESS;
     boolean           adminActionRequired = false;
     ArrayList<String> messages            = new ArrayList<String>();
 
-
-    // Get the set of alternate bind DNs for the entry, if there are any.
-    List<DN> alternateBindDNs = null;
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN;
-    DNConfigAttribute alternateDNsStub =
-         new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID),
-                               false, true, false);
-    try
+    HashSet<DN> altBindDNs = new HashSet<DN>();
+    for (DN altBindDN : configuration.getAlternateBindDN())
     {
-      DNConfigAttribute alternateDNsAttr =
-           (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub);
-      if (alternateDNsAttr != null)
+      try
       {
-        alternateBindDNs = alternateDNsAttr.activeValues();
+        DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN);
+        altBindDNs.add(altBindDN);
+      }
+      catch (DirectoryException de)
+      {
+        // This shouldn't happen, since the set of DNs should have already been
+        // validated.
+        resultCode = DirectoryServer.getServerErrorResultCode();
+        messages.add(de.getErrorMessage());
+
+        for (DN dn : altBindDNs)
+        {
+          DirectoryServer.deregisterAlternateRootBindDN(dn);
+        }
+        break;
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS;
-      messages.add(getMessage(msgID, getExceptionMessage(e)));
-
-      resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
-    }
-
 
     if (resultCode == ResultCode.SUCCESS)
     {
-      List<DN> existingMappings = bindMappings.get(configEntryDN);
-      if (existingMappings != null)
-      {
-        for (DN mappedDN : existingMappings)
-        {
-          if ((alternateBindDNs == null) ||
-              (! alternateBindDNs.contains(mappedDN)))
-          {
-            DirectoryServer.deregisterAlternateRootBindDN(mappedDN);
-          }
-        }
-      }
-
-      if (alternateBindDNs == null)
-      {
-        alternateBindDNs = new ArrayList<DN>(0);
-      }
-      else
-      {
-        for (DN alternateBindDN : alternateBindDNs)
-        {
-          try
-          {
-            DirectoryServer.registerAlternateRootDN(configEntryDN,
-                                                    alternateBindDN);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            msgID = MSGID_CONFIG_ROOTDN_CANNOT_REGISTER_ALTERNATE_BIND_DN;
-            messages.add(getMessage(msgID, String.valueOf(alternateBindDN),
-                                    String.valueOf(configEntryDN),
-                                    de.getErrorMessage()));
-
-            if (resultCode == ResultCode.SUCCESS)
-            {
-              resultCode = ResultCode.CONSTRAINT_VIOLATION;
-            }
-          }
-        }
-      }
-
-      bindMappings.put(configEntryDN, alternateBindDNs);
+      DirectoryServer.registerRootDN(configuration.dn());
+      alternateBindDNs.put(configuration.dn(), altBindDNs);
     }
 
-
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
 
 
 
   /**
-   * Indicates whether the configuration entry that will result from a proposed
-   * add is acceptable to this add listener.
-   *
-   * @param  configEntry         The configuration entry that will result from
-   *                             the requested add.
-   * @param  unacceptableReason  A buffer to which this method can append a
-   *                             human-readable message explaining why the
-   *                             proposed entry is not acceptable.
-   *
-   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
-   *          configuration, or <CODE>false</CODE> if it does not.
+   * {@inheritDoc}
    */
-  public boolean configAddIsAcceptable(ConfigEntry configEntry,
-                                       StringBuilder unacceptableReason)
+  public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration,
+                      List<String> unacceptableReasons)
   {
-    // Make sure that no entry already exists with the specified DN, and that
-    // there is no other root user with a conflicting alternate bind DN.
-    DN configEntryDN = configEntry.getDN();
-    if (bindMappings.containsKey(configEntryDN) ||
-        (DirectoryServer.getActualRootBindDN(configEntryDN) != null))
-    {
-      int    msgID   = MSGID_CONFIG_ROOTDN_EXISTS;
-      String message = getMessage(msgID, String.valueOf(configEntryDN));
-      unacceptableReason.append(message);
-      return false;
-    }
-
-
-    // Make sure that the entry has the root DN objectclass.
-    if (! configEntry.hasObjectClass(OC_ROOT_DN))
-    {
-      int    msgID   = MSGID_CONFIG_ROOTDN_INVALID_OBJECTCLASS;
-      String message = getMessage(msgID, configEntryDN.toString());
-      unacceptableReason.append(message);
-      return false;
-    }
-
-
-
-
-    // See if the entry has any alternate DNs.  If so, then make sure they are
-    // valid.
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN;
-    DNConfigAttribute alternateDNsStub =
-         new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID),
-                               false, true, false);
-    try
-    {
-      DNConfigAttribute alternateDNsAttr =
-           (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub);
-
-      if (alternateDNsAttr != null)
-      {
-        // There were alternate DNs provided, so see if there are any duplicate
-        // values that are already registered for a different root DN.
-        for (DN alternateBindDN : alternateDNsAttr.pendingValues())
-        {
-          DN rootDN = DirectoryServer.getActualRootBindDN(alternateBindDN);
-          if ((rootDN != null) && (! rootDN.equals(configEntryDN)))
-          {
-            msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING;
-            String message = getMessage(msgID, String.valueOf(alternateBindDN),
-                                        String.valueOf(configEntryDN),
-                                        String.valueOf(rootDN));
-            unacceptableReason.append(message);
-            return false;
-          }
-        }
-      }
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS;
-      String message = getMessage(msgID, getExceptionMessage(e));
-      unacceptableReason.append(message);
-      return false;
-    }
-
-
-    // If we've gotten here then the root DN entry appears to be acceptable.
     return true;
   }
 
 
 
   /**
-   * Attempts to apply a new configuration based on the provided added entry.
-   *
-   * @param  configEntry  The new configuration entry that contains the
-   *                      configuration to apply.
-   *
-   * @return  Information about the result of processing the configuration
-   *          change.
+   * {@inheritDoc}
    */
-  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
+  public ConfigChangeResult applyConfigurationDelete(
+                                 RootDNUserCfg configuration)
   {
-    DN                configEntryDN       = configEntry.getDN();
+    DirectoryServer.deregisterRootDN(configuration.dn());
+    configuration.removeChangeListener(this);
+
     ResultCode        resultCode          = ResultCode.SUCCESS;
     boolean           adminActionRequired = false;
     ArrayList<String> messages            = new ArrayList<String>();
 
-
-    // Get the set of alternate bind DNs for the entry, if there are any.
-    List<DN> alternateBindDNs = null;
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN;
-    DNConfigAttribute alternateDNsStub =
-         new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID),
-                               false, true, false);
-    try
+    HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn());
+    if (altBindDNs != null)
     {
-      DNConfigAttribute alternateDNsAttr =
-           (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub);
-      if (alternateDNsAttr != null)
+      for (DN dn : altBindDNs)
       {
-        alternateBindDNs = alternateDNsAttr.activeValues();
+        DirectoryServer.deregisterAlternateRootBindDN(dn);
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS;
-      messages.add(getMessage(msgID, getExceptionMessage(e)));
-
-      resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
-    }
-
-
-    if (resultCode == ResultCode.SUCCESS)
-    {
-      List<DN> existingMappings = bindMappings.get(configEntryDN);
-      if (existingMappings != null)
-      {
-        for (DN mappedDN : existingMappings)
-        {
-          if ((alternateBindDNs == null) ||
-              (! alternateBindDNs.contains(mappedDN)))
-          {
-            DirectoryServer.deregisterAlternateRootBindDN(mappedDN);
-          }
-        }
-      }
-
-      if (alternateBindDNs == null)
-      {
-        alternateBindDNs = new ArrayList<DN>(0);
-      }
-      else
-      {
-        for (DN alternateBindDN : alternateBindDNs)
-        {
-          try
-          {
-            DirectoryServer.registerAlternateRootDN(configEntryDN,
-                                                    alternateBindDN);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            msgID = MSGID_CONFIG_ROOTDN_CANNOT_REGISTER_ALTERNATE_BIND_DN;
-            messages.add(getMessage(msgID, String.valueOf(alternateBindDN),
-                                    String.valueOf(configEntryDN)));
-
-            if (resultCode == ResultCode.SUCCESS)
-            {
-              resultCode = ResultCode.CONSTRAINT_VIOLATION;
-            }
-          }
-        }
-      }
-
-      bindMappings.put(configEntryDN, alternateBindDNs);
-      DirectoryServer.registerRootDN(configEntryDN);
-      configEntry.registerChangeListener(this);
-    }
-
 
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
@@ -665,267 +278,107 @@
 
 
   /**
-   * Indicates whether it is acceptable to remove the provided configuration
-   * entry.
-   *
-   * @param  configEntry         The configuration entry that will be removed
-   *                             from the configuration.
-   * @param  unacceptableReason  A buffer to which this method can append a
-   *                             human-readable message explaining why the
-   *                             proposed delete is not acceptable.
-   *
-   * @return  <CODE>true</CODE> if the proposed entry may be removed from the
-   *          configuration, or <CODE>false</CODE> if not.
+   * {@inheritDoc}
    */
-  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
-                                          StringBuilder unacceptableReason)
+  public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration,
+                      List<String> unacceptableReasons)
   {
-    // A delete should always be acceptable, so just return true.
-    return true;
-  }
+    boolean configAcceptable = true;
 
-
-
-  /**
-   * Attempts to apply a new configuration based on the provided deleted entry.
-   *
-   * @param  configEntry  The new configuration entry that has been deleted.
-   *
-   * @return  Information about the result of processing the configuration
-   *          change.
-   */
-  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
-  {
-    DN         configEntryDN       = configEntry.getDN();
-    ResultCode resultCode          = ResultCode.SUCCESS;
-    boolean    adminActionRequired = false;
-
-
-    // See if the entry is registered as a root DN.  If so, then deregister it
-    // and any alternate bind DNs that it might have.
-    List<DN> alternateBindDNs = bindMappings.remove(configEntryDN);
-    if (alternateBindDNs != null)
+    // There must not be any new alternate bind DNs that are already in use by
+    // other root users.
+    for (DN altBindDN: configuration.getAlternateBindDN())
     {
-      for (DN alternateBindDN : alternateBindDNs)
+      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
+      if ((existingRootDN != null) &&
+          (! existingRootDN.equals(configuration.dn())))
       {
-        DirectoryServer.deregisterAlternateRootBindDN(alternateBindDN);
+        int    msgID   = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING;
+        String message = getMessage(msgID, String.valueOf(altBindDN),
+                                    String.valueOf(configuration.dn()),
+                                    String.valueOf(existingRootDN));
+        unacceptableReasons.add(message);
+
+        configAcceptable = false;
       }
     }
 
-
-    return new ConfigChangeResult(resultCode, adminActionRequired);
+    return configAcceptable;
   }
 
 
 
   /**
-   * 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.
+   * {@inheritDoc}
    */
-  public DN getConfigurableComponentEntryDN()
-  {
-    return rootDNConfigBaseDN;
-  }
-
-
-
-  /**
-   * 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()
-  {
-    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
-
-    LinkedList<String> currentValues = new LinkedList<String>();
-    for (Privilege p : rootPrivileges)
-    {
-      currentValues.add(p.getName());
-    }
-
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE;
-    attrList.add(new MultiChoiceConfigAttribute(
-                          ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID),
-                          false, true, false, Privilege.getPrivilegeNames(),
-                          currentValues));
-
-    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)
-  {
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE;
-    MultiChoiceConfigAttribute rootPrivStub =
-         new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                        getMessage(msgID), false, true, false,
-                                        Privilege.getPrivilegeNames());
-    try
-    {
-      MultiChoiceConfigAttribute rootPrivAttr =
-           (MultiChoiceConfigAttribute)
-           configEntry.getConfigAttribute(rootPrivStub);
-      if (rootPrivAttr != null)
-      {
-        for (String value : rootPrivAttr.activeValues())
-        {
-          String privName = toLowerCase(value);
-          Privilege p = Privilege.privilegeForName(privName);
-          if (p == null)
-          {
-            msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE;
-            String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                        String.valueOf(rootDNConfigBaseDN),
-                                        String.valueOf(value));
-            unacceptableReasons.add(message);
-            return false;
-          }
-        }
-      }
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES;
-      String message = getMessage(msgID, getExceptionMessage(e));
-      unacceptableReasons.add(message);
-      return false;
-    }
-
-
-    // If we've gotten here, then everything looks OK.
-    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)
+  public ConfigChangeResult applyConfigurationChange(
+                                 RootDNUserCfg configuration)
   {
     ResultCode        resultCode          = ResultCode.SUCCESS;
-    ArrayList<String> messages            = new ArrayList<String>();
     boolean           adminActionRequired = false;
+    ArrayList<String> messages            = new ArrayList<String>();
 
+    HashSet<DN> setDNs = new HashSet<DN>();
+    HashSet<DN> addDNs = new HashSet<DN>();
+    HashSet<DN> delDNs =
+         new HashSet<DN>(alternateBindDNs.get(configuration.dn()));
 
-    LinkedHashSet<Privilege> newRootPrivileges =
-         new LinkedHashSet<Privilege>(Privilege.getDefaultRootPrivileges());
-
-    int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE;
-    MultiChoiceConfigAttribute rootPrivStub =
-         new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                        getMessage(msgID), false, true, false,
-                                        Privilege.getPrivilegeNames());
-    try
+    for (DN altBindDN : configuration.getAlternateBindDN())
     {
-      MultiChoiceConfigAttribute rootPrivAttr =
-           (MultiChoiceConfigAttribute)
-           configEntry.getConfigAttribute(rootPrivStub);
-      if (rootPrivAttr != null)
-      {
-        ArrayList<Privilege> privList = new ArrayList<Privilege>();
-        for (String value : rootPrivAttr.activeValues())
-        {
-          String privName = toLowerCase(value);
-          Privilege p = Privilege.privilegeForName(privName);
-          if (p == null)
-          {
-            if (resultCode == ResultCode.SUCCESS)
-            {
-              resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
-            }
+      setDNs.add(altBindDN);
 
-            msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE;
-            messages.add(getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME,
-                                    String.valueOf(rootDNConfigBaseDN),
-                                    String.valueOf(value)));
-          }
-          else
-          {
-            privList.add(p);
-          }
+      if (! delDNs.remove(altBindDN))
+      {
+        addDNs.add(altBindDN);
+      }
+    }
+
+    for (DN dn : delDNs)
+    {
+      DirectoryServer.deregisterAlternateRootBindDN(dn);
+    }
+
+    HashSet<DN> addedDNs = new HashSet<DN>(addDNs.size());
+    for (DN dn : addDNs)
+    {
+      try
+      {
+        DirectoryServer.registerAlternateRootDN(configuration.dn(), dn);
+        addedDNs.add(dn);
+      }
+      catch (DirectoryException de)
+      {
+        // This shouldn't happen, since the set of DNs should have already been
+        // validated.
+        resultCode = DirectoryServer.getServerErrorResultCode();
+        messages.add(de.getErrorMessage());
+
+        for (DN addedDN : addedDNs)
+        {
+          DirectoryServer.deregisterAlternateRootBindDN(addedDN);
         }
 
-        newRootPrivileges = new LinkedHashSet<Privilege>(privList);
+        for (DN deletedDN : delDNs)
+        {
+          try
+          {
+            DirectoryServer.registerAlternateRootDN(configuration.dn(),
+                                                    deletedDN);
+          }
+          catch (Exception e)
+          {
+            // This should also never happen.
+            alternateBindDNs.get(configuration.dn()).remove(deletedDN);
+          }
+        }
       }
     }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      if (resultCode == ResultCode.SUCCESS)
-      {
-        resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
-      }
-
-      msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES;
-      messages.add(getMessage(msgID, getExceptionMessage(e)));
-    }
-
 
     if (resultCode == ResultCode.SUCCESS)
     {
-      rootPrivileges = newRootPrivileges;
-
-      if (detailedResults)
-      {
-        msgID = MSGID_CONFIG_ROOTDN_UPDATED_PRIVILEGES;
-        messages.add(getMessage(msgID));
-      }
+      alternateBindDNs.put(configuration.dn(), setDNs);
     }
 
-
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
 }
diff --git a/opends/src/server/org/opends/server/core/RootPrivilegeChangeListener.java b/opends/src/server/org/opends/server/core/RootPrivilegeChangeListener.java
new file mode 100644
index 0000000..b2cbe40
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/RootPrivilegeChangeListener.java
@@ -0,0 +1,190 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.RootDNCfgDefn;
+import org.opends.server.admin.std.server.RootDNCfg;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.ResultCode;
+
+
+
+/**
+ * This class defines a data structure that is used to handle changes to the set
+ * of default root privileges.
+ */
+public class RootPrivilegeChangeListener
+       implements ConfigurationChangeListener<RootDNCfg>
+{
+  // The set of privileges that will be given to root users by default.
+  private Set<Privilege> defaultRootPrivileges;
+
+
+
+  /**
+   * Creates a new instance of this root privilege change listener.
+   */
+  public RootPrivilegeChangeListener()
+  {
+    defaultRootPrivileges = Privilege.getDefaultRootPrivileges();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(RootDNCfg configuration,
+                      List<String> unacceptableReasons)
+  {
+    // No special validation is required.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(RootDNCfg configuration)
+  {
+    setDefaultRootPrivileges(configuration);
+    return new ConfigChangeResult(ResultCode.SUCCESS, false);
+  }
+
+
+
+  /**
+   * Retrieves the set of privileges that will be automatically granted to root
+   * users.
+   *
+   * @return  The set of privileges that will be automatically granted to root
+   *          users.
+   */
+  public Set<Privilege> getDefaultRootPrivileges()
+  {
+    return defaultRootPrivileges;
+  }
+
+
+
+  /**
+   * Specifies the set of privileges that will be automatically granted to root
+   * users.
+   *
+   * @param  configuration  The configuration object that specifies the set of
+   *                        privileges that will be automatically granted to
+   *                        root users.
+   */
+  void setDefaultRootPrivileges(RootDNCfg configuration)
+  {
+    Set<RootDNCfgDefn.DefaultRootPrivilegeName> configPrivSet =
+         configuration.getDefaultRootPrivilegeName();
+
+    HashSet<Privilege> privSet = new HashSet<Privilege>(configPrivSet.size());
+    for (RootDNCfgDefn.DefaultRootPrivilegeName p : configPrivSet)
+    {
+      switch (p)
+      {
+        case BYPASS_ACL:
+          privSet.add(Privilege.BYPASS_ACL);
+          break;
+        case MODIFY_ACL:
+          privSet.add(Privilege.MODIFY_ACL);
+          break;
+        case CONFIG_READ:
+          privSet.add(Privilege.CONFIG_READ);
+          break;
+        case CONFIG_WRITE:
+          privSet.add(Privilege.CONFIG_WRITE);
+          break;
+        case JMX_READ:
+          privSet.add(Privilege.JMX_READ);
+          break;
+        case JMX_WRITE:
+          privSet.add(Privilege.JMX_WRITE);
+          break;
+        case JMX_NOTIFY:
+          privSet.add(Privilege.JMX_NOTIFY);
+          break;
+        case LDIF_IMPORT:
+          privSet.add(Privilege.LDIF_IMPORT);
+          break;
+        case LDIF_EXPORT:
+          privSet.add(Privilege.LDIF_EXPORT);
+          break;
+        case BACKEND_BACKUP:
+          privSet.add(Privilege.BACKEND_BACKUP);
+          break;
+        case BACKEND_RESTORE:
+          privSet.add(Privilege.BACKEND_RESTORE);
+          break;
+        case SERVER_SHUTDOWN:
+          privSet.add(Privilege.SERVER_SHUTDOWN);
+          break;
+        case SERVER_RESTART:
+          privSet.add(Privilege.SERVER_RESTART);
+          break;
+        case PROXIED_AUTH:
+          privSet.add(Privilege.PROXIED_AUTH);
+          break;
+        case DISCONNECT_CLIENT:
+          privSet.add(Privilege.DISCONNECT_CLIENT);
+          break;
+        case CANCEL_REQUEST:
+          privSet.add(Privilege.CANCEL_REQUEST);
+          break;
+        case PASSWORD_RESET:
+          privSet.add(Privilege.PASSWORD_RESET);
+          break;
+        case DATA_SYNC:
+          privSet.add(Privilege.DATA_SYNC);
+          break;
+        case UPDATE_SCHEMA:
+          privSet.add(Privilege.UPDATE_SCHEMA);
+          break;
+        case PRIVILEGE_CHANGE:
+          privSet.add(Privilege.PRIVILEGE_CHANGE);
+          break;
+        case UNINDEXED_SEARCH:
+          privSet.add(Privilege.UNINDEXED_SEARCH);
+          break;
+      }
+    }
+
+    defaultRootPrivileges = privSet;
+  }
+}
+

--
Gitblit v1.10.0