From eadeef95188152bda18ea3e01200996c3ddac1be Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 04 Sep 2007 01:24:19 +0000
Subject: [PATCH] Add a 7-bit clean plugin, which can be used to ensure that values for a specified set of attributes (optionally only for entries below a specified set of base DNs) are 7-bit clean.

---
 opendj-sdk/opends/src/server/org/opends/server/plugins/SevenBitCleanPlugin.java                                 |  552 +++++++++++++++++++++
 opendj-sdk/opends/resource/config/config.ldif                                                                   |   39 +
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/SevenBitCleanPluginConfiguration.xml               |  125 ++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/SevenBitCleanPluginTestCase.java |  717 +++++++++++++++++++++++++++
 opendj-sdk/opends/src/messages/messages/plugin.properties                                                       |   68 +-
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                |    9 
 6 files changed, 1,472 insertions(+), 38 deletions(-)

diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 0f64d33..36bf959 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -1419,6 +1419,21 @@
 objectClass: ds-cfg-plugin-root
 cn: Plugins
 
+dn: cn=7-Bit Clean,cn=Plugins,cn=config
+objectClass: top
+objectClass: ds-cfg-plugin
+objectClass: ds-cfg-7-bit-clean-plugin
+cn: 7-Bit Clean
+ds-cfg-plugin-class: org.opends.server.plugins.SevenBitCleanPlugin
+ds-cfg-plugin-enabled: false
+ds-cfg-plugin-type: ldifImport
+ds-cfg-plugin-type: preParseAdd
+ds-cfg-plugin-type: preParseModify
+ds-cfg-plugin-type: preParseModifyDN
+ds-cfg-7-bit-clean-attribute-type: uid
+ds-cfg-7-bit-clean-attribute-type: mail
+ds-cfg-7-bit-clean-attribute-type: userPassword
+
 dn: cn=Entry UUID,cn=Plugins,cn=config
 objectClass: top
 objectClass: ds-cfg-plugin
@@ -1470,18 +1485,6 @@
 ds-cfg-profile-directory: logs
 ds-cfg-profile-sample-interval: 10 milliseconds
 
-dn: cn=UID Unique Attribute,cn=Plugins,cn=config
-objectClass: top
-objectClass: ds-cfg-plugin
-objectClass: ds-cfg-unique-attribute-plugin
-cn: UID Unique Attribute
-ds-cfg-plugin-class: org.opends.server.plugins.UniqueAttributePlugin
-ds-cfg-plugin-enabled: false
-ds-cfg-plugin-type: preOperationAdd
-ds-cfg-plugin-type: preOperationModify
-ds-cfg-plugin-type: preOperationModifyDN
-ds-cfg-unique-attribute-type: uid
-
 dn: cn=Referential Integrity,cn=Plugins,cn=config
 objectClass: top
 objectClass: ds-cfg-plugin
@@ -1495,6 +1498,18 @@
 ds-cfg-referential-integrity-attribute-type: member
 ds-cfg-referential-integrity-attribute-type: uniqueMember
 
+dn: cn=UID Unique Attribute,cn=Plugins,cn=config
+objectClass: top
+objectClass: ds-cfg-plugin
+objectClass: ds-cfg-unique-attribute-plugin
+cn: UID Unique Attribute
+ds-cfg-plugin-class: org.opends.server.plugins.UniqueAttributePlugin
+ds-cfg-plugin-enabled: false
+ds-cfg-plugin-type: preOperationAdd
+ds-cfg-plugin-type: preOperationModify
+ds-cfg-plugin-type: preOperationModifyDN
+ds-cfg-unique-attribute-type: uid
+
 dn: cn=Root DNs,cn=config
 objectClass: top
 objectClass: ds-cfg-root-dn-base
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 4148466..5cfed67 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1646,6 +1646,12 @@
   NAME 'ds-cfg-max-blocked-write-time-limit'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
   X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.487
+  NAME 'ds-cfg-7-bit-clean-attribute-type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.488
+  NAME 'ds-cfg-7-bit-clean-base-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
   MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -2459,4 +2465,7 @@
   $ ds-cfg-referential-integrity-update-interval
   $ ds-cfg-referential-integrity-log-file )
   X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.167 NAME 'ds-cfg-7-bit-clean-plugin'
+  SUP ds-cfg-plugin STRUCTURAL MUST ds-cfg-7-bit-clean-attribute-type
+  MAY ds-cfg-7-bit-clean-base-dn X-ORIGIN 'OpenDS Directory Server' )
 
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/SevenBitCleanPluginConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/SevenBitCleanPluginConfiguration.xml
new file mode 100644
index 0000000..44d3f76
--- /dev/null
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/SevenBitCleanPluginConfiguration.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+! 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
+! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+! 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
+! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+!
+!
+!      Portions Copyright 2007 Sun Microsystems, Inc.
+! -->
+
+<adm:managed-object
+  name="seven-bit-clean-plugin"
+  plural-name="seven-bit-clean-plugins"
+  package="org.opends.server.admin.std"
+  extends="plugin"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+
+  <adm:synopsis>
+    The <adm:user-friendly-name /> may be used to ensure that values for a
+    specified set of attributes are 7-bit clean.  That is, for those attributes,
+    the values are not allowed to contain any bytes having the high-order bit
+    set, which is used to indicate the presence of non-ASCII characters.  Some
+    applications may not properly handle attribute values that contain non-ASCII
+    characters, and this plugin may help ensure that attributes used by those
+    applications do not contain characters which may cause problems in those
+    applications.
+  </adm:synopsis>
+
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:oid>1.3.6.1.4.1.26027.1.2.167</ldap:oid>
+      <ldap:name>ds-cfg-7-bit-clean-plugin</ldap:name>
+      <ldap:superior>ds-cfg-plugin</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:property-override name="plugin-class">
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          org.opends.server.plugins.SevenBitCleanPlugin
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+  </adm:property-override>
+
+  <adm:property-override name="plugin-type">
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>ldifimport</adm:value>
+        <adm:value>preparseadd</adm:value>
+        <adm:value>preparsemodify</adm:value>
+        <adm:value>preparsemodifydn</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+  </adm:property-override>
+
+  <adm:property name="attribute-type" mandatory="true" multi-valued="true">
+    <adm:synopsis>
+      Specifies the name or OID of an attribute type for which values should be
+      checked to ensure that they are 7-bit clean.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>uid</adm:value>
+        <adm:value>mail</adm:value>
+        <adm:value>userPassword</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:attribute-type />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.487</ldap:oid>
+        <ldap:name>ds-cfg-7-bit-clean-attribute-type</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="base-dn" mandatory="false" multi-valued="true">
+    <adm:synopsis>
+      Specifies the base DN below which the checking will be performed.  Any
+      attempt to update a value for one of the configured attributes below this
+      base DN must be 7-bit clean for the operation to be allowed.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          All entries below all public naming contexts will be checked.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:dn />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.488</ldap:oid>
+        <ldap:name>ds-cfg-7-bit-clean-base-dn</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+</adm:managed-object>
+
diff --git a/opendj-sdk/opends/src/messages/messages/plugin.properties b/opendj-sdk/opends/src/messages/messages/plugin.properties
index 747d78b..2739baa 100644
--- a/opendj-sdk/opends/src/messages/messages/plugin.properties
+++ b/opendj-sdk/opends/src/messages/messages/plugin.properties
@@ -319,42 +319,58 @@
  register the Referential Integrity plugin to be invoked as a %s plugin.  This \
  plugin type is not allowed for this plugin
 SEVERE_ERR_PLUGIN_REFERENT_CREATE_LOGFILE_82=An error occured during \
-   Referential Integity plugin initialization because log file creation \
-   failed:  %s
+ Referential Integity plugin initialization because log file creation \
+ failed:  %s
 SEVERE_ERR_PLUGIN_REFERENT_CLOSE_LOGFILE_83=An error occured closing the \
-  Referential Integrity plugin update logile: %s
+ Referential Integrity plugin update logile: %s
 SEVERE_ERR_PLUGIN_REFERENT_REPLACE_LOGFILE_84=An error occured replacing the \
-  Referential Integrity plugin update logile: %s
+ Referential Integrity plugin update logile: %s
 INFO_PLUGIN_REFERENT_LOGFILE_CHANGE_REQUIRES_RESTART_85=The file name that \
-  the Referential Integrity plugin logs changes to during background \
-  processing has been changed from %s to %s, but this change will not take \
-  effect until the server is restarted
+ the Referential Integrity plugin logs changes to during background \
+ processing has been changed from %s to %s, but this change will not take \
+ effect until the server is restarted
 INFO_PLUGIN_REFERENT_BACKGROUND_PROCESSING_UPDATE_INTERVAL_CHANGED_86=The \
-  Referential Integrity plugin background processing update interval has \
-  been changed from %s to %s, the new value will now be during background \
-  processing
+ Referential Integrity plugin background processing update interval has \
+ been changed from %s to %s, the new value will now be during background \
+ processing
 INFO_PLUGIN_REFERENT_BACKGROUND_PROCESSING_STOPPING_87=The Referential \
-  Integrity plugin background processing has been stopped
+ Integrity plugin background processing has been stopped
 INFO_PLUGIN_REFERENT_BACKGROUND_PROCESSING_STARTING_88=The Referential \
-  Integrity plugin has started background processing using the update \
-  interval %s
+ Integrity plugin has started background processing using the update \
+ interval %s
 SEVERE_ERR_PLUGIN_REFERENT_SEARCH_FAILED_89=The Referential \
-  Integrity plugin failed when performaing an internal search: %s
+ Integrity plugin failed when performaing an internal search: %s
 SEVERE_ERR_PLUGIN_REFERENT_MODIFY_FAILED_90=The Referential \
-  Integrity plugin failed when performing an internal modify on entry %s: %s
+ Integrity plugin failed when performing an internal modify on entry %s: %s
 MILD_ERR_PLUGIN_REFERENT_CANNOT_DECODE_STRING_AS_DN_91=The Referential \
-  Integrity plugin failed to decode a entry DN from the update log: %s
+ Integrity plugin failed to decode a entry DN from the update log: %s
 INFO_PLUGIN_REFERENT_SEARCH_NO_SUCH_OBJECT_92=The Referential Integrity \
-  plugin failed when performing a search because the base DN %s does not exist
+ plugin failed when performing a search because the base DN %s does not exist
 SEVERE_ERR_PLUGIN_REFERENT_INVALID_ATTRIBUTE_SYNTAX_93=An error occured \
-  in the Referential Integrity plugin while attempting to configure the \
-  attribute type %s which has a syntax OID of %s. A Referential Integrity \
-  attribute type must have a syntax OID of either \
-  1.3.6.1.4.1.1466.115.121.1.12 (for the distinguished name syntax) or \
-  1.3.6.1.4.1.1466.115.121.1.34 (for the name and optional uid syntax)
+ in the Referential Integrity plugin while attempting to configure the \
+ attribute type %s which has a syntax OID of %s. A Referential Integrity \
+ attribute type must have a syntax OID of either \
+ 1.3.6.1.4.1.1466.115.121.1.12 (for the distinguished name syntax) or \
+ 1.3.6.1.4.1.1466.115.121.1.34 (for the name and optional uid syntax)
 SEVERE_ERR_PLUGIN_REFERENT_SKIP_DELETE_PROCESSING_94=The Referential Integrity \
-  plugin will not process a post delete operation on entry %s because the core \
-  operation failed
+ plugin will not process a post delete operation on entry %s because the core \
+ operation failed
 SEVERE_ERR_PLUGIN_REFERENT_SKIP_MODIFY_DN_PROCESSING_95=The Referential \
-  Integrity plugin will not process a post modify DN operation on entry %s \
-  because the core operation failed
\ No newline at end of file
+ Integrity plugin will not process a post modify DN operation on entry %s \
+ because the core operation failed
+MILD_ERR_PLUGIN_7BIT_INVALID_PLUGIN_TYPE_96=The 7-bit clean plugin is \
+ configured with invalid plugin type %s.  Only the ldifImport, \
+ preOperationAdd, preOperationModify, and preOperationModifyDN plugin types \
+ are allowed
+MILD_ERR_PLUGIN_7BIT_CANNOT_DECODE_DN_97=An error occurred while trying to \
+ decode the DN of the target entry:  %s
+MILD_ERR_PLUGIN_7BIT_CANNOT_DECODE_ATTR_98=An error occurred while trying to \
+ decode attribute %s in the target entry:  %s
+MILD_ERR_PLUGIN_7BIT_CANNOT_DECODE_NEW_RDN_99=An error occurred while trying \
+ to decode the new RDN:  %s
+MILD_ERR_PLUGIN_7BIT_ADD_ATTR_NOT_CLEAN_100=The entry to be added included a \
+ value for attribute %s that was not 7-bit clean
+MILD_ERR_PLUGIN_7BIT_MODIFY_ATTR_NOT_CLEAN_101=The modify operation would \
+ have resulted in a value for attribute %s that was not 7-bit clean
+MILD_ERR_PLUGIN_7BIT_MODIFYDN_ATTR_NOT_CLEAN_102=The modify DN operation \
+ would have resulted in a value for attribute %s that was not 7-bit clean
diff --git a/opendj-sdk/opends/src/server/org/opends/server/plugins/SevenBitCleanPlugin.java b/opendj-sdk/opends/src/server/org/opends/server/plugins/SevenBitCleanPlugin.java
new file mode 100644
index 0000000..e7e0f32
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/plugins/SevenBitCleanPlugin.java
@@ -0,0 +1,552 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.plugins;
+
+
+
+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.SevenBitCleanPluginCfg;
+import org.opends.server.admin.std.server.PluginCfg;
+import org.opends.server.api.plugin.DirectoryServerPlugin;
+import org.opends.server.api.plugin.LDIFPluginResult;
+import org.opends.server.api.plugin.PluginType;
+import org.opends.server.api.plugin.PreParsePluginResult;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.RawAttribute;
+import org.opends.server.types.RawModification;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.operation.PreParseAddOperation;
+import org.opends.server.types.operation.PreParseModifyOperation;
+import org.opends.server.types.operation.PreParseModifyDNOperation;
+
+import static org.opends.messages.PluginMessages.*;
+
+
+
+/**
+ * This class implements a Directory Server plugin that can be used to ensure
+ * that the values for a specified set of attributes (optionally, below a
+ * specified set of base DNs) are 7-bit clean (i.e., contain only ASCII
+ * characters).
+ */
+public final class SevenBitCleanPlugin
+       extends DirectoryServerPlugin<SevenBitCleanPluginCfg>
+       implements ConfigurationChangeListener<SevenBitCleanPluginCfg>
+{
+  /**
+   * The bitmask that will be used to make the comparisons.
+   */
+  private static final byte MASK = (byte) 0x7F;
+
+
+
+  /**
+   * The result that should be returned if an imported entry fails the 7-bit
+   * clean check.
+   */
+  private static final LDIFPluginResult LDIF_FAILURE_RESULT =
+       new LDIFPluginResult(false, false);
+
+
+
+  /**
+   * The result that should be returned if a pre-parse operation fails the 7-bit
+   * clean check.
+   */
+  private static final PreParsePluginResult PRE_PARSE_FAILURE_RESULT =
+       new PreParsePluginResult(false, false, false, true);
+
+
+
+  // The current configuration for this plugin.
+  private SevenBitCleanPluginCfg currentConfig;
+
+
+
+  /**
+   * Creates a new instance of this Directory Server plugin.  Every plugin must
+   * implement a default constructor (it is the only one that will be used to
+   * create plugins defined in the configuration), and every plugin constructor
+   * must call {@code super()} as its first element.
+   */
+  public SevenBitCleanPlugin()
+  {
+    super();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void initializePlugin(Set<PluginType> pluginTypes,
+                                     SevenBitCleanPluginCfg configuration)
+         throws ConfigException
+  {
+    currentConfig = configuration;
+    configuration.addSevenBitCleanChangeListener(this);
+
+    // Make sure that the plugin has been enabled for the appropriate types.
+    for (PluginType t : pluginTypes)
+    {
+      switch (t)
+      {
+        case LDIF_IMPORT:
+        case PRE_PARSE_ADD:
+        case PRE_PARSE_MODIFY:
+        case PRE_PARSE_MODIFY_DN:
+          // These are acceptable.
+          break;
+
+
+        default:
+          Message message =
+              ERR_PLUGIN_7BIT_INVALID_PLUGIN_TYPE.get(t.toString());
+          throw new ConfigException(message);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void finalizePlugin()
+  {
+    currentConfig.removeSevenBitCleanChangeListener(this);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final LDIFPluginResult doLDIFImport(LDIFImportConfig importConfig,
+                                             Entry entry)
+  {
+    // Get the current configuration for this plugin.
+    SevenBitCleanPluginCfg config = currentConfig;
+
+
+    // Make sure that the entry is within the scope of this plugin.  While
+    // processing an LDIF import, we don't have access to the set of public
+    // naming contexts defined in the server, so if no explicit set of base DNs
+    // is defined, then assume that the entry is in scope.
+    Set<DN> baseDNs = config.getBaseDN();
+    if ((baseDNs != null) && (! baseDNs.isEmpty()))
+    {
+      boolean found = true;
+      for (DN baseDN : baseDNs)
+      {
+        if (baseDN.isAncestorOf(entry.getDN()))
+        {
+          found = true;
+          break;
+        }
+      }
+
+      if (! found)
+      {
+        // The entry is out of scope, so we won't process it.
+        return LDIFPluginResult.SUCCESS;
+      }
+    }
+
+
+    // Make sure all configured attributes have clean values.
+    for (AttributeType t : config.getAttributeType())
+    {
+      List<Attribute> attrList = entry.getAttribute(t);
+      if (attrList != null)
+      {
+        for (Attribute a : attrList)
+        {
+          for (AttributeValue v : a.getValues())
+          {
+            if (! is7BitClean(v.getValue()))
+            {
+              return LDIF_FAILURE_RESULT;
+            }
+          }
+        }
+      }
+    }
+
+
+    // If we've gotten here, then everything is acceptable.
+System.err.println("  The entry is acceptable");
+    return LDIFPluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreParsePluginResult
+                    doPreParse(PreParseAddOperation addOperation)
+  {
+    // Get the current configuration for this plugin.
+    SevenBitCleanPluginCfg config = currentConfig;
+
+
+    // If the entry is within the scope of this plugin, then make sure all
+    // configured attributes have clean values.
+    DN entryDN;
+    try
+    {
+      entryDN = DN.decode(addOperation.getRawEntryDN());
+    }
+    catch (DirectoryException de)
+    {
+      addOperation.appendErrorMessage(
+           ERR_PLUGIN_7BIT_CANNOT_DECODE_DN.get(de.getMessageObject()));
+      return PRE_PARSE_FAILURE_RESULT;
+    }
+
+    if (isInScope(config, entryDN))
+    {
+      for (RawAttribute rawAttr : addOperation.getRawAttributes())
+      {
+        Attribute a;
+        try
+        {
+          a = rawAttr.toAttribute();
+        }
+        catch (LDAPException le)
+        {
+          addOperation.appendErrorMessage(
+               ERR_PLUGIN_7BIT_CANNOT_DECODE_DN.get(le.getMessageObject()));
+          return PRE_PARSE_FAILURE_RESULT;
+        }
+
+        if (! config.getAttributeType().contains(a.getAttributeType()))
+        {
+          continue;
+        }
+
+        for (AttributeValue v : a.getValues())
+        {
+          if (! is7BitClean(v.getValue()))
+          {
+            addOperation.appendErrorMessage(
+                 ERR_PLUGIN_7BIT_ADD_ATTR_NOT_CLEAN.get(
+                      rawAttr.getAttributeType()));
+            return PRE_PARSE_FAILURE_RESULT;
+          }
+        }
+      }
+    }
+
+
+    // If we've gotten here, then everything is acceptable.
+    return PreParsePluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreParsePluginResult
+                    doPreParse(PreParseModifyOperation modifyOperation)
+  {
+    // Get the current configuration for this plugin.
+    SevenBitCleanPluginCfg config = currentConfig;
+
+
+    // If the target entry is within the scope of this plugin, then make sure
+    // all values that will be added during the modification will be acceptable.
+    DN entryDN;
+    try
+    {
+      entryDN = DN.decode(modifyOperation.getRawEntryDN());
+    }
+    catch (DirectoryException de)
+    {
+      modifyOperation.appendErrorMessage(
+           ERR_PLUGIN_7BIT_CANNOT_DECODE_DN.get(de.getMessageObject()));
+      return PRE_PARSE_FAILURE_RESULT;
+    }
+
+    if (isInScope(config, entryDN))
+    {
+      for (RawModification m : modifyOperation.getRawModifications())
+      {
+        switch (m.getModificationType())
+        {
+          case ADD:
+          case REPLACE:
+            // These are modification types that we will process.
+            break;
+          default:
+            // This is not a modifiation type that we will process.
+            continue;
+        }
+
+        RawAttribute rawAttr = m.getAttribute();
+        Attribute a;
+        try
+        {
+          a = rawAttr.toAttribute();
+        }
+        catch (LDAPException le)
+        {
+          modifyOperation.appendErrorMessage(
+               ERR_PLUGIN_7BIT_CANNOT_DECODE_DN.get(le.getMessageObject()));
+          return PRE_PARSE_FAILURE_RESULT;
+        }
+
+        if (! config.getAttributeType().contains(a.getAttributeType()))
+        {
+          continue;
+        }
+
+        for (AttributeValue v : a.getValues())
+        {
+          if (! is7BitClean(v.getValue()))
+          {
+            modifyOperation.appendErrorMessage(
+                 ERR_PLUGIN_7BIT_MODIFY_ATTR_NOT_CLEAN.get(
+                      rawAttr.getAttributeType()));
+            return PRE_PARSE_FAILURE_RESULT;
+          }
+        }
+      }
+    }
+
+
+    // If we've gotten here, then everything is acceptable.
+    return PreParsePluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreParsePluginResult
+                    doPreParse(PreParseModifyDNOperation modifyDNOperation)
+  {
+    // Get the current configuration for this plugin.
+    SevenBitCleanPluginCfg config = currentConfig;
+
+
+    // If the target entry is within the scope of this plugin, then make sure
+    // all values that will be added during the modification will be acceptable.
+    DN entryDN;
+    try
+    {
+      entryDN = DN.decode(modifyDNOperation.getRawEntryDN());
+    }
+    catch (DirectoryException de)
+    {
+      modifyDNOperation.appendErrorMessage(
+           ERR_PLUGIN_7BIT_CANNOT_DECODE_DN.get(de.getMessageObject()));
+      return PRE_PARSE_FAILURE_RESULT;
+    }
+
+    if (isInScope(config, entryDN))
+    {
+      ByteString rawNewRDN = modifyDNOperation.getRawNewRDN();
+
+      RDN newRDN;
+      try
+      {
+        newRDN = RDN.decode(rawNewRDN.stringValue());
+      }
+      catch (DirectoryException de)
+      {
+        modifyDNOperation.appendErrorMessage(
+             ERR_PLUGIN_7BIT_CANNOT_DECODE_NEW_RDN.get(de.getMessageObject()));
+        return PRE_PARSE_FAILURE_RESULT;
+      }
+
+      int numValues = newRDN.getNumValues();
+      for (int i=0; i < numValues; i++)
+      {
+        if (! config.getAttributeType().contains(newRDN.getAttributeType(i)))
+        {
+          continue;
+        }
+
+        if (! is7BitClean(newRDN.getAttributeValue(i).getValue()))
+        {
+          modifyDNOperation.appendErrorMessage(
+               ERR_PLUGIN_7BIT_MODIFYDN_ATTR_NOT_CLEAN.get(
+                    newRDN.getAttributeName(i)));
+          return PRE_PARSE_FAILURE_RESULT;
+        }
+      }
+    }
+
+
+    // If we've gotten here, then everything is acceptable.
+    return PreParsePluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * Indicates whether the provided DN is within the scope of this plugin.
+   *
+   * @param  config  The configuration to use when making the determination.
+   * @param  dn      The DN for which to make the determination.
+   *
+   * @return  {@code true} if the provided DN is within the scope of this
+   *          plugin, or {@code false} if  not.
+   */
+  private final boolean isInScope(SevenBitCleanPluginCfg config, DN dn)
+  {
+    Set<DN> baseDNs = config.getBaseDN();
+    if ((baseDNs == null) || baseDNs.isEmpty())
+    {
+      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+    }
+
+    boolean found = false;
+    for (DN baseDN: baseDNs)
+    {
+      if (dn.isDescendantOf(baseDN))
+      {
+        found = true;
+        break;
+      }
+    }
+
+    return found;
+  }
+
+
+
+  /**
+   * Indicates whether the provided value is 7-bit clean.
+   *
+   * @param  value  The value for which to make the determination.
+   *
+   * @return {@code true} if the provided value is 7-bit clean, or {@code false}
+   *         if it is not.
+   */
+  private final boolean is7BitClean(ByteString value)
+  {
+    for (byte b : value.value())
+    {
+      int i = (b & 0xFF);
+      if ((b & MASK) != b)
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public boolean isConfigurationAcceptable(PluginCfg configuration,
+                                           List<Message> unacceptableReasons)
+  {
+    SevenBitCleanPluginCfg cfg = (SevenBitCleanPluginCfg) configuration;
+    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+                      SevenBitCleanPluginCfg configuration,
+                      List<Message> unacceptableReasons)
+  {
+    boolean configAcceptable = true;
+
+    // Ensure that the set of plugin types is acceptable.
+    for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
+    {
+      switch (pluginType)
+      {
+        case LDIFIMPORT:
+        case PREPARSEADD:
+        case PREPARSEMODIFY:
+        case PREPARSEMODIFYDN:
+          // These are acceptable.
+          break;
+
+
+        default:
+          Message message = ERR_PLUGIN_7BIT_INVALID_PLUGIN_TYPE.get(
+                  pluginType.toString());
+          unacceptableReasons.add(message);
+          configAcceptable = false;
+      }
+    }
+
+    return configAcceptable;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+                                 SevenBitCleanPluginCfg configuration)
+  {
+    currentConfig = configuration;
+    return new ConfigChangeResult(ResultCode.SUCCESS, false);
+  }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/SevenBitCleanPluginTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/SevenBitCleanPluginTestCase.java
new file mode 100644
index 0000000..ea900c3
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/SevenBitCleanPluginTestCase.java
@@ -0,0 +1,717 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.plugins;
+
+
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.tools.LDAPModify;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * This class defines a set of tests for the
+ * org.opends.server.plugins.SevenBitCleanPlugin class.
+ */
+public class SevenBitCleanPluginTestCase
+       extends PluginTestCase
+{
+  /**
+   * The base64-encoded value that will be used as the password for entries that
+   * are not 7-bit clean.
+   */
+  public static final String BASE64_DIRTY_PASSWORD = "cORzc3f2cmQ=";
+
+
+
+  /**
+   * Ensures that the Directory Server is running.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @BeforeClass()
+  public void startServer()
+         throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to add a clean entry when the plugin
+   * is disabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddCleanAllowedDisabled()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String path = TestCaseUtils.createTempFile(
+      "dn: uid=test.user,o=test",
+      "changetype: add",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "uid: test.user",
+      "givenName: Test",
+      "sn: User",
+      "cn: Test User",
+      "mail: test.user@example.com",
+      "userPassword: password");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to add a dirty entry when the plugin
+   * is disabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddDirtyAllowedDisabled()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    String path = TestCaseUtils.createTempFile(
+      "dn: uid=test.user,o=test",
+      "changetype: add",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "uid: test.user",
+      "givenName: Test",
+      "sn: User",
+      "cn: Test User",
+      "mail: test.user@example.com",
+      "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to add a clean entry when the plugin
+   * is enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddCleanAllowedEnabled()
+         throws Exception
+  {
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      TestCaseUtils.initializeTestBackend(true);
+
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: add",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword: password");
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                   0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is not possible to add a dirty entry when the
+   * plugin is enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddDirtyRejectedEnabled()
+         throws Exception
+  {
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      TestCaseUtils.initializeTestBackend(true);
+
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: add",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertFalse(LDAPModify.mainModify(args, false, System.out, System.err) ==
+                  0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is not possible to add a dirty entry when the
+   * plugin is enabled but the entry being added is outside of the scope of the
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAddDirtyAcceptedEnabledOutsideScope()
+         throws Exception
+  {
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true",
+      "--set", "base-dn:dc=example,dc=com");
+
+    try
+    {
+      TestCaseUtils.initializeTestBackend(true);
+
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: add",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                   0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false",
+        "--remove", "base-dn:dc=example,dc=com");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to modify an entry to have a dirty
+   * password value with the plugin disabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDirtyAllowedDisabled()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntry(
+      "dn: uid=test.user,o=test",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "uid: test.user",
+      "givenName: Test",
+      "sn: User",
+      "cn: Test User",
+      "mail: test.user@example.com",
+      "userPassword: password");
+
+    String path = TestCaseUtils.createTempFile(
+      "dn: uid=test.user,o=test",
+      "changetype: modify",
+      "replace: userPassword",
+      "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests to ensure that it is not possible to modify an entry to have a dirty
+   * password value with the plugin enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDirtyRejectedEnabled()
+         throws Exception
+  {
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      TestCaseUtils.initializeTestBackend(true);
+
+      TestCaseUtils.addEntry(
+        "dn: uid=test.user,o=test",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword: password");
+
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: modify",
+        "replace: userPassword",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertFalse(LDAPModify.mainModify(args, false, System.out, System.err) ==
+                  0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to modify an entry containing a dirty
+   * password value when changing that value to be clean.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDirtyToCleanAllowedEnabled()
+         throws Exception
+  {
+      TestCaseUtils.initializeTestBackend(true);
+
+      TestCaseUtils.addEntry(
+        "dn: uid=test.user,o=test",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: modify",
+        "replace: userPassword",
+        "userPassword: clean");
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                   0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to modify an entry containing a dirty
+   * password in order to remove that value.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyRemoveDirtyValueAllowedEnabled()
+         throws Exception
+  {
+      TestCaseUtils.initializeTestBackend(true);
+
+      TestCaseUtils.addEntry(
+        "dn: uid=test.user,o=test",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: modify",
+        "delete: userPassword",
+        "userPassword:: " + BASE64_DIRTY_PASSWORD);
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                   0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to perform a modify DN operation to
+   * provide a dirty new RDN with the plugin disabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDNDirtyAllowedDisabled()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntry(
+      "dn: uid=test.user,o=test",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "uid: test.user",
+      "givenName: Test",
+      "sn: User",
+      "cn: Test User",
+      "mail: test.user@example.com",
+      "userPassword: password");
+
+    String path = TestCaseUtils.createTempFile(
+      "dn: uid=test.user,o=test",
+      "changetype: modrdn",
+      "newrdn: uid=p\\e4ssw\\f6rd",
+      "deleteoldrdn: 1");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests to ensure that it is not possible to perform a modify DN operation to
+   * provide a dirty new RDN with the plugin enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDNDirtyRejectedEnabled()
+         throws Exception
+  {
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      TestCaseUtils.initializeTestBackend(true);
+
+      TestCaseUtils.addEntry(
+        "dn: uid=test.user,o=test",
+        "objectClass: top",
+        "objectClass: person",
+        "objectClass: organizationalPerson",
+        "objectClass: inetOrgPerson",
+        "uid: test.user",
+        "givenName: Test",
+        "sn: User",
+        "cn: Test User",
+        "mail: test.user@example.com",
+        "userPassword: password");
+
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=test.user,o=test",
+        "changetype: modrdn",
+        "newrdn: uid=p\\e4ssw\\f6rd",
+        "deleteoldrdn: 1");
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertFalse(LDAPModify.mainModify(args, false, System.out, System.err) ==
+                  0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+
+
+
+  /**
+   * Tests to ensure that it is possible to perform a modify DN operation to
+   * provide a clean new RDN for a dirty entry with the plugin enabled.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testModifyDNDirtyToCleanAllowedEnabled()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestCaseUtils.addEntry(
+      "dn: uid=p\\e4ssw\\f6rd,o=test",
+      "objectClass: top",
+      "objectClass: person",
+      "objectClass: organizationalPerson",
+      "objectClass: inetOrgPerson",
+      "uid:: " + BASE64_DIRTY_PASSWORD,
+      "givenName: Test",
+      "sn: User",
+      "cn: Test User",
+      "mail: test.user@example.com",
+      "userPassword: password");
+
+    TestCaseUtils.dsconfig(
+      "set-plugin-prop",
+      "--plugin-name", "7-Bit Clean",
+      "--set", "enabled:true");
+
+    try
+    {
+      String path = TestCaseUtils.createTempFile(
+        "dn: uid=p\\e4ssw\\f6rd,o=test",
+        "changetype: modrdn",
+        "newrdn: uid=test.user",
+        "deleteoldrdn: 1");
+
+      String[] args =
+      {
+        "-h", "127.0.0.1",
+        "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+        "-D", "cn=Directory Manager",
+        "-w", "password",
+        "-f", path
+      };
+
+      assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                   0);
+    }
+    finally
+    {
+      TestCaseUtils.dsconfig(
+        "set-plugin-prop",
+        "--plugin-name", "7-Bit Clean",
+        "--set", "enabled:false");
+    }
+  }
+}
+

--
Gitblit v1.10.0