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

Nemanja Lukic
24.35.2011 85ba0a0b7c41216411fe99c1e714415e53fa5629
Patch for OPENDJ-221
6 files modified
1635 ■■■■■ changed files
opends/resource/schema/02-config.ldif 19 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml 93 ●●●●● patch | view | raw | blame | history
opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties 9 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/plugin.properties 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java 505 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java 993 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -2717,6 +2717,20 @@
  NAME 'ds-cfg-check-substrings'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.53
  NAME 'ds-cfg-check-references'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.54
  NAME 'ds-cfg-check-references-filter-criteria'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.55
  NAME 'ds-cfg-check-references-scope-criteria'
  SINGLE-VALUE
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -3917,7 +3931,10 @@
  MUST ds-cfg-attribute-type
  MAY ( ds-cfg-base-dn $
        ds-cfg-update-interval $
        ds-cfg-log-file )
        ds-cfg-log-file $
        ds-cfg-check-references $
        ds-cfg-check-references-filter-criteria $
        ds-cfg-check-references-scope-criteria )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.120
  NAME 'ds-cfg-smtp-account-status-notification-handler'
opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml
@@ -24,6 +24,7 @@
  !
  !
  !      Copyright 2007-2010 Sun Microsystems, Inc.
  !      Portions copyright 2011 profiq s.r.o.
  ! -->
<adm:managed-object name="referential-integrity-plugin"
  plural-name="referential-integrity-plugins"
@@ -68,6 +69,8 @@
        <adm:value>postoperationmodifydn</adm:value>
        <adm:value>subordinatemodifydn</adm:value>
        <adm:value>subordinatedelete</adm:value>
        <adm:value>preoperationadd</adm:value>
        <adm:value>preoperationmodify</adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
@@ -168,4 +171,94 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="check-references">
    <adm:synopsis>
      Specifies whether or not reference attributes must refer to existing
      entries.
    </adm:synopsis>
    <adm:description>
      When this property is set to true, this plugin will ensure that any new
      references added as part of an add or modify operation point to existing
      entries, and that the referenced entries match the filter criteria for the
      referencing attribute, if specified.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-check-references</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="check-references-filter-criteria"
    multi-valued="true" mandatory="false">
    <adm:synopsis>
      Specifies additional filter criteria which will be enforced when checking
      references.
    </adm:synopsis>
    <adm:description>
      If a reference attribute has filter criteria defined then this plugin
      will ensure that any new references added as part of an add or modify
      operation refer to an existing entry which matches the specified filter.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string>
        <adm:pattern>
          <adm:regex>^[^:]+:\\(.+\\)$</adm:regex>
          <adm:usage>ATTRIBUTE:FILTER</adm:usage>
          <adm:synopsis>An attribute-filter mapping.</adm:synopsis>
        </adm:pattern>
      </adm:string>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-check-references-filter-criteria</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="check-references-scope-criteria">
    <adm:synopsis>
      Specifies whether or not referenced entries must reside within the same
      naming context as the entry containing the reference.
    </adm:synopsis>
    <adm:description>
      The reference scope will only be enforced when reference checking is
      enabled.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>global</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:enumeration>
        <adm:value name="global">
          <adm:synopsis>
            References may refer to existing entries located anywhere in the
            Directory.
          </adm:synopsis>
        </adm:value>
        <adm:value name="naming-context">
          <adm:synopsis>
            References must refer to existing entries located within the same
            naming context.
          </adm:synopsis>
        </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-check-references-scope-criteria</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties
@@ -6,6 +6,15 @@
property.attribute-type.description=At least one attribute type must be specified, and the syntax of any attributes must be either a distinguished name (1.3.6.1.4.1.1466.115.121.1.12) or name and optional UID (1.3.6.1.4.1.1466.115.121.1.34).
property.base-dn.synopsis=Specifies the base DN that limits the scope within which referential integrity is maintained.
property.base-dn.default-behavior.alias.synopsis=Referential integrity is maintained in all public naming contexts.
property.check-references.synopsis=Specifies whether or not reference attributes must refer to existing entries.
property.check-references.description=When this property is set to true, this plugin will ensure that any new references added as part of an add or modify operation point to existing entries, and that the referenced entries match the filter criteria for the referencing attribute, if specified.
property.check-references-filter-criteria.synopsis=Specifies additional filter criteria which will be enforced when checking references.
property.check-references-filter-criteria.description=If a reference attribute has filter criteria defined then this plugin will ensure that any new references added as part of an add or modify operation refer to an existing entry which matches the specified filter.
property.check-references-filter-criteria.syntax.string.pattern.synopsis=An attribute-filter mapping.
property.check-references-scope-criteria.synopsis=Specifies whether or not referenced entries must reside within the same naming context as the entry containing the reference.
property.check-references-scope-criteria.description=The reference scope will only be enforced when reference checking is enabled.
property.check-references-scope-criteria.syntax.enumeration.value.global.synopsis=References may refer to existing entries located anywhere in the Directory.
property.check-references-scope-criteria.syntax.enumeration.value.naming-context.synopsis=References must refer to existing entries located within the same naming context.
property.enabled.synopsis=Indicates whether the plug-in is enabled for use.
property.invoke-for-internal-operations.synopsis=Indicates whether the plug-in should be invoked for internal operations.
property.invoke-for-internal-operations.description=Any plug-in that can be invoked for internal operations must ensure that it does not create any new internal operatons that can cause the same plug-in to be re-invoked.
opends/src/messages/messages/plugin.properties
@@ -436,3 +436,19 @@
 already been defined in the configuration
SEVERE_ERR_PLUGIN_ATTR_CLEANUP_EQUAL_VALUES_123=The mapping '%s:%s' maps the \
 attribute to itself
SEVERE_ERR_PLUGIN_REFERENT_ATTR_NOT_LISTED_124=The property \
 'check-references-filter-criteria' specifies filtering criteria for attribute \
 '%s', but this attribute is not listed in the 'attribute-type' property.
SEVERE_ERR_PLUGIN_REFERENT_BAD_FILTER_125=The filtering criteria '%s' specified \
 in property 'check-references-filter-criteria' is invalid because the filter \
 could not be decoded: '%s'.
SEVERE_ERR_PLUGIN_REFERENT_ENTRY_MISSING_126=The entry referenced by the value \
 '%s' of the attribute '%s' in the entry '%s' does not exist in any of the configured \
 naming contexts.
SEVERE_ERR_PLUGIN_REFERENT_FILTER_MISMATCH_127=The entry referenced by the value \
 '%s' of the attribute '%s' in the entry '%s' does not match the filter '%s'.
SEVERE_ERR_PLUGIN_REFERENT_NAMINGCONTEXT_MISMATCH_128=The entry referenced by the \
 value '%s' of the attribute '%s' in the entry '%s' does not belong to any of \
 the configured naming contexts.
SEVERE_ERR_PLUGIN_REFERENT_EXCEPTION_129=The opration could not be processed \
 due to an unexpected exception: '%s'.
opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
@@ -24,11 +24,15 @@
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS.
 *      Portions copyright 2011 profiq s.r.o.
 */
package org.opends.server.plugins;
import java.util.Iterator;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -50,6 +54,8 @@
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.admin.std.meta.PluginCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.ReferentialIntegrityPluginCfgDefn
  .CheckReferencesScopeCriteria;
import org.opends.server.api.Backend;
import org.opends.server.api.DirectoryThread;
import org.opends.server.api.ServerShutdownListener;
@@ -153,6 +159,12 @@
  //when the plugin is in background processing mode.
  private BufferedWriter writer;
  /* Specifies the mapping between the attribute type (specified in the
   * attributeTypes list) and the filter which the plugin should use
   * to verify the integrity of the value of the given attribute.
   */
  private LinkedHashMap<AttributeType, SearchFilter> attrFiltMap =
    new LinkedHashMap<AttributeType, SearchFilter>();
  /**
@@ -163,64 +175,14 @@
         throws ConfigException
  {
    pluginCfg.addReferentialIntegrityChangeListener(this);
    currentConfiguration = pluginCfg;
    LinkedList<Message> unacceptableReasons = new LinkedList<Message>();
    for (PluginType t : pluginTypes)
    if (!isConfigurationAcceptable(pluginCfg, unacceptableReasons))
    {
      switch (t)
      {
        case POST_OPERATION_DELETE:
        case POST_OPERATION_MODIFY_DN:
        case SUBORDINATE_MODIFY_DN:
        case SUBORDINATE_DELETE:
          // These are acceptable.
          break;
        default:
          throw new
             ConfigException(ERR_PLUGIN_REFERENT_INVALID_PLUGIN_TYPE.get(
                                  t.toString()));
      }
      throw new ConfigException(unacceptableReasons.getFirst());
    }
    Set<DN> cfgBaseDNs = pluginCfg.getBaseDN();
    if ((cfgBaseDNs == null) || cfgBaseDNs.isEmpty())
    {
      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
    }
    else
    {
      baseDNs.addAll(cfgBaseDNs);
    }
    // Iterate through all of the defined attribute types and ensure that they
    // have acceptable syntaxes and that they are indexed for equality below all
    // base DNs.
    for (AttributeType type : pluginCfg.getAttributeType())
    {
      if (! isAttributeSyntaxValid(type))
      {
        throw new ConfigException(
                       ERR_PLUGIN_REFERENT_INVALID_ATTRIBUTE_SYNTAX.get(
                            type.getNameOrOID(),
                             type.getSyntax().getSyntaxName()));
      }
      for (DN baseDN : cfgBaseDNs)
      {
        Backend b = DirectoryServer.getBackend(baseDN);
        if ((b != null) && (! b.isIndexed(type, IndexType.EQUALITY)))
        {
          throw new ConfigException(ERR_PLUGIN_REFERENT_ATTR_UNINDEXED.get(
                                         pluginCfg.dn().toString(),
                                         type.getNameOrOID(),
                                         b.getBackendID()));
        }
      }
      attributeTypes.add(type);
    }
    applyConfigurationChange(pluginCfg);
    // Set up log file. Note: it is not allowed to change once the plugin is
    // active.
@@ -261,10 +223,42 @@
      newAttributeTypes.add(type);
    }
    // Load the attribute-filter mapping
    LinkedHashMap<AttributeType, SearchFilter> newAttrFiltMap =
      new LinkedHashMap<AttributeType, SearchFilter>();
    for (String attrFilt : newConfiguration.getCheckReferencesFilterCriteria())
    {
      int sepInd = attrFilt.lastIndexOf(":");
      String attr = attrFilt.substring(0, sepInd);
      String filtStr = attrFilt.substring(sepInd + 1);
      AttributeType attrType =
        DirectoryServer.getAttributeType(attr.toLowerCase());
      try
      {
        SearchFilter filter =
          SearchFilter.createFilterFromString(filtStr);
        newAttrFiltMap.put(attrType, filter);
      }
      catch (DirectoryException de)
      {
        /* This should never happen because the filter has already
         * been verified.
         */
        logError(de.getMessageObject());
      }
    }
    //User is not allowed to change the logfile name, append a message that the
    //server needs restarting for change to take effect.
    // The first time the plugin is initialised the 'logFileName' is
    // not initialised, so in order to verify if it is equal to the new
    // log file name, we have to make sure the variable is not null.
    String newLogFileName=newConfiguration.getLogFile();
    if(!logFileName.equals(newLogFileName))
    if(logFileName != null && !logFileName.equals(newLogFileName))
    {
      adminActionRequired=true;
      messages.add(
@@ -275,6 +269,7 @@
    //Switch to the new lists.
    baseDNs = newConfiguredBaseDNs;
    attributeTypes = newAttributeTypes;
    attrFiltMap = newAttrFiltMap;
    //If the plugin is enabled and the interval has changed, process that
    //change. The change might start or stop the background processing thread.
@@ -294,9 +289,109 @@
  public boolean isConfigurationAcceptable(PluginCfg configuration,
                                           List<Message> unacceptableReasons)
  {
    ReferentialIntegrityPluginCfg cfg =
    boolean isAcceptable = true;
    ReferentialIntegrityPluginCfg pluginCfg =
         (ReferentialIntegrityPluginCfg) configuration;
    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
    for (PluginCfgDefn.PluginType t : pluginCfg.getPluginType())
    {
      switch (t)
      {
        case POSTOPERATIONDELETE:
        case POSTOPERATIONMODIFYDN:
        case SUBORDINATEMODIFYDN:
        case SUBORDINATEDELETE:
        case PREOPERATIONMODIFY:
        case PREOPERATIONADD:
          // These are acceptable.
          break;
        default:
          isAcceptable = false;
          unacceptableReasons.add(ERR_PLUGIN_REFERENT_INVALID_PLUGIN_TYPE.get(
                                  t.toString()));
      }
    }
    Set<DN> cfgBaseDNs = pluginCfg.getBaseDN();
    if ((cfgBaseDNs == null) || cfgBaseDNs.isEmpty())
    {
      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
    }
    // Iterate through all of the defined attribute types and ensure that they
    // have acceptable syntaxes and that they are indexed for equality below all
    // base DNs.
    Set<AttributeType> attributeTypes = pluginCfg.getAttributeType();
    for (AttributeType type : attributeTypes)
    {
      if (! isAttributeSyntaxValid(type))
      {
        isAcceptable = false;
        unacceptableReasons.add(
                       ERR_PLUGIN_REFERENT_INVALID_ATTRIBUTE_SYNTAX.get(
                            type.getNameOrOID(),
                             type.getSyntax().getSyntaxName()));
      }
      for (DN baseDN : cfgBaseDNs)
      {
        Backend b = DirectoryServer.getBackend(baseDN);
        if ((b != null) && (!b.isIndexed(type, IndexType.EQUALITY)))
        {
          isAcceptable = false;
          unacceptableReasons.add(ERR_PLUGIN_REFERENT_ATTR_UNINDEXED.get(
                                         pluginCfg.dn().toString(),
                                         type.getNameOrOID(),
                                         b.getBackendID()));
        }
      }
    }
    /* Iterate through the attribute-filter mapping and verify that the
     * map contains attributes listed in the attribute-type parameter
     * and that the filter is valid.
     */
    for (String attrFilt : pluginCfg.getCheckReferencesFilterCriteria())
    {
      int sepInd = attrFilt.lastIndexOf(":");
      String attr = attrFilt.substring(0, sepInd).trim();
      String filtStr = attrFilt.substring(sepInd + 1).trim();
      /* TODO: strip the ;options part? */
      /* Get the attribute type for the given attribute. The attribute
       * type has to be present in the attributeType list.
       */
      AttributeType attrType =
        DirectoryServer.getAttributeType(attr.toLowerCase());
      if (attrType == null || !attributeTypes.contains(attrType))
      {
        isAcceptable = false;
        unacceptableReasons.add(
          ERR_PLUGIN_REFERENT_ATTR_NOT_LISTED.get(attr));
      }
      /* Verify the filter.
       */
      try
      {
        SearchFilter.createFilterFromString(filtStr);
      }
      catch (DirectoryException de)
      {
        isAcceptable = false;
        unacceptableReasons.add(
          ERR_PLUGIN_REFERENT_BAD_FILTER.get(filtStr, de.getMessage()));
      }
    }
    return isAcceptable;
  }
@@ -307,61 +402,10 @@
          ReferentialIntegrityPluginCfg configuration,
          List<Message> unacceptableReasons)
  {
    boolean configAcceptable = true;
    for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
    {
      switch (pluginType)
      {
        case POSTOPERATIONDELETE:
        case POSTOPERATIONMODIFYDN:
        case SUBORDINATEMODIFYDN:
        case SUBORDINATEDELETE:
          // These are acceptable.
          break;
        default:
          unacceptableReasons.add(ERR_PLUGIN_REFERENT_INVALID_PLUGIN_TYPE.
                                  get(pluginType.toString()));
          configAcceptable = false;
      }
    }
    // Iterate through the set of base DNs that we will check and ensure that
    // the corresponding backend is indexed appropriately.
    Set<DN> cfgBaseDNs = configuration.getBaseDN();
    if ((cfgBaseDNs == null) || cfgBaseDNs.isEmpty())
    {
      cfgBaseDNs = DirectoryServer.getPublicNamingContexts().keySet();
    }
    //Iterate through attributes and check that each has a valid syntax
    for (AttributeType type : configuration.getAttributeType())
    {
      if (!isAttributeSyntaxValid(type))
      {
        unacceptableReasons.add(
             ERR_PLUGIN_REFERENT_INVALID_ATTRIBUTE_SYNTAX.get(
                  type.getNameOrOID(), type.getSyntax().getSyntaxName()));
        configAcceptable = false;
      }
      for (DN baseDN : cfgBaseDNs)
      {
        Backend b = DirectoryServer.getBackend(baseDN);
        if ((b != null) && (! b.isIndexed(type, IndexType.EQUALITY)))
        {
          unacceptableReasons.add(ERR_PLUGIN_REFERENT_ATTR_UNINDEXED.get(
                                       configuration.dn().toString(),
                                       type.getNameOrOID(), b.getBackendID()));
          configAcceptable = false;
        }
      }
    }
    return configAcceptable;
    return isConfigurationAcceptable(configuration, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
@@ -1057,4 +1101,247 @@
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
    public PluginResult.PreOperation doPreOperation(
    PreOperationModifyOperation modifyOperation)
  {
    /* Skip the integrity checks if the enforcing is not enabled
     */
    if (!currentConfiguration.isCheckReferences())
    {
      return PluginResult.PreOperation.continueOperationProcessing();
    }
    final List<Modification> mods = modifyOperation.getModifications();
    final Entry entry = modifyOperation.getModifiedEntry();
    for (Modification mod : mods)
    {
      final ModificationType modType = mod.getModificationType();
      /* Process only ADD and REPLACE modification types.
       */
      if ((modType != ModificationType.ADD)
          && (modType != ModificationType.REPLACE))
      {
        break;
      }
      AttributeType attrType      = mod.getAttribute().getAttributeType();
      Set<String> attrOptions     = mod.getAttribute().getOptions();
      Attribute modifiedAttribute = entry.getExactAttribute(attrType,
                                                            attrOptions);
      if (modifiedAttribute != null)
      {
        PluginResult.PreOperation result =
        isIntegrityMaintained(modifiedAttribute, entry.getDN());
        if (result.getResultCode() != ResultCode.SUCCESS)
        {
          return result;
        }
      }
    }
    /* At this point, everything is fine.
     */
    return PluginResult.PreOperation.continueOperationProcessing();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public PluginResult.PreOperation doPreOperation(
    PreOperationAddOperation addOperation)
  {
    /* Skip the integrity checks if the enforcing is not enabled.
     */
    if (!currentConfiguration.isCheckReferences())
    {
      return PluginResult.PreOperation.continueOperationProcessing();
    }
    final Entry entry = addOperation.getEntryToAdd();
    for (AttributeType attrType : attributeTypes)
    {
      final List<Attribute> attrs = entry.getAttribute(attrType, false);
      if (attrs != null)
      {
        PluginResult.PreOperation result =
        isIntegrityMaintained(attrs, entry.getDN());
        if (result.getResultCode() != ResultCode.SUCCESS)
        {
          return result;
        }
      }
    }
    /* If we reahed this point, everything is fine.
     */
    return PluginResult.PreOperation.continueOperationProcessing();
  }
  /**
   * Verifies that the integrity of values is maintained.
   * @param attrs   Attribute list which refers to another entry in the
   *                directory.
   * @param entryDN DN of the entry which contains the <CODE>attr</CODE>
   *                attribute.
   * @return        The SUCCESS if the integrity is maintained or
   *                CONSTRAINT_VIOLATION oherwise
   */
  private PluginResult.PreOperation
    isIntegrityMaintained(List<Attribute> attrs, DN entryDN)
  {
    PluginResult.PreOperation result = null;
    for(Attribute attr : attrs)
    {
      result = isIntegrityMaintained(attr, entryDN);
      if (result != PluginResult.PreOperation.continueOperationProcessing())
      {
        return result;
      }
    }
    return PluginResult.PreOperation.continueOperationProcessing();
  }
  /**
   * Verifies that the integrity of values is maintained.
   * @param attr    Attribute which refers to another entry in the
   *                directory.
   * @param entryDN DN of the entry which contains the <CODE>attr</CODE>
   *                attribute.
   * @return        The SUCCESS if the integrity is maintained or
   *                CONSTRAINT_VIOLATION oherwise
   */
  private PluginResult.PreOperation isIntegrityMaintained(Attribute attr,
                                                          DN entryDN)
  {
    /* Verify that the entry belongs to one of the configured naming
     * contexts.
     */
    boolean isLocal = false;
    if (baseDNs.isEmpty())
    {
      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
    }
    for (DN baseDN : baseDNs)
    {
      if (entryDN.matchesBaseAndScope(baseDN, SearchScope.SUBORDINATE_SUBTREE))
      {
        isLocal = true;
        break;
      }
    }
    /* If the entry does not belong to any of the configured naming
     * contexts continue further operation processing without checking
     * the integrity.
     */
    if (!isLocal)
    {
      return PluginResult.PreOperation.continueOperationProcessing();
    }
    /* Iterate over the list of attributes */
    Iterator<AttributeValue> attrValIt = attr.iterator();
    try
    {
      while (attrValIt.hasNext())
      {
        AttributeValue attrVal = attrValIt.next();
        DN valueEntryDN = null;
        Entry valueEntry = null;
        valueEntryDN = DN.decode(attrVal.getNormalizedValue());
        if (currentConfiguration.getCheckReferencesScopeCriteria()
          == CheckReferencesScopeCriteria.NAMING_CONTEXT)
        {
          boolean matches = false;
          for (DN baseDN : baseDNs)
          {
            if (entryDN.matchesBaseAndScope(baseDN,
              SearchScope.SUBORDINATE_SUBTREE))
            {
              matches = true;
              break;
            }
          }
          if (!matches)
          {
            return PluginResult.PreOperation.stopProcessing(
                  ResultCode.CONSTRAINT_VIOLATION,
                  ERR_PLUGIN_REFERENT_NAMINGCONTEXT_MISMATCH.get(
                    valueEntryDN.toString(),
                    attr.getName(),
                    entryDN.toString()
                  )
                );
          }
          valueEntry = DirectoryServer.getEntry(valueEntryDN);
        }
        else
        {
          valueEntry = DirectoryServer.getEntry(valueEntryDN);
        }
        /* Verify that the value entry exists in the backend.
         */
        if (valueEntry == null)
        {
          return PluginResult.PreOperation.stopProcessing(
            ResultCode.CONSTRAINT_VIOLATION,
            ERR_PLUGIN_REFERENT_ENTRY_MISSING.get(
            valueEntryDN.toString(),
            attr.getName(),
            entryDN.toString()
            ));
        }
        /* Verify that the value entry conforms to the filter.
         */
        SearchFilter filter = attrFiltMap.get(attr.getAttributeType());
        if (filter != null && !filter.matchesEntry(valueEntry))
        {
          return PluginResult.PreOperation.stopProcessing(
            ResultCode.CONSTRAINT_VIOLATION,
            ERR_PLUGIN_REFERENT_FILTER_MISMATCH.get(
              valueEntry.getDN().toString(),
              attr.getName(),
              entryDN.toString(),
              filter.toString())
            );
        }
      }
    }
    catch (DirectoryException de)
    {
      return PluginResult.PreOperation.stopProcessing(
        ResultCode.OTHER,
        ERR_PLUGIN_REFERENT_EXCEPTION.get(de.getLocalizedMessage()));
    }
    return PluginResult.PreOperation.continueOperationProcessing();
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 profiq s.r.o.
 */
package org.opends.server.plugins;
@@ -59,6 +60,10 @@
  private String dsConfigBaseDN="ds-cfg-base-dn";
  private String dsConfigUpdateInterval=
                               "ds-cfg-update-interval";
  private String dsConfigEnforceIntegrity = "ds-cfg-check-references";
  private String dsConfigAttrFiltMapping =
    "ds-cfg-check-references-filter-criteria";
  private String dsConfigPluginType = "ds-cfg-plugin-type";
  //Suffixes to use for non-public naming context tests.
  private String exSuffix="dc=example,dc=com";
@@ -427,7 +432,77 @@
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: logs/test"
            "ds-cfg-log-file: logs/test",
            "",
            // check-references, default
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-check-references: false",
            "",
            // check-references enabled
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "",
            // check-references enabled, check-references-filter-criteria set
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)",
            "",
            // check-references disabled, check-references-filter-criteria set
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-check-references: false",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)"
    );
    Object[][] array = new Object[entries.size()][1];
    for (int i=0; i < array.length; i++)
@@ -576,7 +651,121 @@
            "ds-cfg-base-dn: ou=dept, dc=example,dc=com",
            "ds-cfg-base-dn: ou=people, o=test",
            "ds-cfg-update-interval: 300 seconds",
            "ds-cfg-log-file: /hopefully/doesn't/file/exist"
            "ds-cfg-log-file: /hopefully/doesn't/file/exist",
            "",
            // check-references bad value
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: bad",
            "ds-cfg-check-references-filter-criteria: member:(objectclass=person)",
            "",
            // check-references enabled, attrbute not on the list
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: manager:(objectclass=person)",
            "",
            // check-references true, bad filter
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:bad",
            "",
            // check-references true, attr-filt bad format
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: bad",
            "",
            // check-references true, no filter
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: member:",
            "",
            // check-references true, null:null
            "dn: cn=Referential Integrity,cn=Plugins,cn=config",
            "objectClass: top",
            "objectClass: ds-cfg-plugin",
            "objectClass: ds-cfg-referential-integrity-plugin",
            "cn: Referential Integrity",
            "ds-cfg-java-class: org.opends.server.plugins.ReferentialIntegrityPlugin",
            "ds-cfg-enabled: true",
            "ds-cfg-plugin-type: postOperationDelete",
            "ds-cfg-plugin-type: postOperationModifyDN",
            "ds-cfg-plugin-type: subordinateModifyDN",
            "ds-cfg-plugin-type: preOperationAdd",
            "ds-cfg-plugin-type: preOperationModify",
            "ds-cfg-attribute-type: member",
            "ds-cfg-base-dn: o=test",
            "ds-cfg-base-dn: dc=example, dc=com",
            "ds-cfg-check-references: true",
            "ds-cfg-check-references-filter-criteria: :"
    );
    Object[][] array = new Object[entries.size()][1];
    for (int i=0; i < array.length; i++)
@@ -640,6 +829,8 @@
  @BeforeMethod
  public void clearConfigEntries() throws Exception {
    deleteAttrsEntry(configDN, dsConfigBaseDN);
    deleteAttrsEntry(configDN, dsConfigEnforceIntegrity);
    deleteAttrsEntry(configDN, dsConfigAttrFiltMapping);
    //Hopefully put an attribute type there that won't impact the rest of the
    //unit tests.
    replaceAttrEntry(configDN, dsConfigAttrType,"seeAlso");
@@ -847,7 +1038,7 @@
   * @param attrValStrings The values to add to the entry.
   *
   */
  private void
  private ModifyOperation
  addAttrEntry(DN dn, String attrTypeString, String... attrValStrings) {
    LinkedList<Modification> mods = new LinkedList<Modification>();
    AttributeType attrType = getAttrType(attrTypeString);
@@ -858,7 +1049,7 @@
    mods.add(new Modification(ModificationType.ADD, builder.toAttribute()));
    InternalClientConnection conn =
            InternalClientConnection.getRootConnection();
    conn.processModify(dn, mods);
    return conn.processModify(dn, mods);
  }
/**
@@ -871,7 +1062,7 @@
   * @param attrValStrings The values to replace in the the entry.
 *
   */
  private void
  private ModifyOperation
  replaceAttrEntry(DN dn, String attrTypeString, String... attrValStrings) {
    LinkedList<Modification> mods = new LinkedList<Modification>();
    AttributeType attrType = getAttrType(attrTypeString);
@@ -882,7 +1073,7 @@
    mods.add(new Modification(ModificationType.REPLACE, builder.toAttribute()));
    InternalClientConnection conn =
            InternalClientConnection.getRootConnection();
    conn.processModify(dn, mods);
    return conn.processModify(dn, mods);
  }
@@ -1062,4 +1253,794 @@
                                       false, null);
    assertEquals(modDNop.getResultCode(), ResultCode.SUCCESS);
  }
    /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddUserWitManagerFilterNoNC()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    deleteAttrsEntry(configDN, dsConfigBaseDN);
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    Entry entry = null;
    AddOperation addOperation = null;
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    entry = makeEntry("uid=manager,ou=people,ou=dept,o=test");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,o=test");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddUserWitManagerFilter()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    Entry entry = null;
    AddOperation addOperation = null;
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    entry = makeEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,dc=example,dc=com");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
  }
    /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager' which doesn't exist
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddUserWithMissingManagerEntry()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    Entry entry = null;
    AddOperation addOperation = null;
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=bad,ou=people,ou=dept,dc=example,dc=com");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
    /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=groupOfNames)
   * - add a user 'manager' with the object class 'person' to the
   *   'dc=example,dc=com'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddUserWitManagerFilterMismatch()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=gropuOfNames)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    Entry entry = null;
    AddOperation addOperation = null;
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    entry = makeEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,dc=example,dc=com");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
    /**
   * Test case:
   * - integrity is enforced on the attribute 'manager'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a user 'manager' to the 'o=test'
   * - add a user 'employee' with the attribute manager which points to the
   *   entry 'manager'
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddUserWithManagerNC()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    Entry entry = null;
    AddOperation addOperation = null;
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    entry = makeEntry("uid=manager,ou=people,ou=dept,o=test");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    entry = TestCaseUtils.makeEntry(
      "dn: uid=employee,ou=people,ou=dept,dc=example,dc=com",
      "objectclass: top",
      "objectclass: person",
      "objectclass: organizationalperson",
      "objectclass: inetorgperson",
      "uid: employee",
      "cn: employee",
      "sn: employee",
      "givenname: employee",
      "manager: uid=manager,ou=people,ou=dept,o=test");
    addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddGroupWithFilter()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.3,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries and one missing
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddGroupWithMissingMember()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=bad,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries and one entry
   *   being of object class groupOfNames
   * - CONSTRAINT VIOLATION
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddGroupMemberFilterMismatch()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: cn=group,ou=groups,dc=example,dc=com",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  /**
   * Test case:
   * - integrity is enforced on the attribute 'member'
   * - value of the 'manager' attribute should match the filter:
   *  (objectclass=person)
   * - add a group 'referent group' to the 'dc=example,dc=com' with the
   *   'member' attribute pointing to the existing user entries with one memeber
   *   belonging to 'o=test' naming context
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityAddGroupMemberNC()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = TestCaseUtils.makeEntry(
      "dn: cn=referent group,ou=groups,dc=example,dc=com",
      "objectclass: top",
      "objectclass: groupofnames",
      "cn: refetent group",
      "member: uid=user.1,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.2,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.3,ou=people,ou=dept,o=test",
      "member: uid=user.4,ou=people,ou=dept,dc=example,dc=com",
      "member: uid=user.5,ou=people,ou=dept,dc=example,dc=com"
      );
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(),
                 ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - employee entry exists
   * - manager entry exists
   * - add 'manager' attribute to the manager entry
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityModifyUserAddManagerFilter()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = makeEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    ModifyOperation modOperation = addAttrEntry(DN.decode(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - employee entry exists with 'manager' attribute pointing to the
   *   manager entry
   * - manager entry exists
   * - user.2 entry exists
   * - modify 'manager' attribute to the 'user.2' entry
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityModifyUserModifyManagerFilter()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = makeEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    ModifyOperation modOperation = addAttrEntry(DN.decode(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
    modOperation = replaceAttrEntry(DN.decode(user1),
                                    "manager", user2);
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - filter is set to posixAccount
   * - employee entry exists
   * - manager entry exists with objectclass person
   * - add 'manager' attribute to the manager entry
   * - constraint violation
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityModifyUserAddManagerFilterMismatch()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=posixAccount)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = makeEntry("uid=manager,ou=people,ou=dept,dc=example,dc=com");
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    ModifyOperation modOperation = addAttrEntry(DN.decode(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  /**
   * Test case:
   * - employee entry exists in dc=example,dc=com
   * - manager entry exists in o=other
   * - add 'manager' attribute to the employee poiting to the manager
   *   entry
   * - SUCCESS
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityModifyUserAddManagerNC()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    InternalClientConnection conn =
      InternalClientConnection.getRootConnection();
    Entry entry = makeEntry("uid=manager,ou=people,ou=dept,o=test");
    AddOperation addOperation = conn.processAdd(entry);
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    ModifyOperation modOperation = addAttrEntry(DN.decode(user1),
     "manager", "uid=manager,ou=people,ou=dept,o=test");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.SUCCESS);
  }
  /**
   * Test case:
   * - employee entry exists
   * - manager entry does not exist
   * - add 'manager' attribute to the employee
   * - constraint violation
   * @throws Exception
   */
  @Test()
  public void testEnforceIntegrityModifyUserAddManagerMissing()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "manager");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "manager:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    ModifyOperation modOperation = addAttrEntry(DN.decode(user1),
     "manager", "uid=manager,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  @Test()
  public void testEnforceIntegrityModifyGroupAddMember()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    ModifyOperation modOperation = addAttrEntry(DN.decode(group),
                                                "member",
                                                user1);
    assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS);
  }
  @Test()
  public void testEnforceIntegrityModifyGroupAddMissingMember()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    ModifyOperation modOperation = addAttrEntry(DN.decode(group),
      "member", "uid=user.100,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  @Test()
  public void testEnforceIntegrityModifyGroupAddMemberFilterMismatch()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=posixaccount)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    ModifyOperation modOperation = addAttrEntry(DN.decode(group),
      "member", "uid=user.100,ou=people,ou=dept,dc=example,dc=com");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.CONSTRAINT_VIOLATION);
  }
  @Test()
  public void testEnforceIntegrityModifyGroupAddMemberNC()
    throws Exception
  {
    replaceAttrEntry(configDN, "ds-cfg-enabled", "false");
    replaceAttrEntry(configDN, dsConfigPluginType,
                               "postoperationdelete",
                               "postoperationmodifydn",
                               "subordinatemodifydn",
                               "subordinatedelete",
                               "preoperationadd",
                               "preoperationmodify");
    addAttrEntry(configDN, dsConfigBaseDN,
                           "dc=example,dc=com",
                           "o=test");
    replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true");
    replaceAttrEntry(configDN, dsConfigAttrType, "member");
    addAttrEntry(configDN, dsConfigAttrFiltMapping,
                           "member:(objectclass=person)");
    replaceAttrEntry(configDN, "ds-cfg-enabled", "true");
    ModifyOperation modOperation = addAttrEntry(DN.decode(group),
      "member", "uid=user.1,ou=people,ou=dept,o=test");
    assertEquals(modOperation.getResultCode(),
                 ResultCode.SUCCESS);
  }
}