From 0fca8e8c7e19434aeb20afda1db6c1fcae66a2d8 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 05 Jul 2007 20:28:03 +0000
Subject: [PATCH] Fix a nummber of issues related to search filter processing:
---
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java | 50 +++++++
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java | 11 +
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java | 11
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java | 8 -
opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java | 270 ++++++++++++++++++++++++++++++++++++--
opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java | 17 ++
6 files changed, 341 insertions(+), 26 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index 48da4b4..499793c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/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 " +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java
index aa9a360..7c1c813 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java
+++ b/opendj-sdk/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 " +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java
index 4f0325c..589565e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPFilter.java
+++ b/opendj-sdk/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);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java b/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java
index 6e6d8b9..056c53c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/SearchFilter.java
+++ b/opendj-sdk/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;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java
index 448c76c..2ecf493 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestLDAPFilter.java
+++ b/opendj-sdk/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:=)" +
")");
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
index 4ef6643..5d92167 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/SearchFilterTests.java
+++ b/opendj-sdk/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},
--
Gitblit v1.10.0