From 94bbd5bb7c20a490558d8ec97d1be7e3dc492a42 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sun, 09 Sep 2007 23:08:09 +0000
Subject: [PATCH] Update the server to provide a basic framework for controlling when plugins will be invoked. There are two basic changes:
---
opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java | 1137 +++++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 793 insertions(+), 344 deletions(-)
diff --git a/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java b/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
index 9c9a8d8..279164f 100644
--- a/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
+++ b/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
@@ -26,47 +26,104 @@
*/
package org.opends.server.plugins;
-import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
-import org.opends.server.admin.std.meta.PluginCfgDefn;
+
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.PluginCfgDefn;
+import org.opends.server.admin.std.server.PluginCfg;
+import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
+import org.opends.server.api.AlertGenerator;
import org.opends.server.api.plugin.DirectoryServerPlugin;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.plugin.PreOperationPluginResult;
import org.opends.server.config.ConfigException;
-import org.opends.server.types.*;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.operation.PostSynchronizationAddOperation;
+import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
+import org.opends.server.types.operation.PostSynchronizationModifyOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PreOperationModifyDNOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
-import org.opends.server.types.operation.PreOperationOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.messages.Message;
-import static org.opends.messages.PluginMessages.*;
-import java.util.*;
+import static org.opends.messages.PluginMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
/**
- * This class implements a Directory Server plugin that performs attribute
- * uniqueness checking on the add, modify and modifyDN operations. If the
- * operation is eligible for checking based on a set of configuration criteria,
- * then the operation's attribute values will be checked, using that
- * configuration criteria, for uniqueness against the server's values to
- * determine if the operation can proceed.
+ * This class implements a Directory Server plugin that can be used to ensure
+ * that all values for a given attribute or set of attributes are unique within
+ * the server (or optionally, below a specified set of base DNs). It will
+ * examine all add, modify, and modify DN operations to determine whether any
+ * new conflicts are introduced. If a conflict is detected then the operation
+ * will be rejected, unless that operation is being applied through
+ * synchronization in which case an alert will be generated to notify
+ * administrators of the problem.
*/
public class UniqueAttributePlugin
extends DirectoryServerPlugin<UniqueAttributePluginCfg>
- implements ConfigurationChangeListener<UniqueAttributePluginCfg> {
+ implements ConfigurationChangeListener<UniqueAttributePluginCfg>,
+ AlertGenerator
+{
+ /**
+ * The debug log tracer that will be used for this plugin.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ /**
+ * The pre-operation plugin result that should be returned if an operation
+ * would have resulted in a unique attribute conflict.
+ */
+ private static final PreOperationPluginResult FAILED_PREOP_RESULT =
+ new PreOperationPluginResult(false, false, false, true);
+
+
+
+ /**
+ * The set of attributes that will be requested when performing internal
+ * search operations. This indicates that no attributes should be returned.
+ */
+ private static final LinkedHashSet<String> SEARCH_ATTRS =
+ new LinkedHashSet<String>(1);
+ static
+ {
+ SEARCH_ATTRS.add("1.1");
+ }
+
+
//Current plugin configuration.
private UniqueAttributePluginCfg currentConfiguration;
- //List of attribute types that must be unique.
- private LinkedHashSet<AttributeType> uniqueAttributeTypes =
- new LinkedHashSet<AttributeType>();
-//List of base DNs that limit the scope of the uniqueness checking.
- private LinkedHashSet<DN> baseDNs = new LinkedHashSet<DN>();
/**
* {@inheritDoc}
@@ -74,37 +131,710 @@
@Override()
public final void initializePlugin(Set<PluginType> pluginTypes,
UniqueAttributePluginCfg configuration)
- throws ConfigException {
+ throws ConfigException
+ {
configuration.addUniqueAttributeChangeListener(this);
currentConfiguration = configuration;
+ DirectoryServer.registerAlertGenerator(this);
+
for (PluginType t : pluginTypes)
- switch (t) {
+ {
+ switch (t)
+ {
case PRE_OPERATION_ADD:
case PRE_OPERATION_MODIFY:
case PRE_OPERATION_MODIFY_DN:
+ case POST_SYNCHRONIZATION_ADD:
+ case POST_SYNCHRONIZATION_MODIFY:
+ case POST_SYNCHRONIZATION_MODIFY_DN:
// These are acceptable.
break;
+
default:
Message message =
ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(t.toString());
throw new ConfigException(message);
}
- //Load base DNs if any.
- for(DN baseDN : configuration.getUniqueAttributeBaseDN())
- baseDNs.add(baseDN);
- //Load attribute types if any.
- for(AttributeType attributeType : configuration.getUniqueAttributeType())
- uniqueAttributeTypes.add(attributeType);
+ }
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final void finalizePlugin()
+ {
+ currentConfiguration.removeUniqueAttributeChangeListener(this);
+ DirectoryServer.deregisterAlertGenerator(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final PreOperationPluginResult
+ doPreOperation(PreOperationAddOperation addOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+ Entry entry = addOperation.getEntryToAdd();
+
+ Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+ for (AttributeType t : config.getUniqueAttributeType())
+ {
+ List<Attribute> attrList = entry.getAttribute(t);
+ if (attrList != null)
+ {
+ for (Attribute a : attrList)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
+ config, v);
+ if (conflictDN != null)
+ {
+ addOperation.appendErrorMessage(
+ ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+ v.getStringValue(), conflictDN.toString()));
+ addOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+ de.getResultCode().toString(),
+ de.getMessageObject());
+
+ addOperation.setResultCode(
+ DirectoryServer.getServerErrorResultCode());
+ addOperation.appendErrorMessage(m);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ }
+ }
+ }
+
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final PreOperationPluginResult
+ doPreOperation(PreOperationModifyOperation modifyOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+ DN entryDN = modifyOperation.getEntryDN();
+
+ Set<DN> baseDNs = getBaseDNs(config, entryDN);
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+ for (Modification m : modifyOperation.getModifications())
+ {
+ Attribute a = m.getAttribute();
+ AttributeType t = a.getAttributeType();
+ if (! config.getUniqueAttributeType().contains(t))
+ {
+ // This modification isn't for a unique attribute.
+ continue;
+ }
+
+ switch (m.getModificationType())
+ {
+ case ADD:
+ case REPLACE:
+ for (AttributeValue v : a.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
+ v);
+ if (conflictDN != null)
+ {
+ modifyOperation.appendErrorMessage(
+ ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+ v.getStringValue(), conflictDN.toString()));
+ modifyOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+ de.getResultCode().toString(),
+ de.getMessageObject());
+
+ modifyOperation.setResultCode(
+ DirectoryServer.getServerErrorResultCode());
+ modifyOperation.appendErrorMessage(message);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ break;
+
+ case INCREMENT:
+ // We could calculate the new value, but we'll just take it from the
+ // updated entry.
+ List<Attribute> attrList =
+ modifyOperation.getModifiedEntry().getAttribute(t,
+ a.getOptions());
+ if (attrList != null)
+ {
+ for (Attribute updatedAttr : attrList)
+ {
+ if (! updatedAttr.optionsEqual(a.getOptions()))
+ {
+ continue;
+ }
+
+ for (AttributeValue v : updatedAttr.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
+ config, v);
+ if (conflictDN != null)
+ {
+ modifyOperation.appendErrorMessage(
+ ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(
+ t.getNameOrOID(), v.getStringValue(),
+ conflictDN.toString()));
+ modifyOperation.setResultCode(
+ ResultCode.CONSTRAINT_VIOLATION);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+ de.getResultCode().toString(),
+ de.getMessageObject());
+
+ modifyOperation.setResultCode(
+ DirectoryServer.getServerErrorResultCode());
+ modifyOperation.appendErrorMessage(message);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ // We don't need to look at this modification because it's not a
+ // modification type of interest.
+ continue;
+ }
+ }
+
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final PreOperationPluginResult doPreOperation(
+ PreOperationModifyDNOperation modifyDNOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+
+ Set<DN> baseDNs = getBaseDNs(config,
+ modifyDNOperation.getUpdatedEntry().getDN());
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+ RDN newRDN = modifyDNOperation.getNewRDN();
+ for (int i=0; i < newRDN.getNumValues(); i++)
+ {
+ AttributeType t = newRDN.getAttributeType(i);
+ if (! config.getUniqueAttributeType().contains(t))
+ {
+ // We aren't interested in this attribute type.
+ continue;
+ }
+
+ try
+ {
+ AttributeValue v = newRDN.getAttributeValue(i);
+ DN conflictDN = getConflictingEntryDN(baseDNs,
+ modifyDNOperation.getEntryDN(), config, v);
+ if (conflictDN != null)
+ {
+ modifyDNOperation.appendErrorMessage(
+ ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+ v.getStringValue(), conflictDN.toString()));
+ modifyDNOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+ de.getResultCode().toString(),
+ de.getMessageObject());
+
+ modifyDNOperation.setResultCode(
+ DirectoryServer.getServerErrorResultCode());
+ modifyDNOperation.appendErrorMessage(m);
+ return FAILED_PREOP_RESULT;
+ }
+ }
+
+ return PreOperationPluginResult.SUCCESS;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final void doPostSynchronization(
+ PostSynchronizationAddOperation addOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+ Entry entry = addOperation.getEntryToAdd();
+
+ Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return;
+ }
+
+ for (AttributeType t : config.getUniqueAttributeType())
+ {
+ List<Attribute> attrList = entry.getAttribute(t);
+ if (attrList != null)
+ {
+ for (Attribute a : attrList)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
+ config, v);
+ if (conflictDN != null)
+ {
+ Message m = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+ t.getNameOrOID(),
+ addOperation.getConnectionID(),
+ addOperation.getOperationID(),
+ v.getStringValue(),
+ entry.getDN().toString(),
+ conflictDN.toString());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+ addOperation.getConnectionID(),
+ addOperation.getOperationID(),
+ entry.getDN().toString(),
+ de.getResultCode().toString(),
+ de.getMessageObject());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final void doPostSynchronization(
+ PostSynchronizationModifyOperation modifyOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+ DN entryDN = modifyOperation.getEntryDN();
+
+ Set<DN> baseDNs = getBaseDNs(config, entryDN);
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return;
+ }
+
+ for (Modification m : modifyOperation.getModifications())
+ {
+ Attribute a = m.getAttribute();
+ AttributeType t = a.getAttributeType();
+ if (! config.getUniqueAttributeType().contains(t))
+ {
+ // This modification isn't for a unique attribute.
+ continue;
+ }
+
+ switch (m.getModificationType())
+ {
+ case ADD:
+ case REPLACE:
+ for (AttributeValue v : a.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
+ v);
+ if (conflictDN != null)
+ {
+ Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+ t.getNameOrOID(),
+ modifyOperation.getConnectionID(),
+ modifyOperation.getOperationID(),
+ v.getStringValue(),
+ entryDN.toString(),
+ conflictDN.toString());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+ message);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+ modifyOperation.getConnectionID(),
+ modifyOperation.getOperationID(),
+ entryDN.toString(),
+ de.getResultCode().toString(),
+ de.getMessageObject());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, message);
+ }
+ }
+ break;
+
+ case INCREMENT:
+ // We could calculate the new value, but we'll just take it from the
+ // updated entry.
+ List<Attribute> attrList =
+ modifyOperation.getModifiedEntry().getAttribute(t,
+ a.getOptions());
+ if (attrList != null)
+ {
+ for (Attribute updatedAttr : attrList)
+ {
+ if (! updatedAttr.optionsEqual(a.getOptions()))
+ {
+ continue;
+ }
+
+ for (AttributeValue v : updatedAttr.getValues())
+ {
+ try
+ {
+ DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
+ config, v);
+ if (conflictDN != null)
+ {
+ Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+ t.getNameOrOID(),
+ modifyOperation.getConnectionID(),
+ modifyOperation.getOperationID(),
+ v.getStringValue(),
+ entryDN.toString(),
+ conflictDN.toString());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+ message);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message =
+ ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+ modifyOperation.getConnectionID(),
+ modifyOperation.getOperationID(),
+ entryDN.toString(),
+ de.getResultCode().toString(),
+ de.getMessageObject());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
+ message);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ // We don't need to look at this modification because it's not a
+ // modification type of interest.
+ continue;
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public final void doPostSynchronization(
+ PostSynchronizationModifyDNOperation modifyDNOperation)
+ {
+ UniqueAttributePluginCfg config = currentConfiguration;
+
+ Set<DN> baseDNs = getBaseDNs(config,
+ modifyDNOperation.getUpdatedEntry().getDN());
+ if (baseDNs == null)
+ {
+ // The entry is outside the scope of this plugin.
+ return;
+ }
+
+ RDN newRDN = modifyDNOperation.getNewRDN();
+ for (int i=0; i < newRDN.getNumValues(); i++)
+ {
+ AttributeType t = newRDN.getAttributeType(i);
+ if (! config.getUniqueAttributeType().contains(t))
+ {
+ // We aren't interested in this attribute type.
+ continue;
+ }
+
+ try
+ {
+ AttributeValue v = newRDN.getAttributeValue(i);
+ DN conflictDN = getConflictingEntryDN(baseDNs,
+ modifyDNOperation.getEntryDN(), config, v);
+ if (conflictDN != null)
+ {
+ Message m =
+ ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+ t.getNameOrOID(),
+ modifyDNOperation.getConnectionID(),
+ modifyDNOperation.getOperationID(),
+ v.getStringValue(),
+ modifyDNOperation.getUpdatedEntry().getDN().toString(),
+ conflictDN.toString());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+ modifyDNOperation.getConnectionID(),
+ modifyDNOperation.getOperationID(),
+ modifyDNOperation.getUpdatedEntry().getDN().toString(),
+ de.getResultCode().toString(),
+ de.getMessageObject());
+ DirectoryServer.sendAlertNotification(this,
+ ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
+ }
+ }
+ }
+
+
+
+ /**
+ * Retrieves the set of base DNs below which uniqueness checks should be
+ * performed. If no uniqueness checks should be performed for the specified
+ * entry, then {@code null} will be returned.
+ *
+ * @param config The plugin configuration to use to make the determination.
+ * @param entryDN The DN of the entry for which the checks will be
+ * performed.
+ */
+ private Set<DN> getBaseDNs(UniqueAttributePluginCfg config, DN entryDN)
+ {
+ Set<DN> baseDNs = config.getUniqueAttributeBaseDN();
+ if ((baseDNs == null) || baseDNs.isEmpty())
+ {
+ baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+ }
+
+ for (DN baseDN : baseDNs)
+ {
+ if (entryDN.isDescendantOf(baseDN))
+ {
+ return baseDNs;
+ }
+ }
+
+ return null;
+ }
+
+
+
+ /**
+ * Retrieves the DN of the first entry identified that conflicts with the
+ * provided value.
+ *
+ * @param baseDNs The set of base DNs below which the search is to be
+ * performed.
+ * @param targetDN The DN of the entry at which the change is targeted. If
+ * a conflict is found in that entry, then it will be
+ * ignored.
+ * @param config The plugin configuration to use when making the
+ * determination.
+ * @param value The value for which to identify any conflicting entries.
+ *
+ * @return The DN of the first entry identified that contains a conflicting
+ * value.
+ *
+ * @throws DirectoryException If a problem occurred while attempting to
+ * make the determination.
+ */
+ private DN getConflictingEntryDN(Set<DN> baseDNs, DN targetDN,
+ UniqueAttributePluginCfg config,
+ AttributeValue value)
+ throws DirectoryException
+ {
+ SearchFilter filter;
+ Set<AttributeType> attrTypes = config.getUniqueAttributeType();
+ if (attrTypes.size() == 1)
+ {
+ filter = SearchFilter.createEqualityFilter(attrTypes.iterator().next(),
+ value);
+ }
+ else
+ {
+ ArrayList<SearchFilter> equalityFilters =
+ new ArrayList<SearchFilter>(attrTypes.size());
+ for (AttributeType t : attrTypes)
+ {
+ equalityFilters.add(SearchFilter.createEqualityFilter(t, value));
+ }
+ filter = SearchFilter.createORFilter(equalityFilters);
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ for (DN baseDN : baseDNs)
+ {
+ InternalSearchOperation searchOperation =
+ conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 2, 0,
+ false, filter, SEARCH_ATTRS);
+ for (SearchResultEntry e : searchOperation.getSearchEntries())
+ {
+ if (! e.getDN().equals(targetDN))
+ {
+ return e.getDN();
+ }
+ }
+
+ switch (searchOperation.getResultCode())
+ {
+ case SUCCESS:
+ case NO_SUCH_OBJECT:
+ // These are fine. Either the search was successful or the base DN
+ // didn't exist.
+ break;
+
+ default:
+ // An error occurred that prevented the search from completing
+ // successfully.
+ throw new DirectoryException(searchOperation.getResultCode(),
+ searchOperation.getErrorMessage().toMessage());
+ }
+ }
+
+ // If we've gotten here, then no conflict was found.
+ return null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isConfigurationAcceptable(PluginCfg configuration,
+ List<Message> unacceptableReasons)
+ {
+ UniqueAttributePluginCfg cfg = (UniqueAttributePluginCfg) configuration;
+ return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
+ }
+
+
+
/**
* {@inheritDoc}
*/
public boolean isConfigurationChangeAcceptable(
- UniqueAttributePluginCfg configuration,
- List<Message> unacceptableReasons) {
+ UniqueAttributePluginCfg configuration,
+ List<Message> unacceptableReasons)
+ {
boolean configAcceptable = true;
for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
{
@@ -113,12 +843,15 @@
case PREOPERATIONADD:
case PREOPERATIONMODIFY:
case PREOPERATIONMODIFYDN:
+ case POSTSYNCHRONIZATIONADD:
+ case POSTSYNCHRONIZATIONMODIFY:
+ case POSTSYNCHRONIZATIONMODIFYDN:
// These are acceptable.
break;
default:
- Message message =
- ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(pluginType.toString());
+ Message message = ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(
+ pluginType.toString());
unacceptableReasons.add(message);
configAcceptable = false;
}
@@ -126,337 +859,53 @@
return configAcceptable;
}
+
+
/**
* {@inheritDoc}
*/
public ConfigChangeResult applyConfigurationChange(
- UniqueAttributePluginCfg newConfiguration) {
- ResultCode resultCode = ResultCode.SUCCESS;
- boolean adminActionRequired = false;
- ArrayList<Message> messages = new ArrayList<Message>();
- LinkedHashSet<AttributeType> newUniqueattributeTypes=
- new LinkedHashSet<AttributeType>();
- LinkedHashSet<DN> newConfiguredBaseDNs = new LinkedHashSet<DN>();
- //Load base DNs from new configuration.
- for(DN baseDN : newConfiguration.getUniqueAttributeBaseDN())
- newConfiguredBaseDNs.add(baseDN);
- //Load attribute types from new configuration.
- for(AttributeType attributeType : newConfiguration.getUniqueAttributeType())
- newUniqueattributeTypes.add(attributeType);
- //Switch to the new lists and configurations.
- baseDNs = newConfiguredBaseDNs;
- uniqueAttributeTypes = newUniqueattributeTypes;
+ UniqueAttributePluginCfg newConfiguration)
+ {
currentConfiguration = newConfiguration;
- return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ return new ConfigChangeResult(ResultCode.SUCCESS, false);
}
+
+
/**
* {@inheritDoc}
*/
- @Override()
- public final PreOperationPluginResult
- doPreOperation(PreOperationAddOperation addOperation) {
- PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
- DN entryDN=addOperation.getEntryDN();
- if(isEntryUniquenessCandidate(entryDN)) {
- List<AttributeValue> valueList =
- getEntryAttributeValues(addOperation.getEntryToAdd());
- if(!searchAllBaseDNs(valueList, entryDN))
- pluginResult = getPluginErrorResult(addOperation,
- ERR_PLUGIN_UNIQUEATTR_ADD_NOT_UNIQUE.get(entryDN.toString()));
- }
- return pluginResult;
+ public DN getComponentEntryDN()
+ {
+ return currentConfiguration.dn();
}
+
+
/**
* {@inheritDoc}
*/
- @Override()
- public final PreOperationPluginResult
- doPreOperation(PreOperationModifyOperation modifyOperation) {
- PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
- DN entryDN = modifyOperation.getEntryDN();
- if(isEntryUniquenessCandidate(entryDN)) {
- List<AttributeValue> valueList =
- getModificationAttributeValues(modifyOperation.getModifications(),
- modifyOperation.getModifiedEntry());
- if(!searchAllBaseDNs(valueList, entryDN))
- pluginResult = getPluginErrorResult(modifyOperation,
- ERR_PLUGIN_UNIQUEATTR_MOD_NOT_UNIQUE.get(entryDN.toString()));
- }
- return pluginResult;
+ public String getClassName()
+ {
+ return UniqueAttributePlugin.class.getName();
}
+
+
/**
* {@inheritDoc}
*/
- @Override()
- public final PreOperationPluginResult
- doPreOperation(PreOperationModifyDNOperation modifyDNOperation) {
- PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
- DN entryDN=modifyDNOperation.getOriginalEntry().getDN();
- //If the operation has a new superior DN then use that, since any moves
- //need to make sure there are no conflicts in the new superior base DN.
- if(modifyDNOperation.getNewSuperior() != null)
- entryDN = modifyDNOperation.getNewSuperior();
- if(isEntryUniquenessCandidate(entryDN)) {
- List<AttributeValue> valueList =
- getRDNAttributeValues(modifyDNOperation.getNewRDN());
- if(!searchAllBaseDNs(valueList, entryDN))
- pluginResult = getPluginErrorResult(modifyDNOperation,
- ERR_PLUGIN_UNIQUEATTR_MODDN_NOT_UNIQUE.get(entryDN.toString()));
- }
- return pluginResult;
- }
+ public LinkedHashMap<String,String> getAlerts()
+ {
+ LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>(2);
- /**
- * Determine if the specified DN is a candidate for attribute uniqueness
- * checking. Checking is skipped if the the unique attribute type list is
- * empty or if there are base DNS configured and the specified DN is not a
- * descendant of any of them. Checking is performed for all other cases.
- *
- * @param dn The DN to check.
- *
- * @return Returns <code>true</code> if the operation needs uniqueness
- * checking performed.
- */
- private boolean
- isEntryUniquenessCandidate(DN dn) {
- if(uniqueAttributeTypes.isEmpty())
- return false;
- else if(baseDNs.isEmpty())
- return true;
- else {
- for(DN baseDN : baseDNs)
- if(baseDN.isAncestorOf(dn))
- return true;
- }
- return false;
- }
+ alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+ ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_CONFLICT);
+ alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
+ ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_ERROR);
- /**
- * Returns a plugin result instance indicating that the operation should be
- * terminated; that no further pre-operation processing should be performed
- * and that the server should send the response immediately. It also adds
- * a CONSTRAINT_VIOLATION result code and the specified error message to
- * the specified operation.
- *
- * @param operation The operation to add the result code and message to.
- *
- * @param message The message to add to the operation.
- *
- * @return Returns a plugin result instance that halts further processing
- * on this operation.
- */
- private PreOperationPluginResult
- getPluginErrorResult(PreOperationOperation operation, Message message) {
- operation.appendErrorMessage(message);
- operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
- return new PreOperationPluginResult(false, false, true);
- }
-
- /**
- * Searches all of the the attribute types of the specified RDN for matches
- * in the unique attribute type list. If matches are found, then the
- * corresponding values are added to a list of values that will be eventually
- * searched for uniqueness.
-
- * @param rdn The RDN to examine.
- *
- * @return Returns a list of attribute values from the RDN that matches the
- * unique attribute type list.
- */
- private List<AttributeValue> getRDNAttributeValues(RDN rdn) {
- LinkedList<AttributeValue> valueList=
- new LinkedList<AttributeValue>();
- int numAVAs = rdn.getNumValues();
- for (int i = 0; i < numAVAs; i++) {
- if(uniqueAttributeTypes.contains(rdn.getAttributeType(i)))
- valueList.add(rdn.getAttributeValue(i));
- }
- return valueList;
- }
-
- /**
- * Searches all of the attribute types of the specified entry for matches
- * in the unique attribute type list. Ff matches are found, then the
- * corresponding values are added to a list of values that will eventually
- * be searched for uniqueness.
- *
- * @param entry The entry to examine.
- *
- * @return Returns a list of attribute values from the entry that matches the
- * unique attribute type list.
- */
- private List<AttributeValue> getEntryAttributeValues(Entry entry) {
- LinkedList<AttributeValue> valueList=new LinkedList<AttributeValue>();
- for(AttributeType attributeType : uniqueAttributeTypes) {
- if(entry.hasAttribute(attributeType)) {
- List<Attribute> attrList=entry.getAttribute(attributeType);
- for (Attribute a : attrList)
- valueList.addAll(a.getValues());
- }
- }
- return valueList;
- }
-
- /**
- * Iterate over the unique attribute type list calling a method that will
- * search the specified modification list for each attribute type and add
- * the corresponding values to a list of values.
- *
- * @param modificationList The modification list to search over.
- *
- * @param modifedEntry The copy of the entry with modifications applied.
- *
- * @return Returns a list of attribute values from the modification list
- * that matches the unique attribute type list.
- */
- private List<AttributeValue>
- getModificationAttributeValues(List<Modification> modificationList,
- Entry modifedEntry) {
- LinkedList<AttributeValue> valueList =
- new LinkedList<AttributeValue>();
- for(AttributeType attributeType : uniqueAttributeTypes)
- getModValuesForAttribute(modificationList, attributeType, valueList,
- modifedEntry);
- return valueList;
- }
-
- /**
- * Searches the specified modification list for the provided attribute type.
- * If a match is found than the attribute value is added to a list of
- * attribute values that will be eventually searched for uniqueness.
- *
- * @param modificationList The modification list to search over.
- *
- * @param attributeType The attribute type to search for.
- *
- * @param valueList A list of attribute values to put the values in.
- *
- * @param modifiedEntry A copy of the entry with modifications applied.
- */
- private void
- getModValuesForAttribute(List<Modification> modificationList,
- AttributeType attributeType,
- LinkedList<AttributeValue> valueList,
- Entry modifiedEntry) {
-
- for(Modification modification : modificationList) {
- ModificationType modType=modification.getModificationType();
- //Skip delete modifications or modifications on attribute types not
- //matching the specified type.
- if(modType == ModificationType.DELETE ||
- !modification.getAttribute().getAttributeType().equals(attributeType))
- continue;
- //Increment uses modified entry to get value for the attribute type.
- if(modType == ModificationType.INCREMENT) {
- List<Attribute> modifiedAttrs =
- modifiedEntry.getAttribute(attributeType,
- modification.getAttribute().getOptions());
- if (modifiedAttrs != null) {
- for (Attribute a : modifiedAttrs)
- valueList.addAll(a.getValues());
- }
- } else {
- Attribute modifiedAttribute=modification.getAttribute();
- if(modifiedAttribute.hasValue())
- valueList.addAll(modifiedAttribute.getValues());
- }
- }
- }
-
-
- /**
- * Iterates over the base DNs configured by the plugin entry searching for
- * value matches. If the base DN list is empty then the public naming
- * contexts are used instead.
- *
- * @param valueList The list of values to search for.
- *
- * @param entryDN The DN of the entry related to the operation.
- *
- * @return Returns <code>true</code> if a value is unique.
- */
- private boolean
- searchAllBaseDNs(List<AttributeValue> valueList, DN entryDN) {
- if(valueList.isEmpty())
- return true;
- if(baseDNs.isEmpty()) {
- for(DN baseDN : DirectoryServer.getPublicNamingContexts().keySet()) {
- if(searchBaseDN(valueList, baseDN, entryDN))
- return false;
- }
- } else {
- for(DN baseDN : baseDNs) {
- if(searchBaseDN(valueList, baseDN, entryDN))
- return false;
- }
- }
- return true;
- }
-
- /**
- * Search a single base DN for all the values in a specified value list.
- * A filter is created to search all the attribute at once for each
- * value in the list.
- *
- * @param valueList The list of values to search for.
- *
- * @param baseDN The base DN to base the search at.
- *
- * @param entryDN The DN of the entry related to the operation.
- *
- * @return Returns <code>true</code> if the values are not unique under the
- * under the base DN.
- */
- private boolean
- searchBaseDN(List<AttributeValue> valueList, DN baseDN,
- DN entryDN) {
- //Filter set to hold component filters.
- HashSet<SearchFilter> componentFilters=new HashSet<SearchFilter>();
- for(AttributeValue value : valueList) {
- //Iterate over the unique attribute list and build a equality filter
- //using each attribute type in the list and the current value being
- //matched.
- for(AttributeType attributeType : uniqueAttributeTypes)
- componentFilters.add(SearchFilter.createEqualityFilter(attributeType,
- value));
- //Perform the search using the OR filter created from the filter
- //components created above.
- InternalClientConnection conn =
- InternalClientConnection.getRootConnection();
- InternalSearchOperation operation = conn.processSearch(baseDN,
- SearchScope.WHOLE_SUBTREE,
- DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, true,
- SearchFilter.createORFilter(componentFilters),
- null);
- switch (operation.getResultCode()) {
- case SUCCESS:
- break;
-
- case NO_SUCH_OBJECT:
- //This base DN doesn't exist, return false.
- return false;
-
- case SIZE_LIMIT_EXCEEDED:
- case TIME_LIMIT_EXCEEDED:
- case ADMIN_LIMIT_EXCEEDED:
- default:
- //Couldn't determine if the attribute is unique because an
- //administrative limit was reached during the search. Fail the
- //operation by returning true. Possibly log an error here?
- return true;
- }
- for (SearchResultEntry entry : operation.getSearchEntries()) {
- //Only allow the entry DN to exist. The user might be modifying
- //the attribute values and putting the same value back. Any other entry
- //means the value is not unique.
- if(!entry.getDN().equals(entryDN))
- return true;
- }
- componentFilters.clear();
- }
- return false;
+ return alerts;
}
}
+
--
Gitblit v1.10.0