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

Jean-Noël Rouvignac
06.09.2016 10f0e9c1ad2e1296678c3706152a84437da35a78
Fix uninstall tool + add upgrade task for removing configuration backend

The uninstall tool was complaining about the ConfigurationBackend being
registered twice within the DirectoryServer.
It happened because the ConfigurationBackend is now registered by
DirectoryServer.initializeConfiguration().

This commit changes that by splitting ConfigurationHandler creation and
ConfigurationBackend registration.
The ConfigurationBackend is not initialized by the BackendConfigManager
like any other backends.

This commit also adds an update task that removes the config entry for
the ConfigurationBackend in config.ldif.

This commit also removes the coupling of sub entry manager, group
manager and ACI sub system towards the configuration backend.
There is no longer any custom code for this and all is handled via the
BackendInitializationListener.

This commit also removes a lot of duplicated code in the
BackendConfigManager.

Finally, this commit also does some code cleanup to make things simpler
(or not).

DirectoryServer.java:
In initializeConfiguration(), do not create and register the
ConfigurationBackend. Moved this resposibility to the
BackendConfigManager.

BackendConfigManager.java:
In initializeBackendConfig(), now call initializeConfigurationBackend().
Big refactoring to reduce code duplkication:
- rename initializeBackend() to configureAndOpenBackend()
- extracted initializeBackend(), registerBackend(), deregisterBackend()
methods
- inilined onBackendPreInitialization(), onBackendPostInitialization()
methods
- reused releaseSharedLock()

ConfigFileHandlerBackendConfiguration.xml: REMOVED
It was already unused after the switch to the new config has removed the
entry for the configuration backend in config.ldif.

Upgrade.java, tool.properties:
Added a task to remove the config entry for the configuration backend
1 files deleted
7 files modified
773 ■■■■■ changed files
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ConfigFileHandlerBackendConfiguration.xml 52 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java 143 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciListenerManager.java 120 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java 342 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/ConfigurationBackend.java 89 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java 21 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/tool.properties 2 ●●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ConfigFileHandlerBackendConfiguration.xml
File was deleted
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -22,7 +22,6 @@
import java.util.SortedSet;
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
@@ -33,23 +32,17 @@
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.RDN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.server.config.server.DseeCompatAccessControlHandlerCfg;
import org.opends.server.api.AccessControlHandler;
import org.opends.server.api.Backend;
import org.opends.server.api.ClientConnection;
import org.opends.server.backends.pluggable.SuffixContainer;
import org.opends.server.controls.GetEffectiveRightsRequestControl;
import org.opends.server.core.BindOperation;
import org.opends.server.core.ConfigurationBackend;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.SearchRequest;
import org.opends.server.protocols.ldap.LDAPControl;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeBuilder;
@@ -68,22 +61,17 @@
import org.opends.server.workflowelement.localbackend.LocalBackendCompareOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendDeleteOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendModifyOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation;
import static org.opends.messages.AccessControlMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.authorization.dseecompat.EnumEvalReason.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.core.DirectoryServer.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.protocols.internal.Requests.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * The AciHandler class performs the main processing for the dseecompat package.
 */
/** The AciHandler class performs the main processing for the dseecompat package. */
public final class AciHandler extends
    AccessControlHandler<DseeCompatAccessControlHandlerCfg>
{
@@ -129,8 +117,6 @@
    initStatics();
  }
  /**
   * We initialize these for each new AciHandler so that we can clear out the
   * stale references that can occur during an in-core restart.
@@ -168,7 +154,6 @@
    // the intializeAccessControlHandler method.
  }
  /** {@inheritDoc} */
  @Override
  public void filterEntry(Operation operation,
      SearchResultEntry unfilteredEntry, SearchResultEntry filteredEntry)
@@ -194,7 +179,6 @@
    }
  }
  /** {@inheritDoc} */
  @Override
  public void finalizeAccessControlHandler()
  {
@@ -203,7 +187,6 @@
    DirectoryServer.deregisterSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
  }
  /** {@inheritDoc} */
  @Override
  public void initializeAccessControlHandler(
      DseeCompatAccessControlHandlerCfg configuration)
@@ -214,11 +197,9 @@
    aciList = new AciList(configurationDN);
    aciListenerMgr = new AciListenerManager(aciList, configurationDN);
    processGlobalAcis(configuration);
    processConfigAcis();
    DirectoryServer.registerSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(DN entryDN, Operation op, Control control)
      throws DirectoryException
@@ -257,7 +238,6 @@
    return true;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(ExtendedOperation operation)
  {
@@ -272,7 +252,6 @@
    return accessAllowed(container);
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(LocalBackendAddOperation operation)
      throws DirectoryException
@@ -284,7 +263,6 @@
        && verifySyntax(operation.getEntryToAdd(), operation, container.getClientDN());
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(BindOperation bindOperation)
  {
@@ -292,8 +270,6 @@
    return true;
  }
  /**
   * Check access on compare operations. Note that the attribute type is
   * unavailable at this time, so this method partially parses the raw
@@ -328,8 +304,6 @@
    return isAllowed(container, operation);
  }
  /**
   * Check access on delete operations.
   *
@@ -345,8 +319,6 @@
    return isAllowed(container, operation);
  }
  /**
   * Checks access on a modifyDN operation.
   *
@@ -394,7 +366,6 @@
    return rdnChangesAllowed;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(LocalBackendModifyOperation operation)
      throws DirectoryException
@@ -403,7 +374,6 @@
    return aciCheckMods(container, operation, skipAccessCheck(operation));
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(SearchOperation searchOperation)
  {
@@ -411,7 +381,6 @@
    return true;
  }
  /** {@inheritDoc} */
  @Override
  public boolean isAllowed(Operation operation, Entry entry,
      SearchFilter filter) throws DirectoryException
@@ -426,7 +395,6 @@
    return testFilter(container, filter);
  }
  /** {@inheritDoc} */
  @Override
  public boolean mayProxy(Entry proxyUser, Entry proxiedUser, Operation op)
  {
@@ -443,7 +411,6 @@
    return accessAllowedEntry(container);
  }
  /** {@inheritDoc} */
  @Override
  public boolean maySend(DN dn, Operation operation, SearchResultReference reference)
  {
@@ -465,7 +432,6 @@
    return accessAllowed(container);
  }
  /** {@inheritDoc} */
  @Override
  public boolean maySend(Operation operation, SearchResultEntry entry)
  {
@@ -513,8 +479,6 @@
    return true;
  }
  /**
   * Check access using the specified container. This container will
   * have all of the information to gather applicable ACIs and perform
@@ -579,8 +543,6 @@
    return ret;
  }
  /*
   * TODO Evaluate performance of this method. TODO Evaluate security
   * concerns of this method. Logic from this method taken almost
@@ -642,8 +604,6 @@
    return false;
  }
  /**
   * Performs an access check against all of the attributes of an entry. The
   * attributes that fail access are removed from the entry. This method
@@ -676,8 +636,6 @@
    }
  }
  /**
   * Checks to see if a LDAP modification is allowed access.
   *
@@ -708,8 +666,7 @@
      if (modAttrType.equals(aciType)
          /*
           * Check that the operation has modify privileges if it contains
           * an "aci" attribute type.
           * Check that the operation has modify privileges if it contains an "aci" attribute type.
           */
          && !operation.getClientConnection().hasPrivilege(
              Privilege.MODIFY_ACL, operation))
@@ -725,8 +682,8 @@
              || modType == ModificationType.REPLACE
              || modType == ModificationType.INCREMENT)
          /*
           * Check if we have rights to delete all values of an attribute
           * type in the resource entry.
           * Check if we have rights to delete all values of an attribute type in the resource
           * entry.
           */
          && resourceEntry.hasAttribute(modAttrType))
      {
@@ -816,8 +773,6 @@
    return true;
  }
  /**
   * Perform all needed RDN checks for the modifyDN operation. The old RDN is
   * not equal to the new RDN. The access checks are:
@@ -860,8 +815,6 @@
    return ret;
  }
  /**
   * Check access on the new superior entry if it exists. If superiordn is null,
   * the entry does not exist or the DN cannot be locked then false is returned.
@@ -891,8 +844,6 @@
    }
  }
  /**
   * Check access on each attribute-value pair component of the
   * specified RDN. There may be more than one attribute-value pair if
@@ -922,8 +873,6 @@
    return true;
  }
  /**
   * Creates the allow and deny ACI lists based on the provided target
   * match context. These lists are stored in the evaluation context.
@@ -960,8 +909,6 @@
    targetMatchCtx.setDenyList(denys);
  }
  /**
   * Gathers all of the attribute types in an entry along with the
   * "objectclass" attribute type in a List. The "objectclass" attribute
@@ -988,8 +935,6 @@
    return typeList;
  }
  /**
   * Check access using the accessAllowed method. The LDAP add, compare,
   * modify and delete operations use this function. The other supported
@@ -1021,70 +966,6 @@
    return SYNTAX_DN_OID.equals(attribute.getSyntax().getOID());
  }
  /**
   * Process all ACIs under the "cn=config" naming context and adds them
   * to the ACI list cache. It also logs messages about the number of
   * ACIs added to the cache. This method is called once at startup. It
   * will put the server in lockdown mode if needed.
   *
   * @throws InitializationException
   *           If there is an error searching for the ACIs in the naming
   *           context.
   */
  private void processConfigAcis() throws InitializationException
  {
    LinkedList<LocalizableMessage> failedACIMsgs = new LinkedList<>();
    InternalClientConnection conn = getRootConnection();
    Backend<?> configBackend = DirectoryServer.getBackend(ConfigurationBackend.CONFIG_BACKEND_ID);
    for (DN baseDN : configBackend.getBaseDNs())
    {
      try
      {
        if (! configBackend.entryExists(baseDN))
        {
          continue;
        }
      }
      catch (Exception e)
      {
        logger.traceException(e);
        // FIXME -- Is there anything that we need to do here?
        continue;
      }
      try {
        SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, "aci=*").addAttribute("aci");
        InternalSearchOperation internalSearch =
            new InternalSearchOperation(conn, nextOperationID(), nextMessageID(), request);
        LocalBackendSearchOperation localSearch = new LocalBackendSearchOperation(internalSearch);
        configBackend.search(localSearch);
        if (!internalSearch.getSearchEntries().isEmpty())
        {
          int validAcis =
              aciList.addAci(internalSearch.getSearchEntries(), failedACIMsgs);
          if (!failedACIMsgs.isEmpty())
          {
            aciListenerMgr.logMsgsSetLockDownMode(failedACIMsgs);
          }
          logger.debug(INFO_ACI_ADD_LIST_ACIS, validAcis, baseDN);
        }
      }
      catch (Exception e)
      {
        LocalizableMessage message = INFO_ACI_HANDLER_FAIL_PROCESS_ACI.get();
        throw new InitializationException(message, e);
      }
    }
  }
  /**
   * Process all global ACI attribute types found in the configuration
   * entry and adds them to that ACI list cache. It also logs messages
@@ -1105,7 +986,7 @@
  {
    try
    {
      final SortedSet<Aci> globalAcis = new TreeSet<Aci>();
      final SortedSet<Aci> globalAcis = new TreeSet<>();
      for (String value : configuration.getGlobalACI())
      {
        globalAcis.add(Aci.decode(ByteString.valueOfUtf8(value), DN.rootDN()));
@@ -1124,8 +1005,6 @@
    }
  }
  /**
   * Check to see if the specified entry has the specified privilege.
   *
@@ -1139,8 +1018,6 @@
    return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
  }
  /**
   * Check to see if the client entry has BYPASS_ACL privileges for this
   * operation.
@@ -1156,8 +1033,6 @@
        Privilege.BYPASS_ACL, operation);
  }
  /**
   * Performs the test of the deny and allow access lists using the
   * provided evaluation context. The deny list is checked first.
@@ -1185,12 +1060,12 @@
      final EnumEvalResult res = Aci.evaluate(evalCtx, denyAci);
      // Failure could be returned if a system limit is hit or
      // search fails
      if (res.equals(EnumEvalResult.FAIL))
      if (EnumEvalResult.FAIL.equals(res))
      {
        evalCtx.setEvaluationResult(EVALUATED_DENY_ACI, denyAci);
        return false;
      }
      else if (res.equals(EnumEvalResult.TRUE))
      else if (EnumEvalResult.TRUE.equals(res))
      {
        if (testAndSetTargAttrOperationMatches(evalCtx, denyAci, true))
        {
@@ -1204,7 +1079,7 @@
    for (Aci allowAci : evalCtx.getAllowList())
    {
      final EnumEvalResult res = Aci.evaluate(evalCtx, allowAci);
      if (res.equals(EnumEvalResult.TRUE))
      if (EnumEvalResult.TRUE.equals(res))
      {
        if (testAndSetTargAttrOperationMatches(evalCtx, allowAci, false))
        {
@@ -1281,8 +1156,6 @@
    return true;
  }
  /**
   * Evaluate an entry to be added to see if it has any "aci" attribute
   * type. If it does, examines each "aci" attribute type value for
opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -16,13 +16,18 @@
 */
package org.opends.server.authorization.dseecompat;
import java.util.*;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
@@ -31,13 +36,22 @@
import org.opends.server.api.plugin.PluginResult.PostOperation;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.SearchRequest;
import org.opends.server.protocols.ldap.LDAPControl;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.opends.server.types.*;
import org.opends.server.types.operation.*;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.IndexType;
import org.opends.server.types.Modification;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.operation.PostOperationAddOperation;
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostOperationModifyDNOperation;
import org.opends.server.types.operation.PostOperationModifyOperation;
import org.opends.server.types.operation.PostSynchronizationAddOperation;
import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
import org.opends.server.types.operation.PostSynchronizationModifyOperation;
import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation;
import static org.opends.messages.AccessControlMessages.*;
@@ -55,18 +69,11 @@
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
   * The fully-qualified name of this class.
   */
  /** The fully-qualified name of this class. */
  private static final String CLASS_NAME =
      "org.opends.server.authorization.dseecompat.AciListenerManager";
  /**
   * Internal plugin used for updating the cache before a response is
   * sent to the client.
   */
  /** Internal plugin used for updating the cache before a response is sent to the client. */
  private final class AciChangeListenerPlugin extends
      InternalDirectoryServerPlugin
  {
@@ -83,9 +90,6 @@
          PluginType.POST_OPERATION_MODIFY_DN), true);
    }
    /** {@inheritDoc} */
    @Override
    public void doPostSynchronization(
        PostSynchronizationAddOperation addOperation)
@@ -97,9 +101,6 @@
      }
    }
    /** {@inheritDoc} */
    @Override
    public void doPostSynchronization(
        PostSynchronizationDeleteOperation deleteOperation)
@@ -111,9 +112,6 @@
      }
    }
    /** {@inheritDoc} */
    @Override
    public void doPostSynchronization(
        PostSynchronizationModifyDNOperation modifyDNOperation)
@@ -125,9 +123,6 @@
      }
    }
    /** {@inheritDoc} */
    @Override
    public void doPostSynchronization(
        PostSynchronizationModifyOperation modifyOperation)
@@ -140,9 +135,6 @@
      }
    }
    /** {@inheritDoc} */
    @Override
    public PostOperation doPostOperation(
        PostOperationAddOperation addOperation)
@@ -158,9 +150,6 @@
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /** {@inheritDoc} */
    @Override
    public PostOperation doPostOperation(
        PostOperationDeleteOperation deleteOperation)
@@ -176,9 +165,6 @@
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /** {@inheritDoc} */
    @Override
    public PostOperation doPostOperation(
        PostOperationModifyDNOperation modifyDNOperation)
@@ -195,9 +181,6 @@
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /** {@inheritDoc} */
    @Override
    public PostOperation doPostOperation(
        PostOperationModifyOperation modifyOperation)
@@ -214,8 +197,6 @@
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    private void doPostAdd(Entry addedEntry)
    {
      // This entry might have both global and aci attribute types.
@@ -231,8 +212,6 @@
      }
    }
    private void doPostDelete(Entry deletedEntry)
    {
      // This entry might have both global and aci attribute types.
@@ -243,15 +222,11 @@
      aciList.removeAci(deletedEntry, hasAci, hasGlobalAci);
    }
    private void doPostModifyDN(DN fromDN, DN toDN)
    {
      aciList.renameAci(fromDN, toDN);
    }
    private void doPostModify(List<Modification> mods, Entry oldEntry,
        Entry newEntry)
    {
@@ -284,11 +259,8 @@
            hasGlobalAci);
      }
    }
  }
  /** The configuration DN. */
  private DN configurationDN;
@@ -300,15 +272,6 @@
  /** Search filter used in context search for "aci" attribute types. */
  private static SearchFilter aciFilter;
  /**
   * Internal plugin used for updating the cache before a response is
   * sent to the client.
   */
  private final AciChangeListenerPlugin plugin;
  /** The aci attribute type is operational so we need to specify it to be returned. */
  private static LinkedHashSet<String> attrs = new LinkedHashSet<>();
  static
  {
    // Set up the filter used to search private and public contexts.
@@ -320,10 +283,10 @@
    {
      // TODO should never happen, error message?
    }
    attrs.add("aci");
  }
  /** Internal plugin used for updating the cache before a response is sent to the client. */
  private final AciChangeListenerPlugin plugin;
  /**
   * Save the list created by the AciHandler routine. Registers as an
@@ -358,8 +321,6 @@
    DirectoryServer.registerAlertGenerator(this);
  }
  /**
   * Deregister from the change notification listener, the backend
   * initialization listener and the alert generator.
@@ -371,8 +332,6 @@
    DirectoryServer.deregisterAlertGenerator(this);
  }
  /**
   * {@inheritDoc} In this case, the server will search the backend to
   * find all aci attribute type values that it may contain and add them
@@ -393,9 +352,7 @@
    LinkedList<LocalizableMessage> failedACIMsgs = new LinkedList<>();
    InternalClientConnection conn = getRootConnection();
    // Add manageDsaIT control so any ACIs in referral entries will be
    // picked up.
    // Add manageDsaIT control so any ACIs in referral entries will be picked up.
    LDAPControl c1 = new LDAPControl(OID_MANAGE_DSAIT_CONTROL, true);
    // Add group membership control to let a backend look for it and
    // decide if it would abort searches.
@@ -418,18 +375,17 @@
      SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, aciFilter)
          .addControl(c1)
          .addControl(c2)
          .addAttribute(attrs);
          .addAttribute("aci");
      InternalSearchOperation internalSearch =
          new InternalSearchOperation(conn, nextOperationID(), nextMessageID(), request);
      LocalBackendSearchOperation localInternalSearch =
          new LocalBackendSearchOperation(internalSearch);
          new InternalSearchOperation(getRootConnection(), nextOperationID(), nextMessageID(), request);
      LocalBackendSearchOperation localInternalSearch = new LocalBackendSearchOperation(internalSearch);
      try
      {
        backend.search(localInternalSearch);
      }
      catch (Exception e)
      {
        logger.traceException(e);
        logger.trace(INFO_ACI_HANDLER_FAIL_PROCESS_ACI, e);
        continue;
      }
      if (!internalSearch.getSearchEntries().isEmpty())
@@ -444,12 +400,11 @@
    }
  }
  /**
   * {@inheritDoc} In this case, the server will remove all aci
   * attribute type values associated with entries in the provided
   * backend.
   * {@inheritDoc}
   * <p>
   * In this case, the server will remove all aci attribute type values associated with entries in
   * the provided backend.
   */
  @Override
  public void performBackendPostFinalizationProcessing(Backend<?> backend)
@@ -467,7 +422,6 @@
    // nothing to do.
  }
  /**
   * Retrieves the fully-qualified name of the Java class for this alert
   * generator implementation.
@@ -481,8 +435,6 @@
    return CLASS_NAME;
  }
  /**
   * Retrieves the DN of the configuration entry used to configure the
   * handler.
@@ -496,8 +448,6 @@
    return this.configurationDN;
  }
  /**
   * Retrieves information about the set of alerts that this generator
   * may produce. The map returned should be between the notification
@@ -536,11 +486,8 @@
    }
  }
  /**
   * Send an WARN_ACI_ENTER_LOCKDOWN_MODE alert notification and put the
   * server in lockdown mode.
   * Send an WARN_ACI_ENTER_LOCKDOWN_MODE alert notification and put the server in lockdown mode.
   */
  private void setLockDownMode()
  {
@@ -554,7 +501,6 @@
          ALERT_TYPE_ACCESS_CONTROL_PARSE_FAILED, lockDownMsg);
      // Enter lockdown mode.
      DirectoryServer.setLockdownMode(true);
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
@@ -27,26 +27,25 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.config.server.ConfigurationAddListener;
import org.forgerock.opendj.config.server.ConfigurationChangeListener;
import org.forgerock.opendj.config.server.ConfigurationDeleteListener;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.server.config.meta.BackendCfgDefn;
import org.forgerock.opendj.server.config.server.BackendCfg;
import org.forgerock.opendj.server.config.server.RootCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.config.ConfigConstants;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.WritabilityMode;
@@ -83,18 +82,20 @@
   * Initializes the configuration associated with the Directory Server
   * backends. This should only be called at Directory Server startup.
   *
   * @param backendIDsToStart
   *           The list of backendID to start. Everything will be started if empty.
   * @throws ConfigException
   *           If a critical configuration problem prevents the backend
   *           initialization from succeeding.
   * @throws InitializationException
   *           If a problem occurs while initializing the backends that is not
   *           related to the server configuration.
   * @param backendIDsToStart
   *           The list of backendID to start. Everything will be started if empty.
   */
  public void initializeBackendConfig(Collection<String> backendIDsToStart)
         throws ConfigException, InitializationException
  {
    initializeConfigurationBackend();
    // Register add and delete listeners.
    RootCfg root = serverContext.getRootConfig();
    root.addBackendAddListener(this);
@@ -115,7 +116,6 @@
      LocalizableMessage message =
          ERR_CONFIG_BACKEND_CANNOT_GET_CONFIG_BASE.get(getExceptionMessage(e));
      throw new ConfigException(message, e);
    }
@@ -124,8 +124,7 @@
    // configuration, even if there are no backends defined below it.
    if (backendRoot == null)
    {
      LocalizableMessage message = ERR_CONFIG_BACKEND_BASE_DOES_NOT_EXIST.get();
      throw new ConfigException(message);
      throw new ConfigException(ERR_CONFIG_BACKEND_BASE_DOES_NOT_EXIST.get());
    }
@@ -134,109 +133,77 @@
    {
      // Get the handler's configuration.
      // This will decode and validate its properties.
      BackendCfg backendCfg = root.getBackend(name);
      final BackendCfg backendCfg = root.getBackend(name);
      final DN backendDN = backendCfg.dn();
      final String backendID = backendCfg.getBackendId();
      if (!backendIDsToStart.isEmpty() && !backendIDsToStart.contains(backendID))
      {
        continue;
      }
      DN backendDN = backendCfg.dn();
      // Register as a change listener for this backend so that we can be
      // notified when it is disabled or enabled.
      backendCfg.addChangeListener(this);
      // Ignore this handler if it is disabled.
      if (backendCfg.isEnabled())
      if (!backendCfg.isEnabled())
      {
        // If there is already a backend registered with the specified ID,
        // then log an error and skip it.
        if (DirectoryServer.hasBackend(backendID))
        {
          logger.warn(WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID, backendID, backendDN);
          continue;
        }
        // See if the entry contains an attribute that specifies the class name
        // for the backend implementation.  If it does, then load it and make
        // sure that it's a valid backend implementation.  There is no such
        // attribute, the specified class cannot be loaded, or it does not
        // contain a valid backend implementation, then log an error and skip it.
        String className = backendCfg.getJavaClass();
        Backend<? extends BackendCfg> backend;
        try
        {
          backend = loadBackendClass(className).newInstance();
        }
        catch (Exception e)
        {
          logger.traceException(e);
          logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e));
          continue;
        }
        // If this backend is the configuration backend, then we don't want to do
        // any more with it because the configuration will have already been started.
        if (backend instanceof ConfigurationBackend)
        {
          continue;
        }
        // Set the backend ID and writability mode for this backend.
        backend.setBackendID(backendID);
        backend.setWritabilityMode(toWritabilityMode(backendCfg.getWritabilityMode()));
        ConfigChangeResult ccr = new ConfigChangeResult();
        if (!acquireSharedLock(backend, backendID, ccr)
            || !initializeBackend(backend, backendCfg, ccr))
        {
          logger.error(ccr.getMessages().get(0));
          continue;
        }
        onBackendPreInitialization(backend);
        try
        {
          DirectoryServer.registerBackend(backend);
        }
        catch (Exception e)
        {
          logger.traceException(e);
          logger.warn(WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND, backendID, getExceptionMessage(e));
          // FIXME -- Do we need to send an admin alert?
        }
        onBackendPostInitialization(backend);
        // Put this backend in the hash so that we will be able to find it if it is altered
        registeredBackends.put(backendDN, backend);
      }
      else
      {
        // The backend is explicitly disabled.  Log a mild warning and continue.
        logger.debug(INFO_CONFIG_BACKEND_DISABLED, backendDN);
        continue;
      }
      else if (DirectoryServer.hasBackend(backendID))
      {
        logger.warn(WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID, backendID, backendDN);
        continue;
      }
      // See if the entry contains an attribute that specifies the class name
      // for the backend implementation.  If it does, then load it and make
      // sure that it's a valid backend implementation.  There is no such
      // attribute, the specified class cannot be loaded, or it does not
      // contain a valid backend implementation, then log an error and skip it.
      String className = backendCfg.getJavaClass();
      Backend<? extends BackendCfg> backend;
      try
      {
        backend = loadBackendClass(className).newInstance();
      }
      catch (Exception e)
      {
        logger.traceException(e);
        logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e));
        continue;
      }
      initializeBackend(backend, backendCfg);
    }
  }
  private void onBackendPreInitialization(Backend<? extends BackendCfg> backend)
  private void initializeConfigurationBackend() throws InitializationException
  {
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    final ConfigurationBackend configBackend =
        new ConfigurationBackend(serverContext, DirectoryServer.getConfigurationHandler());
    initializeBackend(configBackend, configBackend.getBackendCfg());
  }
  private void initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg)
  {
    ConfigChangeResult ccr = new ConfigChangeResult();
    initializeBackend(backend, backendCfg, ccr);
    for (LocalizableMessage msg : ccr.getMessages())
    {
      listener.performBackendPreInitializationProcessing(backend);
      logger.error(msg);
    }
  }
  private void onBackendPostInitialization(Backend<? extends BackendCfg> backend)
  private void initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg, ConfigChangeResult ccr)
  {
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    backend.setBackendID(backendCfg.getBackendId());
    backend.setWritabilityMode(toWritabilityMode(backendCfg.getWritabilityMode()));
    if (acquireSharedLock(backend, backendCfg.getBackendId(), ccr) && configureAndOpenBackend(backend, backendCfg, ccr))
    {
      listener.performBackendPostInitializationProcessing(backend);
      registerBackend(backend, backendCfg, ccr);
    }
  }
@@ -422,40 +389,11 @@
        {
          // It isn't disabled, so we will do so now and deregister it from the
          // Directory Server.
          registeredBackends.remove(backendDN);
          for (BackendInitializationListener listener : getBackendInitializationListeners())
          {
            listener.performBackendPreFinalizationProcessing(backend);
          }
          DirectoryServer.deregisterBackend(backend);
          for (BackendInitializationListener listener : getBackendInitializationListeners())
          {
            listener.performBackendPostFinalizationProcessing(backend);
          }
          deregisterBackend(backendDN, backend);
          backend.finalizeBackend();
          // Remove the shared lock for this backend.
          try
          {
            String lockFile = LockFileManager.getBackendLockFileName(backend);
            StringBuilder failureReason = new StringBuilder();
            if (! LockFileManager.releaseLock(lockFile, failureReason))
            {
              logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend.getBackendID(), failureReason);
              // FIXME -- Do we need to send an admin alert?
            }
          }
          catch (Exception e2)
          {
            logger.traceException(e2);
            logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backend
                .getBackendID(), stackTraceToSingleLineString(e2));
            // FIXME -- Do we need to send an admin alert?
          }
          releaseSharedLock(backend, backend.getBackendID());
          return ccr;
        } // else already disabled, no need to do anything.
@@ -471,10 +409,6 @@
      return ccr;
    }
    String backendID = cfg.getBackendId();
    WritabilityMode writabilityMode = toWritabilityMode(cfg.getWritabilityMode());
    // See if the entry contains an attribute that specifies the class name
    // for the backend implementation.  If it does, then load it and make sure
    // that it's a valid backend implementation.  There is no such attribute,
@@ -482,13 +416,10 @@
    // backend implementation, then log an error and skip it.
    String className = cfg.getJavaClass();
    // See if this backend is currently active and if so if the name of the
    // class is the same.
    // See if this backend is currently active and if so if the name of the class is the same.
    if (backend != null && !className.equals(backend.getClass().getName()))
    {
      // It is not the same.  Try to load it and see if it is a valid backend
      // implementation.
      // It is not the same. Try to load it and see if it is a valid backend implementation.
      try
      {
        Class<?> backendClass = DirectoryServer.loadClass(className);
@@ -537,50 +468,51 @@
        return ccr;
      }
      // Set the backend ID and writability mode for this backend.
      backend.setBackendID(backendID);
      backend.setWritabilityMode(writabilityMode);
      if (!acquireSharedLock(backend, backendID, ccr)
          || !initializeBackend(backend, cfg, ccr))
      {
        return ccr;
      }
      onBackendPreInitialization(backend);
      // Register the backend with the server.
      try
      {
        DirectoryServer.registerBackend(backend);
      }
      catch (Exception e)
      {
        logger.traceException(e);
        LocalizableMessage message = WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND.get(
                backendID, getExceptionMessage(e));
        logger.warn(message);
        // FIXME -- Do we need to send an admin alert?
        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
        ccr.addMessage(message);
        return ccr;
      }
      onBackendPostInitialization(backend);
      registeredBackends.put(backendDN, backend);
      initializeBackend(backend, cfg, ccr);
      return ccr;
    }
    else if (ccr.getResultCode() == ResultCode.SUCCESS && backend != null)
    {
      backend.setWritabilityMode(writabilityMode);
      backend.setWritabilityMode(toWritabilityMode(cfg.getWritabilityMode()));
    }
    return ccr;
  }
  private boolean registerBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg, ConfigChangeResult ccr)
  {
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPreInitializationProcessing(backend);
    }
    try
    {
      DirectoryServer.registerBackend(backend);
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message =
          WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND.get(backendCfg.getBackendId(), getExceptionMessage(e));
      logger.error(message);
      // FIXME -- Do we need to send an admin alert?
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(message);
      return false;
    }
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPostInitializationProcessing(backend);
    }
    registeredBackends.put(backendCfg.dn(), backend);
    return true;
  }
  @Override
  public boolean isConfigurationAddAcceptable(
       BackendCfg configEntry,
@@ -709,51 +641,15 @@
      return ccr;
    }
    // Set the backend ID and writability mode for this backend.
    backend.setBackendID(backendID);
    backend.setWritabilityMode(toWritabilityMode(cfg.getWritabilityMode()));
    if (!acquireSharedLock(backend, backendID, ccr)
        || !initializeBackend(backend, cfg, ccr))
    {
      return ccr;
    }
    onBackendPreInitialization(backend);
    // At this point, the backend should be online.  Add it as one of the
    // registered backends for this backend config manager.
    try
    {
      DirectoryServer.registerBackend(backend);
    }
    catch (Exception e)
    {
      logger.traceException(e);
      LocalizableMessage message = WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND.get(
              backendID, getExceptionMessage(e));
      logger.error(message);
      // FIXME -- Do we need to send an admin alert?
      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
      ccr.addMessage(message);
      return ccr;
    }
    onBackendPostInitialization(backend);
    registeredBackends.put(backendDN, backend);
    initializeBackend(backend, cfg, ccr);
    return ccr;
  }
  private boolean initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg cfg, ConfigChangeResult ccr)
  private boolean configureAndOpenBackend(Backend<?> backend, BackendCfg cfg, ConfigChangeResult ccr)
  {
    try
    {
      initializeBackend(backend, cfg);
      configureAndOpenBackend(backend, cfg);
      return true;
    }
    catch (Exception e)
@@ -769,6 +665,13 @@
    }
  }
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void configureAndOpenBackend(Backend backend, BackendCfg cfg) throws ConfigException, InitializationException
  {
    backend.configureBackend(cfg, serverContext);
    backend.openBackend();
  }
  @SuppressWarnings("unchecked")
  private Class<Backend<BackendCfg>> loadBackendClass(String className) throws Exception
  {
@@ -844,18 +747,7 @@
      return ccr;
    }
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPreFinalizationProcessing(backend);
    }
    registeredBackends.remove(backendDN);
    DirectoryServer.deregisterBackend(backend);
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPostFinalizationProcessing(backend);
    }
    deregisterBackend(backendDN, backend);
    try
    {
@@ -873,11 +765,19 @@
    return ccr;
  }
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void initializeBackend(Backend backend, BackendCfg cfg)
       throws ConfigException, InitializationException
  private void deregisterBackend(DN backendDN, Backend<?> backend)
  {
    backend.configureBackend(cfg, serverContext);
    backend.openBackend();
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPreFinalizationProcessing(backend);
    }
    registeredBackends.remove(backendDN);
    DirectoryServer.deregisterBackend(backend);
    for (BackendInitializationListener listener : getBackendInitializationListeners())
    {
      listener.performBackendPostFinalizationProcessing(backend);
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/core/ConfigurationBackend.java
@@ -23,23 +23,28 @@
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.SortedSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.adapter.server3x.Converters;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.config.server.ConfigurationChangeListener;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.server.config.server.ConfigFileHandlerBackendCfg;
import org.forgerock.opendj.server.config.meta.BackendCfgDefn.WritabilityMode;
import org.forgerock.opendj.server.config.server.BackendCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.Backupable;
import org.opends.server.api.ClientConnection;
import org.opends.server.core.ConfigurationBackend.ConfigurationBackendCfg;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.DirectoryException;
@@ -53,12 +58,80 @@
import org.opends.server.types.Privilege;
import org.opends.server.types.RestoreConfig;
import org.opends.server.util.BackupManager;
import org.opends.server.util.CollectionUtils;
import org.opends.server.util.StaticUtils;
/** Back-end responsible for management of configuration entries. */
public class ConfigurationBackend extends Backend<ConfigFileHandlerBackendCfg> implements Backupable
public class ConfigurationBackend extends Backend<ConfigurationBackendCfg> implements Backupable
{
  /**
   * Dummy {@link BackendCfg} implementation for the {@link ConfigurationBackend}. No config is
   * needed for this specific backend, but this class is required to behave like other backends
   * during initialization.
   */
  public final class ConfigurationBackendCfg implements BackendCfg
  {
    private ConfigurationBackendCfg()
    {
      // let nobody instantiate it
    }
    @Override
    public DN dn()
    {
      return getBaseDNs()[0];
    }
    @Override
    public Class<? extends BackendCfg> configurationClass()
    {
      return this.getClass();
    }
    @Override
    public String getBackendId()
    {
      return CONFIG_BACKEND_ID;
    }
    @Override
    public SortedSet<DN> getBaseDN()
    {
      return Collections.unmodifiableSortedSet(CollectionUtils.newTreeSet(getBaseDNs()));
    }
    @Override
    public boolean isEnabled()
    {
      return true;
    }
    @Override
    public String getJavaClass()
    {
      return ConfigurationBackend.class.getName();
    }
    @Override
    public WritabilityMode getWritabilityMode()
    {
      return WritabilityMode.ENABLED;
    }
    @Override
    public void addChangeListener(ConfigurationChangeListener<BackendCfg> listener)
    {
      // no-op
    }
    @Override
    public void removeChangeListener(ConfigurationChangeListener<BackendCfg> listener)
    {
      // no-op
    }
  }
  /**
   * The backend ID for the configuration backend.
   * <p>
   * Try to avoid potential conflict with user backend identifiers.
@@ -114,6 +187,16 @@
    setBackendID(CONFIG_BACKEND_ID);
  }
  /**
   * Returns a new {@link ConfigurationBackendCfg} for this {@link ConfigurationBackend}.
   *
   * @return a new {@link ConfigurationBackendCfg} for this {@link ConfigurationBackend}
   */
  public ConfigurationBackendCfg getBackendCfg()
  {
    return new ConfigurationBackendCfg();
  }
  @Override
  public void closeBackend()
  {
@@ -128,7 +211,7 @@
  }
  @Override
  public void configureBackend(ConfigFileHandlerBackendCfg cfg,  ServerContext serverContext) throws ConfigException
  public void configureBackend(ConfigurationBackendCfg cfg, ServerContext serverContext) throws ConfigException
  {
    // No action is required.
  }
opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -1358,17 +1358,6 @@
    configFile = environmentConfig.getConfigFile();
    configurationHandler = ConfigurationHandler.bootstrapConfiguration(serverContext);
    serverManagementContext = new ServerManagementContext(configurationHandler);
    final ConfigurationBackend configBackend = new ConfigurationBackend(serverContext, configurationHandler);
    configBackend.openBackend();
    try
    {
      registerBackend(configBackend);
    }
    catch (DirectoryException e)
    {
      throw new InitializationException(LocalizableMessage.raw("Unable to register configuration backend", e));
    }
  }
  /**
@@ -1527,18 +1516,8 @@
      initializeRootDNConfigManager();
      initializeAuthenticatedUsers();
      // initialize both subentry manager and group manager for this backend.
      initializeSubentryManager();
      initializeGroupManager();
      // Initialize both subentry manager and group manager
      // for the configuration backend.
      // TODO : why do we initialize these now ? Can't we do them after backend initialization ?
      Backend<?> configBackend = getConfigurationBackend();
      subentryManager.performBackendPreInitializationProcessing(configBackend);
      groupManager.performBackendPreInitializationProcessing(configBackend);
      AccessControlConfigManager.getInstance().initializeAccessControl(serverContext);
      initializeBackends(Collections.<String> emptyList());
opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java
@@ -635,6 +635,10 @@
        rebuildIndexesNamed(INFO_UPGRADE_REBUILD_INDEXES_DISTINGUISHED_NAME.get(),
            "distinguishedName", "member", "owner", "roleOccupant", "seeAlso"));
    register("4.0.0",
        deleteConfigEntry(INFO_UPGRADE_TASK_CONFIGURATION_BACKEND_NOT_CONFIGURABLE.get(),
            "dn: ds-cfg-backend-id=config,cn=Backends,cn=config"));
    /**
     * All upgrades will refresh the server configuration schema and generate a new upgrade folder.
     */
opendj-server-legacy/src/messages/org/opends/messages/tool.properties
@@ -2539,6 +2539,8 @@
ERR_LDIFIMPORT_LDIF_FILE_DOESNT_EXIST_10055=Unable to access the LDIF file %s to import. Please check that the file is \
  local to the server and the path correct.
INFO_UPGRADE_TASK_BCRYPT_SCHEME_SUMMARY_10056=Adding Bcrypt password storage scheme configuration
INFO_UPGRADE_TASK_CONFIGURATION_BACKEND_NOT_CONFIGURABLE_10057=Removing config entry \
 for the non-configurable configuration backend
# Strings for generated reference documentation.
REF_SHORT_DESC_BACKUP_15000=back up OpenDJ directory data