UniqueAttributePlugin.java revision ea1068c292e9b341af6d6b563cd8988a96be20a9
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2009 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* 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
implements ConfigurationChangeListener<UniqueAttributePluginCfg>,
{
/**
* The debug log tracer that will be used for this plugin.
*/
/**
* The set of attributes that will be requested when performing internal
* search operations. This indicates that no attributes should be returned.
*/
static
{
}
/** Current plugin configuration. */
/**
* The data structure to store the mapping between the attribute value and the
* corresponding dn.
*/
/**
* {@inheritDoc}
*/
throws ConfigException
{
for (PluginType t : pluginTypes)
{
switch (t)
{
case PRE_OPERATION_ADD:
case PRE_OPERATION_MODIFY:
case PRE_OPERATION_MODIFY_DN:
case POST_OPERATION_ADD:
case POST_OPERATION_MODIFY:
case POST_OPERATION_MODIFY_DN:
case POST_SYNCHRONIZATION_ADD:
// These are acceptable.
break;
default:
}
}
{
}
{
{
{
}
}
}
}
/**
* {@inheritDoc}
*/
public final void finalizePlugin()
{
}
/**
* {@inheritDoc}
*/
public final PluginResult.PreOperation
{
{
// The entry is outside the scope of this plugin.
}
{
{
{
for (ByteString v : a)
{
{
return stop;
}
}
}
}
}
}
/**
* {@inheritDoc}
*/
public final PluginResult.PreOperation
{
{
// The entry is outside the scope of this plugin.
}
{
Attribute a = m.getAttribute();
AttributeType t = a.getAttributeType();
{
// This modification isn't for a unique attribute.
continue;
}
switch (m.getModificationType().asEnum())
{
case ADD:
case REPLACE:
for (ByteString v : a)
{
{
return stop;
}
}
break;
case INCREMENT:
// We could calculate the new value, but we'll just take it from the
// updated entry.
a.getOptions());
{
{
{
continue;
}
for (ByteString v : updatedAttr)
{
{
return stop;
}
}
}
}
break;
default:
// We don't need to look at this modification because it's not a
// modification type of interest.
continue;
}
}
}
{
try
{
//Raise an exception if a conflicting concurrent operation is
//in progress. Otherwise, store this attribute value with its
//corresponding DN and proceed.
if (conflictDN == null)
{
recordedValues.add(v);
config, v);
}
if (conflictDN != null)
{
// Before returning, we need to remove all values added
// in the uniqueAttrValue2Dn map, because PostOperation
// plugin does not get called.
{
}
t.getNameOrOID(), v, conflictDN);
}
}
catch (DirectoryException de)
{
// Try some cleanup before returning, to avoid memory leaks
{
}
}
return null;
}
/**
* {@inheritDoc}
*/
{
{
// The entry is outside the scope of this plugin.
}
{
{
// We aren't interested in this attribute type.
continue;
}
{
return stop;
}
}
}
/**
* {@inheritDoc}
*/
public final void doPostSynchronization(
{
{
// The entry is outside the scope of this plugin.
return;
}
{
{
{
for (ByteString v : a)
{
}
}
}
}
}
/**
* {@inheritDoc}
*/
public final void doPostSynchronization(
{
{
// The entry is outside the scope of this plugin.
return;
}
{
Attribute a = m.getAttribute();
AttributeType t = a.getAttributeType();
{
// This modification isn't for a unique attribute.
continue;
}
switch (m.getModificationType().asEnum())
{
case ADD:
case REPLACE:
for (ByteString v : a)
{
}
break;
case INCREMENT:
// We could calculate the new value, but we'll just take it from the
// updated entry.
a.getOptions());
{
{
{
continue;
}
for (ByteString v : updatedAttr)
{
}
}
}
break;
default:
// We don't need to look at this modification because it's not a
// modification type of interest.
continue;
}
}
}
/**
* {@inheritDoc}
*/
public final void doPostSynchronization(
{
{
// The entry is outside the scope of this plugin.
return;
}
{
{
// We aren't interested in this attribute type.
continue;
}
}
}
{
try
{
if (conflictDN == null)
{
}
if (conflictDN != null)
{
t.getNameOrOID(),
v,
message);
}
}
catch (DirectoryException de)
{
de.getResultCode(),
de.getMessageObject());
}
}
/**
* 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.
*/
{
{
}
{
{
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.
*/
throws DirectoryException
{
{
value);
}
else
{
for (AttributeType t : attrTypes)
{
}
}
{
.setSizeLimit(2)
{
{
return e.getName();
}
}
{
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.
}
}
// If we've gotten here, then no conflict was found.
return null;
}
/**
* {@inheritDoc}
*/
{
}
/**
* {@inheritDoc}
*/
public boolean isConfigurationChangeAcceptable(
{
boolean configAcceptable = true;
{
switch (pluginType)
{
case PREOPERATIONADD:
case PREOPERATIONMODIFY:
case PREOPERATIONMODIFYDN:
case POSTOPERATIONADD:
case POSTOPERATIONMODIFY:
case POSTOPERATIONMODIFYDN:
case POSTSYNCHRONIZATIONADD:
// These are acceptable.
break;
default:
configAcceptable = false;
}
}
{
}
{
{
{
configAcceptable = false;
}
}
}
return configAcceptable;
}
/**
* {@inheritDoc}
*/
{
return new ConfigChangeResult();
}
/**
* {@inheritDoc}
*/
public DN getComponentEntryDN()
{
return currentConfiguration.dn();
}
/**
* {@inheritDoc}
*/
public String getClassName()
{
return UniqueAttributePlugin.class.getName();
}
/**
* {@inheritDoc}
*/
{
return alerts;
}
/**
* {@inheritDoc}
*/
public final PluginResult.PostOperation
{
{
// The entry is outside the scope of this plugin.
}
//Remove the attribute value from the map.
{
{
{
for (ByteString v : a)
{
}
}
}
}
}
/**
* {@inheritDoc}
*/
public final PluginResult.PostOperation
{
{
// The entry is outside the scope of this plugin.
}
{
Attribute a = m.getAttribute();
AttributeType t = a.getAttributeType();
{
// This modification isn't for a unique attribute.
continue;
}
switch (m.getModificationType().asEnum())
{
case ADD:
case REPLACE:
for (ByteString v : a)
{
}
break;
case INCREMENT:
// We could calculate the new value, but we'll just take it from the
// updated entry.
a.getOptions());
{
{
{
continue;
}
for (ByteString v : updatedAttr)
{
}
}
}
break;
default:
// We don't need to look at this modification because it's not a
// modification type of interest.
continue;
}
}
}
/**
* {@inheritDoc}
*/
public final PluginResult.PostOperation
{
{
// The entry is outside the scope of this plugin.
return PostOperation.continueOperationProcessing();
}
{
{
// We aren't interested in this attribute type.
continue;
}
}
return PostOperation.continueOperationProcessing();
}
}