opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java
@@ -148,6 +148,35 @@ return new AVA(attribute, value); } static void escapeAttributeValue(final String str, final StringBuilder builder) { if (str.length() > 0) { char c = str.charAt(0); int startPos = 0; if ((c == ' ') || (c == '#')) { builder.append('\\'); builder.append(c); startPos = 1; } final int length = str.length(); for (int si = startPos; si < length; si++) { c = str.charAt(si); if (c < ' ') { for (final byte b : getBytes(String.valueOf(c))) { builder.append('\\'); builder.append(StaticUtils.byteToLowerHex(b)); } } else { if ((c == ' ' && si == length - 1) || (c == '"' || c == '+' || c == ',' || c == ';' || c == '<' || c == '=' || c == '>' || c == '\\' || c == '\u0000')) { builder.append('\\'); } builder.append(c); } } } } private static void appendHexChars(final SubstringReader reader, final StringBuilder valueBuffer, final StringBuilder hexBuffer) throws DecodeException { final int length = hexBuffer.length(); @@ -720,34 +749,7 @@ builder.append("#"); StaticUtils.toHex(attributeValue, builder); } else { final String str = attributeValue.toString(); if (str.length() == 0) { return builder; } char c = str.charAt(0); int startPos = 0; if ((c == ' ') || (c == '#')) { builder.append('\\'); builder.append(c); startPos = 1; } final int length = str.length(); for (int si = startPos; si < length; si++) { c = str.charAt(si); if (c < ' ') { for (final byte b : getBytes(String.valueOf(c))) { builder.append('\\'); builder.append(StaticUtils.byteToLowerHex(b)); } } else { if ((c == ' ' && si == length - 1) || (c == '"' || c == '+' || c == ',' || c == ';' || c == '<' || c == '=' || c == '>' || c == '\\' || c == '\u0000')) { builder.append('\\'); } builder.append(c); } } escapeAttributeValue(attributeValue.toString(), builder); } } return builder; opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DN.java
@@ -86,6 +86,113 @@ }; /** * Returns the LDAP string representation of the provided DN attribute value * in a form suitable for substitution directly into a DN string. This * method may be useful in cases where a DN is to be constructed from a DN * template using {@code String#format(String, Object...)}. The following * example illustrates two approaches to constructing a DN: * * <pre> * // This may contain user input. * String attributeValue = ...; * * // Using the equality filter constructor: * DN dn = DN.valueOf("ou=people,dc=example,dc=com").child("uid", attributeValue); * * // Using a String template: * String dnTemplate = "uid=%s,ou=people,dc=example,dc=com"; * String dnString = String.format(dnTemplate, * DN.escapeAttributeValue(attributeValue)); * DN dn = DN.valueOf(dnString); * </pre> * * <b>Note:</b> attribute values do not and should not be escaped before * passing them to constructors like {@link #child(String, Object)}. * Escaping is only required when creating DN strings. * * @param attributeValue * The attribute 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 escapeAttributeValue(final Object attributeValue) { Validator.ensureNotNull(attributeValue); final String s = String.valueOf(attributeValue); final StringBuilder builder = new StringBuilder(s.length()); AVA.escapeAttributeValue(s, builder); return builder.toString(); } /** * Creates a new DN using the provided DN template and unescaped attribute * values using the default schema. This method first escapes each of the * attribute values and then substitutes them into the template using * {@link String#format(String, Object...)}. Finally, the formatted string * is parsed as an LDAP DN using {@link #valueOf(String)}. * <p> * This method may be useful in cases where the structure of a DN is not * known at compile time, for example, it may be obtained from a * configuration file. Example usage: * * <pre> * String template = "uid=%s,ou=people,dc=example,dc=com"; * DN dn = DN.format(template, "bjensen"); * </pre> * * @param template * The DN template. * @param attributeValues * The attribute values to be substituted into the template. * @return The formatted template parsed as a {@code DN}. * @throws LocalizedIllegalArgumentException * If the formatted template is not a valid LDAP string * representation of a DN. * @see #escapeAttributeValue(Object) */ public static DN format(final String template, final Object... attributeValues) { return format(template, Schema.getDefaultSchema(), attributeValues); } /** * Creates a new DN using the provided DN template and unescaped attribute * values using the provided schema. This method first escapes each of the * attribute values and then substitutes them into the template using * {@link String#format(String, Object...)}. Finally, the formatted string * is parsed as an LDAP DN using {@link #valueOf(String)}. * <p> * This method may be useful in cases where the structure of a DN is not * known at compile time, for example, it may be obtained from a * configuration file. Example usage: * * <pre> * String template = "uid=%s,ou=people,dc=example,dc=com"; * DN dn = DN.format(template, "bjensen"); * </pre> * * @param template * The DN template. * @param schema * The schema to use when parsing the DN. * @param attributeValues * The attribute values to be substituted into the template. * @return The formatted template parsed as a {@code DN}. * @throws LocalizedIllegalArgumentException * If the formatted template is not a valid LDAP string * representation of a DN. * @see #escapeAttributeValue(Object) */ public static DN format(final String template, final Schema schema, final Object... attributeValues) { final String[] attributeValueStrings = new String[attributeValues.length]; for (int i = 0; i < attributeValues.length; i++) { attributeValueStrings[i] = escapeAttributeValue(attributeValues[i]); } final String dnString = String.format(template, (Object[]) attributeValueStrings); return valueOf(dnString, schema); } /** * Returns the Root DN. The Root DN does not contain and RDN components and * is superior to all other DNs. * @@ -107,6 +214,7 @@ * DN. * @throws NullPointerException * If {@code dn} was {@code null}. * @see #format(String, Object...) */ public static DN valueOf(final String dn) { return valueOf(dn, Schema.getDefaultSchema()); @@ -126,6 +234,7 @@ * DN. * @throws NullPointerException * If {@code dn} or {@code schema} was {@code null}. * @see #format(String, Schema, Object...) */ public static DN valueOf(final String dn, final Schema schema) { Validator.ensureNotNull(dn, schema); opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/Filter.java
@@ -66,6 +66,9 @@ * import static org.forgerock.opendj.Filter.*; * * Filter filter = and(equality("cn", "bjensen"), greaterOrEqual("age", 21)); * * // Alternatively use a filter template: * Filter filter = Filter.format("(&(cn=%s)(age>=%s))", "bjensen", 21); * </pre> * * @see <a href="http://tools.ietf.org/html/rfc4511">RFC 4511 - Lightweight @@ -564,20 +567,20 @@ * 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. * @see #format(String, Object...) */ public static String escapeAssertionValue(final Object assertionValue) { Validator.ensureNotNull(assertionValue); @@ -881,6 +884,7 @@ * @throws LocalizedIllegalArgumentException * If {@code string} is not a valid LDAP string representation * of a filter. * @see #format(String, Object...) */ public static Filter valueOf(final String string) { Validator.ensureNotNull(string); @@ -928,7 +932,7 @@ * * <pre> * String template = "(|(cn=%s)(uid=user.%s))"; * Filter filter = Filter.valueOf(template, "alice", 123); * Filter filter = Filter.format(template, "alice", 123); * </pre> * * Any assertion values which are not instances of {@code ByteString} will @@ -942,8 +946,9 @@ * @throws LocalizedIllegalArgumentException * If the formatted template is not a valid LDAP string * representation of a filter. * @see #escapeAssertionValue(Object) */ public static Filter valueOf(final String template, final Object... assertionValues) { public static Filter format(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]); opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
@@ -979,4 +979,35 @@ assertEquals(DN.valueOf(dn).rename(DN.valueOf(fromDN), DN.valueOf(toDN)), DN .valueOf(expectedDN)); } /** * Tests the {@link DN#format(String, Object...)} method. */ @Test public void testFormatNoEscape() { DN actual = DN.format("deviceId=%s,uid=%s,dc=test", 123, "bjensen"); DN expected = DN.valueOf("dc=test").child("uid", "bjensen").child("deviceId", 123); assertEquals(actual, expected); assertEquals(actual.toString(), "deviceId=123,uid=bjensen,dc=test"); } /** * Tests the {@link DN#format(String, Object...)} method. */ @Test public void testFormatEscape() { DN actual = DN.format("uid=%s,dc=test", "#cn=foo+sn=bar"); DN expected = DN.valueOf("dc=test").child("uid", "#cn=foo+sn=bar"); assertEquals(actual, expected); assertEquals(actual.toString(), "uid=\\#cn\\=foo\\+sn\\=bar,dc=test"); } /** * Tests the {@link DN#escapeAttributeValue(Object)} method. */ @Test public void testEscapeAttributeValue() { String actual = DN.escapeAttributeValue("#cn=foo+sn=bar"); assertEquals(actual, "\\#cn\\=foo\\+sn\\=bar"); } } opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/FilterTestCase.java
@@ -307,7 +307,7 @@ @Test(dataProvider = "getAssertionValues") public void testValueOfTemplate(String template, List<?> assertionValues, String expected) throws Exception { Filter filter = Filter.valueOf(template, assertionValues.toArray()); Filter filter = Filter.format(template, assertionValues.toArray()); assertEquals(filter.toString(), expected); }