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

Jean-Noël Rouvignac
06.09.2016 10f0e9c1ad2e1296678c3706152a84437da35a78
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