From b0d7717c48b14cd74dbe264bafd0bdb0c9c097c2 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 28 Nov 2012 14:37:13 +0000
Subject: [PATCH] Fix OPENDJ-656: Add support for generating LDAP filters from printf style templates
---
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java | 81 +++++++++++++++++++++++++++
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java | 71 +++++++++++++++++++++++
2 files changed, 152 insertions(+), 0 deletions(-)
diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java
index 35b639a..875068d 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java
@@ -544,6 +544,50 @@
}
/**
+ * Returns the LDAP string representation of the provided filter assertion
+ * value in a form suitable for substitution directly into a filter string.
+ * This method may be useful in cases where a filter is to be constructed
+ * from a filter template using {@code String#format(String, Object...)}.
+ * The following example illustrates two approaches to constructing an
+ * equality filter:
+ *
+ * <pre>
+ * // This may contain user input.
+ * String assertionValue = ...;
+ *
+ * // Using the equality filter constructor:
+ * Filter filter = Filter.equality("cn", assertionValue);
+ *
+ * // Using a String template:
+ * String filterTemplate = "(cn=%s)";
+ * String filterString = String.format(filterTemplate,
+ * Filter.escapeAssertionValue(assertionValue));
+ * Filter filter = Filter.valueOf(filterString);
+ * </pre>
+ * If {@code assertionValue} is not an instance of {@code ByteString} then
+ * it will be converted using the {@link ByteString#valueOf(Object)} method.
+ *
+ * <p>
+ * <b>Note:</b> assertion values do not and should not be escaped before
+ * passing them to constructors like {@link #equality(String, Object)}.
+ * Escaping is only required when creating filter strings.
+ * <p>
+ * <b>Note:</b>
+ * @param assertionValue
+ * The assertion value.
+ * @return The LDAP string representation of the provided filter assertion
+ * value in a form suitable for substitution directly into a filter
+ * string.
+ */
+ public static String escapeAssertionValue(final Object assertionValue) {
+ Validator.ensureNotNull(assertionValue);
+ final ByteString bytes = ByteString.valueOf(assertionValue);
+ final StringBuilder builder = new StringBuilder(bytes.length());
+ valueToFilterString(builder, bytes);
+ return builder.toString();
+ }
+
+ /**
* Creates a new {@code extensible match} filter.
* <p>
* If {@code assertionValue} is not an instance of {@code ByteString} then
@@ -871,6 +915,43 @@
}
}
+ /**
+ * Creates a new filter using the provided filter template and unescaped
+ * assertion values. This method first escapes each of the assertion values
+ * and then substitutes them into the template using
+ * {@link String#format(String, Object...)}. Finally, the formatted string
+ * is parsed as an LDAP filter using {@link #valueOf(String)}.
+ * <p>
+ * This method may be useful in cases where the structure of a filter is not
+ * known at compile time, for example, it may be obtained from a
+ * configuration file. Example usage:
+ *
+ * <pre>
+ * String template = "(|(cn=%s)(uid=user.%s))";
+ * Filter filter = Filter.valueOf(template, "alice", 123);
+ * </pre>
+ *
+ * Any assertion values which are not instances of {@code ByteString} will
+ * be converted using the {@link ByteString#valueOf(Object)} method.
+ *
+ * @param template
+ * The filter template.
+ * @param assertionValues
+ * The assertion values to be substituted into the template.
+ * @return The formatted template parsed as a {@code Filter}.
+ * @throws LocalizedIllegalArgumentException
+ * If the formatted template is not a valid LDAP string
+ * representation of a filter.
+ */
+ public static Filter valueOf(final String template, final Object... assertionValues) {
+ final String[] assertionValueStrings = new String[assertionValues.length];
+ for (int i = 0; i < assertionValues.length; i++) {
+ assertionValueStrings[i] = escapeAssertionValue(assertionValues[i]);
+ }
+ final String filterString = String.format(template, (Object[]) assertionValueStrings);
+ return valueOf(filterString);
+ }
+
// Converts an assertion value to a substring filter.
private static Filter assertionValue2SubstringFilter(final String filterString,
final String attrType, final int equalPos, final int endPos) {
diff --git a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java
index 20d35f2..d6a0725 100644
--- a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java
+++ b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java
@@ -27,11 +27,13 @@
package org.forgerock.opendj.ldap;
+import static java.util.Arrays.asList;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.ArrayList;
+import java.util.List;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.testng.annotations.DataProvider;
@@ -264,4 +266,73 @@
final Matcher matcher = equal.matcher();
assertTrue(matcher.matches(entry).toBoolean());
}
+
+ @DataProvider
+ public Object[][] getAssertionValues() {
+ // Use List for assertion values instead of an array because a List has a
+ // String representation which can be displayed by debuggers, etc.
+
+ // @formatter:off
+ return new Object[][] {
+ {
+ "(objectClass=*)", asList(), "(objectClass=*)"
+ },
+ {
+ "(objectClass=*)", asList("dummy"), "(objectClass=*)"
+ },
+ {
+ "(objectClass=*)", asList("dummy", "dummy"), "(objectClass=*)"
+ },
+ {
+ "(cn=%s)", asList("dummy"), "(cn=dummy)"
+ },
+ {
+ "(|(cn=%s)(uid=user.%s))", asList("alice", (Object) 1234), "(|(cn=alice)(uid=user.1234))"
+ },
+ {
+ "(|(cn=%1$s)(sn=%1$s))", asList("alice"), "(|(cn=alice)(sn=alice))"
+ },
+ // Check escaping.
+ {
+ "(cn=%s)", asList("*"), "(cn=\\2A)"
+ },
+ {
+ "(|(cn=%1$s)(sn=%1$s))", asList("alice)(objectClass=*"),
+ "(|(cn=alice\\29\\28objectClass=\\2A)(sn=alice\\29\\28objectClass=\\2A))"
+ },
+ };
+ // @formatter:on
+ }
+
+ @Test(dataProvider = "getAssertionValues")
+ public void testValueOfTemplate(String template, List<?> assertionValues, String expected)
+ throws Exception {
+ Filter filter = Filter.valueOf(template, assertionValues.toArray());
+ assertEquals(filter.toString(), expected);
+ }
+
+ @DataProvider
+ public Object[][] getEscapeAssertionValues() {
+ // @formatter:off
+ return new Object[][] {
+ {
+ "dummy", "dummy"
+ },
+ {
+ 1234, "1234"
+ },
+ {
+ "*", "\\2A"
+ },
+ {
+ "alice)(objectClass=*", "alice\\29\\28objectClass=\\2A"
+ },
+ };
+ // @formatter:on
+ }
+
+ @Test(dataProvider = "getEscapeAssertionValues")
+ public void testEscapeAssertionValue(Object unescaped, String expected) throws Exception {
+ assertEquals(Filter.escapeAssertionValue(unescaped), expected);
+ }
}
--
Gitblit v1.10.0