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

Matthew Swift
28.37.2012 b0d7717c48b14cd74dbe264bafd0bdb0c9c097c2
Fix OPENDJ-656: Add support for generating LDAP filters from printf style templates
2 files modified
152 ■■■■■ changed files
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java 81 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java 71 ●●●●● patch | view | raw | blame | history
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 = &quot;(|(cn=%s)(uid=user.%s))&quot;;
     * Filter filter = Filter.valueOf(template, &quot;alice&quot;, 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) {
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);
    }
}