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

neil_a_wilson
05.28.2007 23139785b018c00daaf3eb24db7ef2d96a7ed921
Fix a nummber of issues related to search filter processing:

Issue #704: SearchFilter.equals doesn't handle extensible match edge case
Issue #705: SearchFilter.equals should normalize substring filters
Issue #706: SearchFilter.equals ignores attribute options
Issue #918: Extensible match filters must have attr descrption or matching
rule ID
Issue #1901: SearchFilter.createFilterFromString can't handle extensible
match filters using dnAttrs with a matching rule ID instead of
an attribute description
6 files modified
367 ■■■■■ changed files
opends/src/server/org/opends/server/messages/CoreMessages.java 50 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/ProtocolMessages.java 17 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/SearchFilter.java 270 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java 8 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java 11 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6222,6 +6222,41 @@
  /**
   * The message ID for the message that will be used if an attempt is made to
   * create an extensible match search filter without providing either an
   * attribute type or a matching rule ID.  This does not take any arguments.
   */
  public static final int
       MSGID_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR =
            CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 625;
  /**
   * The message ID for the message that will be used if a filter string cannot
   * be decoded because it contained an extensible match component that did not
   * include either an attribute description or a matching rule ID.  This takes
   * two arguments, which are the filter string and the start position of the
   * extensible match component within that filter string.
   */
  public static final int MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR =
            CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 626;
  /**
   * The message ID for the message that will be used if a filter string cannot
   * be decoded because it contained an extensible match component that
   * referenced an unknown matching rule ID.  This takes three arguments, which
   * are the filter string, the start position of the extensible match component
   * within that filter string, and the unknown matching rule ID.
   */
  public static final int MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_SUCH_MR =
            CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 627;
  /**
   * Associates a set of generic messages with the message IDs defined
   * in this class.
   */
@@ -6686,6 +6721,11 @@
                    "exception was caught during processing:  %s");
    registerMessage(MSGID_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR,
                    "Unable to create an extensible match search filter " +
                    "using the provided information because it did not " +
                    "contain either an attribute type or a matching rule " +
                    "ID.  At least one of these must be provided");
    registerMessage(MSGID_SEARCH_FILTER_NULL,
                    "Unable to decode the provided filter string as a search " +
                    "filter because the provided string was empty or null");
@@ -6729,6 +6769,16 @@
                    "because the extensible match component starting at " +
                    "position %d did not have a colon to denote the end of " +
                    "the attribute type name");
    registerMessage(MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR,
                    "The provided search filter \"%s\" could not be decoded " +
                    "because the extensible match component starting at " +
                    "position %d did not contain either an attribute " +
                    "description or a matching rule ID.  At least one of " +
                    "these must be provided");
    registerMessage(MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_SUCH_MR,
                    "The provided search filter \"%s\" could not be decoded " +
                    "because the extensible match component starting at " +
                    "position %d referenced an unknown matching rule %s");
    registerMessage(MSGID_SEARCH_FILTER_NOT_EXACTLY_ONE,
                    "The provided search filter \"%s\" could not be decoded " +
                    "because the NOT filter between positions %d and %d " +
opends/src/server/org/opends/server/messages/ProtocolMessages.java
@@ -4638,6 +4638,17 @@
  /**
   * The message ID for the message that will be used if a search filter string
   * includes an extensible match component without either an attribute
   * description or a matching rule ID.  This takes a single argument, which is
   * the filter string.
   */
  public static final int MSGID_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR =
       CATEGORY_MASK_PROTOCOL | SEVERITY_MASK_MILD_ERROR | 430;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -5418,6 +5429,12 @@
                    "because the extensible match component starting at " +
                    "position %d did not have a colon to denote the end of " +
                    "the attribute type name");
    registerMessage(MSGID_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR,
                    "The provided search filter \"%s\" could not be decoded " +
                    "because the extensible match component starting at " +
                    "position %d did not include either an attribute " +
                    "description or a matching rule ID.  At least one of " +
                    "them must be provided");
    registerMessage(MSGID_LDAP_FILTER_NOT_EXACTLY_ONE,
                    "The provided search filter \"%s\" could not be decoded " +
                    "because the NOT filter between positions %d and %d " +
opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java
@@ -1740,6 +1740,17 @@
      value = new ASN1OctetString(valueBytes);
    }
    // Make sure that the filter has at least one of an attribute description
    // and/or a matching rule ID.
    if ((attributeType == null) && (matchingRuleID == null))
    {
      int    msgID   = MSGID_LDAP_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR;
      String message = getMessage(msgID, filterString, startPos);
      throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message);
    }
    return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
                          attributeType, value, null, null, null,
                          matchingRuleID, dnAttributes);
opends/src/server/org/opends/server/types/SearchFilter.java
@@ -30,6 +30,7 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -39,6 +40,7 @@
import java.util.Collections;
import org.opends.server.api.MatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
@@ -521,13 +523,27 @@
   *                         attributes for extensible match filters.
   *
   * @return  The constructed search filter.
   *
   * @throws  DirectoryException  If the provided information is not
   *                              sufficient to create an extensible
   *                              match filter.
   */
  public static SearchFilter createExtensibleMatchFilter(
                                  AttributeType attributeType,
                                  AttributeValue assertionValue,
                                  String matchingRuleID,
                                  boolean dnAttributes)
         throws DirectoryException
  {
    if ((attributeType == null) && (matchingRuleID == null))
    {
      int msgID =
           MSGID_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR;
      String message = getMessage(msgID);
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message,
                                   msgID);
    }
    return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null,
                            attributeType, null, assertionValue, null,
                            null, null, matchingRuleID, dnAttributes);
@@ -552,6 +568,10 @@
   *                           filters.
   *
   * @return  The constructed search filter.
   *
   * @throws  DirectoryException  If the provided information is not
   *                              sufficient to create an extensible
   *                              match filter.
   */
  public static SearchFilter createExtensibleMatchFilter(
                                  AttributeType attributeType,
@@ -559,7 +579,17 @@
                                  AttributeValue assertionValue,
                                  String matchingRuleID,
                                  boolean dnAttributes)
         throws DirectoryException
  {
    if ((attributeType == null) && (matchingRuleID == null))
    {
      int msgID =
           MSGID_SEARCH_FILTER_CREATE_EXTENSIBLE_MATCH_NO_AT_OR_MR;
      String message = getMessage(msgID);
      throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message,
                                   msgID);
    }
    return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null,
                            attributeType, attributeOptions,
                            assertionValue, null, null, null,
@@ -2078,8 +2108,43 @@
      userValue = new ASN1OctetString(valueBytes);
    }
    AttributeValue value = new AttributeValue(attributeType,
                                              userValue);
    // Make sure that the filter contains at least one of an attribute
    // type or a matching rule ID.  Also, construct the appropriate
    // attribute  value.
    AttributeValue value;
    if (attributeType == null)
    {
      if (matchingRuleID == null)
      {
        int msgID = MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_AD_OR_MR;
        String message = getMessage(msgID, filterString, startPos);
        throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                     message, msgID);
      }
      else
      {
        MatchingRule mr = DirectoryServer.getMatchingRule(
                               toLowerCase(matchingRuleID));
        if (mr == null)
        {
          int msgID = MSGID_SEARCH_FILTER_EXTENSIBLE_MATCH_NO_SUCH_MR;
          String message = getMessage(msgID, filterString, startPos,
                                      matchingRuleID);
          throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
                                       message, msgID);
        }
        else
        {
          value = new AttributeValue(userValue,
                                     mr.normalizeValue(userValue));
        }
      }
    }
    else
    {
      value = new AttributeValue(attributeType, userValue);
    }
    return new SearchFilter(FilterType.EXTENSIBLE_MATCH, null, null,
                            attributeType, attributeOptions, value,
                            null, null, null, matchingRuleID,
@@ -3472,6 +3537,7 @@
        return notComponent.equals(f.notComponent);
      case EQUALITY:
        return (attributeType.equals(f.attributeType) &&
                optionsEqual(attributeOptions, f.attributeOptions) &&
                assertionValue.equals(f.assertionValue));
      case SUBSTRING:
        if (! attributeType.equals(f.attributeType))
@@ -3479,6 +3545,18 @@
          return false;
        }
        SubstringMatchingRule smr =
             attributeType.getSubstringMatchingRule();
        if (smr == null)
        {
          return false;
        }
        if (! optionsEqual(attributeOptions, f.attributeOptions))
        {
          return false;
        }
        if (subInitialElement == null)
        {
          if (f.subInitialElement != null)
@@ -3488,7 +3566,19 @@
        }
        else
        {
          if (! subInitialElement.equals(f.subInitialElement))
          try
          {
            ByteString nSI1 =
                 smr.normalizeSubstring(subInitialElement);
            ByteString nSI2 =
                 smr.normalizeSubstring(f.subInitialElement);
            if (! Arrays.equals(nSI1.value(), nSI2.value()))
            {
              return false;
            }
          }
          catch (Exception e)
          {
            return false;
          }
@@ -3503,7 +3593,19 @@
        }
        else
        {
          if (! subFinalElement.equals(f.subFinalElement))
          try
          {
            ByteString nSF1 =
                 smr.normalizeSubstring(subFinalElement);
            ByteString nSF2 =
                 smr.normalizeSubstring(f.subFinalElement);
            if (! Arrays.equals(nSF1.value(), nSF2.value()))
            {
              return false;
            }
          }
          catch (Exception e)
          {
            return false;
          }
@@ -3514,10 +3616,22 @@
          return false;
        }
        for (int i = 0; i < subAnyElements.size(); i++) {
          ByteString sub1 = subAnyElements.get(i);
          ByteString sub2 = f.subAnyElements.get(i);
          if (!sub1.equals(sub2)) {
        for (int i = 0; i < subAnyElements.size(); i++)
        {
          try
          {
            ByteString nSA1 =
                 smr.normalizeSubstring(subAnyElements.get(i));
            ByteString nSA2 =
                 smr.normalizeSubstring(f.subAnyElements.get(i));
            if (! Arrays.equals(nSA1.value(), nSA2.value()))
            {
              return false;
            }
          }
          catch (Exception e)
          {
            return false;
          }
        }
@@ -3525,14 +3639,18 @@
        return true;
      case GREATER_OR_EQUAL:
        return (attributeType.equals(f.attributeType) &&
                optionsEqual(attributeOptions, f.attributeOptions) &&
                assertionValue.equals(f.assertionValue));
      case LESS_OR_EQUAL:
        return (attributeType.equals(f.attributeType) &&
                optionsEqual(attributeOptions, f.attributeOptions) &&
                assertionValue.equals(f.assertionValue));
      case PRESENT:
        return (attributeType.equals(f.attributeType));
        return (attributeType.equals(f.attributeType) &&
                optionsEqual(attributeOptions, f.attributeOptions));
      case APPROXIMATE_MATCH:
        return (attributeType.equals(f.attributeType) &&
                optionsEqual(attributeOptions, f.attributeOptions) &&
                assertionValue.equals(f.assertionValue));
      case EXTENSIBLE_MATCH:
        if (attributeType == null)
@@ -3548,6 +3666,11 @@
          {
            return false;
          }
          if (! optionsEqual(attributeOptions, f.attributeOptions))
          {
            return false;
          }
        }
        if (dnAttributes != f.dnAttributes)
@@ -3579,9 +3702,39 @@
        }
        else
        {
          if (! assertionValue.equals(f.assertionValue))
          if (matchingRuleID == null)
          {
            return false;
            if (! assertionValue.equals(f.assertionValue))
            {
              return false;
            }
          }
          else
          {
            MatchingRule mr =
                 DirectoryServer.getMatchingRule(
                      toLowerCase(matchingRuleID));
            if (mr == null)
            {
              return false;
            }
            else
            {
              try
              {
                ConditionResult cr = mr.valuesMatch(
                     mr.normalizeValue(assertionValue.getValue()),
                     mr.normalizeValue(f.assertionValue.getValue()));
                if (cr != ConditionResult.TRUE)
                {
                  return false;
                }
              }
              catch (Exception e)
              {
                return false;
              }
            }
          }
        }
@@ -3592,6 +3745,57 @@
  }
  /**
   * Indicates whether the two provided sets of attribute options
   * should be considered equal.
   *
   * @param  options1  The first set of attribute options for which to
   *                   make the determination.
   * @param  options2  The second set of attribute options for which
   *                   to make the determination.
   *
   * @return  {@code true} if the sets of attribute options are equal,
   *          or {@code false} if not.
   */
  private static boolean optionsEqual(Set<String> options1,
                                      Set<String> options2)
  {
    if ((options1 == null) || options1.isEmpty())
    {
      return ((options2 == null) || options2.isEmpty());
    }
    else if ((options2 == null) || options2.isEmpty())
    {
      return false;
    }
    else
    {
      if (options1.size() != options2.size())
      {
        return false;
      }
      HashSet<String> lowerOptions =
           new HashSet<String>(options1.size());
      for (String option : options1)
      {
        lowerOptions.add(toLowerCase(option));
      }
      for (String option : options2)
      {
        if (! lowerOptions.remove(toLowerCase(option)))
        {
          return false;
        }
      }
      return lowerOptions.isEmpty();
    }
  }
  /**
   * Retrieves the hash code for this search filter.
   *
@@ -3618,22 +3822,60 @@
      case SUBSTRING:
        hashCode = attributeType.hashCode();
        SubstringMatchingRule smr =
             attributeType.getSubstringMatchingRule();
        if (subInitialElement != null)
        {
          hashCode += subInitialElement.hashCode();
          if (smr == null)
          {
            hashCode += subInitialElement.hashCode();
          }
          else
          {
            try
            {
              hashCode += smr.normalizeSubstring(
                               subInitialElement).hashCode();
            }
            catch (Exception e) {}
          }
        }
        if (subAnyElements != null)
        {
          for (ByteString e : subAnyElements)
          {
            hashCode += e.hashCode();
            if (smr == null)
            {
              hashCode += e.hashCode();
            }
            else
            {
              try
              {
                hashCode += smr.normalizeSubstring(e).hashCode();
              }
              catch (Exception e2) {}
            }
          }
        }
        if (subFinalElement != null)
        {
          hashCode += subFinalElement.hashCode();
          if (smr == null)
          {
            hashCode += subFinalElement.hashCode();
          }
          else
          {
            try
            {
              hashCode +=
                   smr.normalizeSubstring(subFinalElement).hashCode();
            }
            catch (Exception e) {}
          }
        }
        return hashCode;
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java
@@ -73,6 +73,7 @@
      { "((uid=user.0))", null },
      { "(&&(uid=user.0))", null },
      { "!uid=user.0", null },
      { "(:dn:=Sally)", null },
    };
  }
@@ -136,10 +137,6 @@
                                                null,
                                           new ASN1OctetString("\\John* (Doe)"),
                                                false);
    LDAPFilter extensible6 = LDAPFilter.createExtensibleFilter(null,
                                                null,
                                           new ASN1OctetString(""),
                                                true);
    ArrayList<RawFilter> list1 = new ArrayList<RawFilter>();
    list1.add(equal);
@@ -186,8 +183,6 @@
        { "(:2.4.6.8.19:=\\5cJohn\\2a \\28Doe\\29)", extensible5 },
        { "(:dn:=)", extensible6 },
        { "(&(objectClass=\\5ctest\\2a\\28Value\\29)(sn~=\\5ctest\\2a\\28Value\\29))",
            LDAPFilter.createANDFilter(list1) },
@@ -296,7 +291,6 @@
          "(cn=*n)" +
          "(cn=n*)" +
          "(cn=n*n)" +
          "(:dn:=Sally)" +
          "(:dn:1.2.3.4:=Doe)" +
          "(cn:2.4.6.8.10:=)" +
        ")");
opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
@@ -287,6 +287,7 @@
            {"(sn=\\H1)"},
            {"(!(sn=test)(cn=test))"},
            {"(!)"},
            {"(:dn:=Sally)"}
    };
  }
@@ -1024,10 +1025,10 @@
          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=Smith)", true, true},
          // This demonstrates bug 704.
//          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
          {"(sn:caseExactMatch:=Smith)", "(sn:caseExactMatch:=smith)", false, false},
          // TODO: open a bug for this.
//          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
          // Ensure that ":dn:" is treated in a case-insensitive manner.
          {"(:dn:caseExactMatch:=example)", "(:DN:caseExactMatch:=example)", true, true}, // ? String not match
          // 2.5.4.4 is 'sn'
          {"(2.5.4.4=Smith)", "(2.5.4.4=Smith)", true, true},
@@ -1036,11 +1037,11 @@
          {"(sn;lang-en=Smith)", "(sn;lang-en=Smith)", true, true},
          // This demonstrates bug 706
//          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
          {"(sn;lang-en=Smith)", "(sn=Smith)", false, false},
          // This demonstrates bug 705.
//          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
          {"(sn=s*t*h)", "(sn=S*T*H)", true, false},
          // These should be case sensitive
          {"(labeledURI=http://opends.org)", "(labeledURI=http://OpenDS.org)", false, false},