From 080289c5388619b8b5059631c96a4d3b5922b6c1 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Fri, 07 Nov 2014 16:47:09 +0000
Subject: [PATCH] OPENDJ-1591 CR-5092 Re-implement UserPasswordEqualityMatchingRule to be  compatible with SDK matching rules and schema

---
 opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRule.java                             |  220 +++++++++++++++++-------------------
 opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleFactory.java                      |   25 +--
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleTest.java |    2 
 opendj3-server-dev/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleFactory.java                      |    1 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleTest.java |   85 +++++++------
 5 files changed, 163 insertions(+), 170 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleFactory.java b/opendj3-server-dev/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleFactory.java
index 97d938e..eafa835 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleFactory.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleFactory.java
@@ -44,7 +44,6 @@
 public final class AuthPasswordEqualityMatchingRuleFactory
         extends MatchingRuleFactory<MatchingRuleCfg>
 {
- //Associated Matching Rule.
   private org.forgerock.opendj.ldap.schema.MatchingRule matchingRule;
 
  /**
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRule.java b/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRule.java
index 2af5146..de7e7c2 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRule.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRule.java
@@ -26,102 +26,46 @@
  */
 package org.opends.server.schema;
 
-
-
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.ldap.Assertion;
 import org.forgerock.opendj.ldap.ByteSequence;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ConditionResult;
 import org.forgerock.opendj.ldap.DecodeException;
-import org.opends.server.api.EqualityMatchingRule;
+import org.forgerock.opendj.ldap.schema.MatchingRuleImpl;
+import org.forgerock.opendj.ldap.schema.Schema;
+import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
+import org.forgerock.opendj.ldap.spi.Indexer;
+import org.forgerock.opendj.ldap.spi.IndexingOptions;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.core.DirectoryServer;
 
-import static org.opends.server.schema.SchemaConstants.*;
-
-
+import static org.forgerock.opendj.ldap.Assertion.*;
+import static org.opends.server.core.DirectoryServer.*;
 
 /**
- * This class implements the userPasswordMatch matching rule, which can be used
+ * Implementation of the userPasswordMatch matching rule, which can be used
  * to determine whether a clear-text value matches an encoded password.
+ * <p>
+ * This matching rule serves a similar purpose to the equivalent
+ * AuthPasswordEqualityMatchingRule defined in RFC 3112 (http://tools.ietf.org/html/rfc3112).
  */
-class UserPasswordEqualityMatchingRule
-       extends EqualityMatchingRule
+class UserPasswordEqualityMatchingRule implements MatchingRuleImpl
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
-
-
-  /**
-   * Creates a new instance of this userPasswordMatch matching rule.
-   */
-  public UserPasswordEqualityMatchingRule()
-  {
-    super();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public Collection<String> getNames()
-  {
-    return Collections.singleton(EMR_USER_PASSWORD_NAME);
-  }
-
-
-  /**
-   * Retrieves the OID for this matching rule.
-   *
-   * @return  The OID for this matching rule.
-   */
-  @Override
-  public String getOID()
-  {
-    return EMR_USER_PASSWORD_OID;
-  }
-
-
-
-  /**
-   * Retrieves the description for this matching rule.
-   *
-   * @return  The description for this matching rule, or <CODE>null</CODE> if
-   *          there is none.
-   */
-  @Override
-  public String getDescription()
-  {
-    // There is no standard description for this matching rule.
-    return EMR_USER_PASSWORD_DESCRIPTION;
-  }
-
-
-
-  /**
-   * Retrieves the OID of the syntax with which this matching rule is
-   * associated.
-   *
-   * @return  The OID of the syntax with which this matching rule is associated.
-   */
-  @Override
-  public String getSyntaxOID()
-  {
-    return SYNTAX_USER_PASSWORD_OID;
-  }
-
-
+  private static final String EQUALITY_ID = "equality";
 
   /**
    * Retrieves the normalized form of the provided value, which is best suited
    * for efficiently performing matching operations on that value.
    *
-   * @param  value  The value to be normalized.
+   * @param schema The schema.
+   * @param value  The value to be normalized.
    *
    * @return  The normalized version of the provided value.
    *
@@ -129,13 +73,93 @@
    *                              the associated attribute syntax.
    */
   @Override
-  public ByteString normalizeAttributeValue(ByteSequence value)
+  public ByteString normalizeAttributeValue(Schema schema, ByteSequence value)
          throws DecodeException
   {
     // We will not alter the value in any way
     return value.toByteString();
   }
 
+  private final Collection<? extends Indexer> indexers = Collections.singleton(new Indexer()
+  {
+    @Override
+    public void createKeys(Schema schema, ByteSequence value, IndexingOptions options, Collection<ByteString> keys)
+        throws DecodeException
+    {
+      keys.add(normalizeAttributeValue(schema, value));
+    }
+
+    @Override
+    public String getIndexID()
+    {
+      return EQUALITY_ID;
+    }
+  });
+
+  /** {@inheritDoc} */
+  @Override
+  public Comparator<ByteSequence> comparator(Schema schema)
+  {
+    return ByteSequence.COMPARATOR;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) throws DecodeException
+  {
+    return new Assertion()
+    {
+      final ByteString normalizedAssertionValue = normalizeAttributeValue(schema, assertionValue);
+
+      @Override
+      public ConditionResult matches(final ByteSequence normalizedAttributeValue)
+      {
+        return valuesMatch(normalizedAttributeValue, normalizedAssertionValue);
+      }
+
+      @Override
+      public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException
+      {
+        return factory.createExactMatchQuery(EQUALITY_ID, normalizedAssertionValue);
+      }
+    };
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Assertion getSubstringAssertion(Schema schema, ByteSequence subInitial,
+      List<? extends ByteSequence> subAnyElements, ByteSequence subFinal) throws DecodeException
+  {
+    return UNDEFINED_ASSERTION;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Assertion getGreaterOrEqualAssertion(Schema schema, ByteSequence value) throws DecodeException
+  {
+    return UNDEFINED_ASSERTION;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Assertion getLessOrEqualAssertion(Schema schema, ByteSequence value) throws DecodeException
+  {
+    return UNDEFINED_ASSERTION;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isIndexingSupported()
+  {
+    return indexers.isEmpty();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Collection<? extends Indexer> getIndexers()
+  {
+    return indexers;
+  }
 
   /**
    * Indicates whether the provided attribute value should be considered a match
@@ -152,30 +176,24 @@
    *          match for the provided assertion value, or <CODE>false</CODE> if
    *          not.
    */
-  @Override
-  public ConditionResult valuesMatch(ByteSequence attributeValue,
-                                     ByteSequence assertionValue)
+  private ConditionResult valuesMatch(ByteSequence attributeValue, ByteSequence assertionValue)
   {
     // We must be able to decode the attribute value using the user password
     // syntax.
     String[] userPWComponents;
     try
     {
-      userPWComponents =
-           UserPasswordSyntax.decodeUserPassword(attributeValue.toString());
+      userPWComponents = UserPasswordSyntax.decodeUserPassword(attributeValue.toString());
     }
     catch (Exception e)
     {
       logger.traceException(e);
-
       return ConditionResult.FALSE;
     }
 
-
-    // The first element of the array will be the scheme.  Make sure that we
-    // support the requested scheme.
-    PasswordStorageScheme storageScheme =
-         DirectoryServer.getPasswordStorageScheme(userPWComponents[0]);
+    // The first element of the array will be the scheme.
+    // Make sure that we support the requested scheme.
+    PasswordStorageScheme<?> storageScheme =  getPasswordStorageScheme(userPWComponents[0]);
     if (storageScheme == null)
     {
       // It's not a scheme that we can support.
@@ -183,34 +201,8 @@
     }
 
     // We support the scheme, so make the determination.
-    return ConditionResult.valueOf(storageScheme.passwordMatches(
-        assertionValue, ByteString.valueOf(userPWComponents[1])));
-  }
-
-
-
-  /**
-   * Generates a hash code for the provided attribute value.  This version of
-   * the method will simply create a hash code from the normalized form of the
-   * attribute value.  For matching rules explicitly designed to work in cases
-   * where byte-for-byte comparisons of normalized values is not sufficient for
-   * determining equality (e.g., if the associated attribute syntax is based on
-   * hashed or encrypted values), then this method must be overridden to provide
-   * an appropriate implementation for that case.
-   *
-   * @param  attributeValue  The attribute value for which to generate the hash
-   *                         code.
-   *
-   * @return  The hash code generated for the provided attribute value.
-   */
-  @Override
-  public int generateHashCode(ByteSequence attributeValue)
-  {
-    // Because of the variable encoding that may be used, we have no way of
-    // comparing two user password values by hash code and therefore we'll
-    // always return the same value so that the valuesMatch method will be
-    // invoked to make the determination.
-    return 1;
+    return ConditionResult.valueOf(
+        storageScheme.passwordMatches(assertionValue, ByteString.valueOf(userPWComponents[1])));
   }
 }
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleFactory.java b/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleFactory.java
index a276346..300f032 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleFactory.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleFactory.java
@@ -24,8 +24,6 @@
  *      Copyright 2008 Sun Microsystems, Inc.
  *      Portions Copyright 2014 ForgeRock AS
  */
-
-
 package org.opends.server.schema;
 
 import java.util.Collection;
@@ -36,20 +34,18 @@
 import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.schema.CoreSchema;
 import org.forgerock.opendj.ldap.schema.MatchingRule;
+import org.forgerock.opendj.ldap.schema.SchemaBuilder;
 import org.opends.server.types.InitializationException;
 
+import static org.opends.server.schema.SchemaConstants.*;
+
 /**
  * This class is a factory class for UserPasswordExactEqualityMatchingRule.
  */
-public final class UserPasswordEqualityMatchingRuleFactory
-        extends MatchingRuleFactory<MatchingRuleCfg>
+public final class UserPasswordEqualityMatchingRuleFactory extends MatchingRuleFactory<MatchingRuleCfg>
 {
-  //Associated Matching Rule.
   private MatchingRule matchingRule;
 
-
-
-
  /**
   * {@inheritDoc}
   */
@@ -57,14 +53,15 @@
  public final void initializeMatchingRule(MatchingRuleCfg configuration)
          throws ConfigException, InitializationException
  {
-   // TODO: either delete completely UserPasswordEqualityMatchingRule or re-implement it
-   // using SDK classes
-   // Meanwhile, just returning UserPasswordExactEqualityMatchingRule instead
-   matchingRule = CoreSchema.getInstance().getMatchingRule("1.3.6.1.4.1.26027.1.4.2");
+   matchingRule = new SchemaBuilder(CoreSchema.getInstance())
+       .buildMatchingRule(EMR_USER_PASSWORD_OID)
+         .names(EMR_USER_PASSWORD_NAME)
+         .syntaxOID(SYNTAX_USER_PASSWORD_OID).description(EMR_USER_PASSWORD_DESCRIPTION)
+         .implementation(new UserPasswordEqualityMatchingRule())
+         .addToSchema()
+       .toSchema().getMatchingRule(EMR_USER_PASSWORD_OID);
  }
 
-
-
  /**
   * {@inheritDoc}
   */
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleTest.java
index cdd1269..acc491c 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleTest.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/AuthPasswordEqualityMatchingRuleTest.java
@@ -137,7 +137,7 @@
   }
 
 
-  protected MatchingRule getRule()
+  private MatchingRule getRule()
   {
     AuthPasswordEqualityMatchingRuleFactory factory = new AuthPasswordEqualityMatchingRuleFactory();
     try
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleTest.java
index 8581471..13fec0d 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleTest.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/schema/UserPasswordEqualityMatchingRuleTest.java
@@ -32,26 +32,22 @@
 import org.opends.server.config.ConfigEntry;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.extensions.SaltedMD5PasswordStorageScheme;
+import org.forgerock.opendj.ldap.Assertion;
 import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.schema.CoreSchema;
+import org.forgerock.opendj.ldap.ConditionResult;
+import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.schema.MatchingRule;
 import org.opends.server.types.DN;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.*;
 
-/**
- * Test the AuthPasswordEqualityMatchingRule.
- */
-public class UserPasswordEqualityMatchingRuleTest extends
-    EqualityMatchingRuleTest
+
+@SuppressWarnings("javadoc")
+public class UserPasswordEqualityMatchingRuleTest extends SchemaTestCase
 {
 
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   @DataProvider(name="equalitymatchingrules")
   public Object[][] createEqualityMatchingRuleTest()
   {
@@ -60,10 +56,6 @@
     };
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   @DataProvider(name="equalityMatchingRuleInvalidValues")
   public Object[][] createEqualityMatchingRuleInvalidValues()
   {
@@ -75,41 +67,32 @@
     ByteString bytePassword = ByteString.valueOf(password);
     SaltedMD5PasswordStorageScheme scheme = new SaltedMD5PasswordStorageScheme();
 
-    ConfigEntry configEntry =
-       DirectoryServer.getConfigEntry(
+    ConfigEntry configEntry = DirectoryServer.getConfigEntry(
            DN.valueOf("cn=Salted MD5,cn=Password Storage Schemes,cn=config"));
 
     SaltedMD5PasswordStorageSchemeCfg configuration =
       AdminTestCaseUtils.getConfiguration(
           SaltedMD5PasswordStorageSchemeCfgDefn.getInstance(),
-          configEntry.getEntry()
-          );
+          configEntry.getEntry());
 
     scheme.initializePasswordStorageScheme(configuration);
 
     ByteString encodedAuthPassword =
          scheme.encodePasswordWithScheme(bytePassword);
 
-     return new Object[] {
-         encodedAuthPassword.toString(), password, true};
+     return new Object[] { encodedAuthPassword.toString(), password, true };
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   @DataProvider(name="valuesMatch")
   public Object[][] createValuesMatch()
   {
     try
     {
       return new Object[][] {
-        // TODO : re-enable when matching rule is re-implemented
-        // with SDK
-//        generateValues("password"),
-//        {"password", "something else", false},
-//        {"password", "{wong}password", false},
-//        {"password", "{SMD5}wrong",    false}
+        generateValues("password"),
+        {"password", "something else", false},
+        {"password", "{wong}password", false},
+        {"password", "{SMD5}wrong",    false}
       };
     }
     catch (Exception e)
@@ -118,16 +101,38 @@
     }
   }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  protected MatchingRule getRule()
+  @Test(dataProvider= "equalityMatchingRuleInvalidValues", expectedExceptions = { DecodeException.class })
+  public void equalityMatchingRulesInvalidValues(String value) throws Exception
   {
-    // TODO: temporary change to make test pass before
-    // re-implementing matching rule with SDK classes.
-    // new UserPasswordEqualityMatchingRule();
-    return CoreSchema.getInstance().getMatchingRule(SchemaConstants.EMR_USER_PASSWORD_EXACT_OID);
+    getRule().normalizeAttributeValue(ByteString.valueOf(value));
+  }
+
+  /**
+   * Test the valuesMatch method used for extensible filters.
+   */
+  @Test(dataProvider= "valuesMatch")
+  public void testValuesMatch(String value1, String value2, Boolean result) throws Exception
+  {
+    MatchingRule rule = getRule();
+
+    ByteString normalizedValue1 = rule.normalizeAttributeValue(ByteString.valueOf(value1));
+    Assertion assertion = rule.getAssertion(ByteString.valueOf(value2));
+
+    ConditionResult liveResult = assertion.matches(normalizedValue1);
+    assertEquals(liveResult, ConditionResult.valueOf(result));
+  }
+
+  private MatchingRule getRule()
+  {
+    UserPasswordEqualityMatchingRuleFactory factory = new UserPasswordEqualityMatchingRuleFactory();
+    try
+    {
+      factory.initializeMatchingRule(null);
+    }
+    catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+    return factory.getMatchingRules().iterator().next();
   }
 }
 

--
Gitblit v1.10.0