From 8b41a0a4ec552d72822bf29ccad3a3bad533ba13 Mon Sep 17 00:00:00 2001 From: Ulrik Sandberg Date: Mon, 15 Nov 2010 22:52:43 +0000 Subject: [PATCH] Implemented choice in key case folding between lower, upper or none. LDAP-188 --- changelog.txt | 9 ++- .../ldap/core/DistinguishedName.java | 41 +++++++++-- .../ldap/core/LdapRdnComponent.java | 28 +++++-- .../ldap/core/DistinguishedNameTest.java | 73 +++++++++++++++++-- .../ldap/LdapTemplateModifyITest.java | 4 +- 5 files changed, 129 insertions(+), 26 deletions(-) diff --git a/changelog.txt b/changelog.txt index 9362714b..0c2d4edd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -12,7 +12,7 @@ http://www.ietf.org/rfc/rfc2256.txt http://www.ietf.org/rfc/rfc2696.txt http://www.ietf.org/rfc/rfc2829.txt -Changes in version 1.3.1 (October 2010) +Changes in version 1.3.1 (November 2010) ------------------------------------------- * Added an object-directory mapping framework (ODM). Contributed by Paul Harvey. @@ -47,12 +47,17 @@ Changes in version 1.3.1 (October 2010) * Authentication methods now log problems at level INFO rather than ERROR. (LDAP-170) +* It's now possible to control the case folding of attribute keys in + DistinguishedName by setting the system property + DistinguishedName.KEY_CASE_FOLD_PROPERTY to one of "lower", "upper", or + "none". (LDAP-188) + * DIGEST-MD5 SASL authentication mechanism is now supported, as specified by RFC 2829 (see http://www.ietf.org/rfc/rfc2829.txt, section 4). (LDAP-173) Contributed by Marvin S. Addison. * DefaultDirObjectFactory calls a Java5 version of the IllegalArgumentException - constructor. (LDAP 196). + constructor. (LDAP 196) * DirContextAdapter JavaDoc now states clearly that several methods are not implemented on purpose. (LDAP-208) diff --git a/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java b/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java index 5e959c19..1a6fd4da 100644 --- a/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java +++ b/core/src/main/java/org/springframework/ldap/core/DistinguishedName.java @@ -95,24 +95,49 @@ import org.springframework.util.Assert; */ public class DistinguishedName implements Name { /** - * System property that will be inspected to determine whether toString will - * format the DN with spaces after each comma or use a more compact - * representation, i.e.: + * System property that will be inspected to determine whether + * {@link #toString()} will format the DN with spaces after each comma or + * use a more compact representation, i.e.: * uid=adam.skogman, ou=People, dc=jayway, dc=se rather than - * uid=adam.skogman,ou=People,dc=jayway,dc=se. Default is - * compact representation. + * uid=adam.skogman,ou=People,dc=jayway,dc=se. A value other + * than null or blank will trigger the spaced format. Default is the compact + * representation. + *

+ * Valid values are: + *

* @since 1.3 + * @see #toCompactString() */ public static final String SPACED_DN_FORMAT_PROPERTY = "org.springframework.ldap.core.spacedDnFormat"; /** * System property that will be inspected to determine whether creating a - * DistinguishedName will leave the keys as they were in the original String - * or convert the keys to lowercase. Default is to convert the keys to + * DistinguishedName will convert the keys to lowercase, convert + * the keys to uppercase, or leave the keys as they were in the + * original String, ie none. Default is to convert the keys to * lowercase. + *

+ * Valid values are: + *

* @since 1.3.1 + * @see #KEY_CASE_FOLD_LOWER + * @see #KEY_CASE_FOLD_UPPER + * @see #KEY_CASE_FOLD_NONE */ - public static final String PRESERVE_KEY_CASE_PROPERTY = "org.springframework.ldap.core.preserveKeyCase"; + public static final String KEY_CASE_FOLD_PROPERTY = "org.springframework.ldap.core.keyCaseFold"; + + public static final String KEY_CASE_FOLD_LOWER = "lower"; + + public static final String KEY_CASE_FOLD_UPPER = "upper"; + + public static final String KEY_CASE_FOLD_NONE = "none"; private static final Log log = LogFactory.getLog(DistinguishedName.class); diff --git a/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java b/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java index 63fc60f5..d8b366f7 100644 --- a/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java +++ b/core/src/main/java/org/springframework/ldap/core/LdapRdnComponent.java @@ -21,6 +21,8 @@ import java.net.URISyntaxException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Represents part of an LdapRdn. As specified in RFC2253 an LdapRdn may be @@ -33,6 +35,8 @@ import org.apache.commons.lang.Validate; public class LdapRdnComponent implements Comparable, Serializable { private static final long serialVersionUID = -3296747972616243038L; + private static final Log log = LogFactory.getLog(LdapRdnComponent.class); + public static final boolean DONT_DECODE_VALUE = false; private String key; @@ -52,27 +56,35 @@ public class LdapRdnComponent implements Comparable, Serializable { /** * Constructs an LdapRdnComponent, optionally decoding the value. *

- * If the System property - * org.springframework.ldap.core.preserveKeyCase is set to - * "true", the keys will preserve their original case. Default is to convert - * them to lowercase. + * Depending on the value of the "key case fold" System property, the keys + * will be lowercased, uppercased, or preserve their original case. Default + * is to convert them to lowercase. * * @param key the Attribute name. * @param value the Attribute value. * @param decodeValue if true the value is decoded (typically * used when a DN is parsed from a String), otherwise the value is used as * specified. - * @see DistinguishedName#PRESERVE_KEY_CASE_PROPERTY + * @see DistinguishedName#KEY_CASE_FOLD_PROPERTY */ public LdapRdnComponent(String key, String value, boolean decodeValue) { Validate.notEmpty(key, "Key must not be empty"); Validate.notEmpty(value, "Value must not be empty"); - String preserveKeyCase = System.getProperty(DistinguishedName.PRESERVE_KEY_CASE_PROPERTY); - if (StringUtils.isBlank(preserveKeyCase)) { + String caseFold = System.getProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); + if (StringUtils.isBlank(caseFold) || caseFold.equals(DistinguishedName.KEY_CASE_FOLD_LOWER)) { this.key = StringUtils.lowerCase(key); - } else { + } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_UPPER)) { + this.key = StringUtils.upperCase(key); + } else if (caseFold.equals(DistinguishedName.KEY_CASE_FOLD_NONE)) { this.key = key; + } else { + log + .warn("\"" + caseFold + "\" invalid property value for " + DistinguishedName.KEY_CASE_FOLD_PROPERTY + + "; expected \"" + DistinguishedName.KEY_CASE_FOLD_LOWER + "\", \"" + + DistinguishedName.KEY_CASE_FOLD_UPPER + "\", or \"" + + DistinguishedName.KEY_CASE_FOLD_NONE + "\""); + this.key = StringUtils.lowerCase(key); } if (decodeValue) { this.value = LdapEncoder.nameDecode(value); diff --git a/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java b/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java index 34c5fc06..5714596f 100644 --- a/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java +++ b/core/src/test/java/org/springframework/ldap/core/DistinguishedNameTest.java @@ -582,18 +582,79 @@ public class DistinguishedNameTest extends TestCase { } } - public void testPreserveKeyCasePropertyTrueShouldNotEqualLowerCasedKeys() throws Exception { + public void testKeyCaseFoldNoneShouldEqualOriginalCasedKeys() throws Exception { try { - DistinguishedName name = new DistinguishedName("ou=foo,Ou=bar,oU=baz,OU=bim"); + String dnString = "ou=foo,Ou=bar,oU=baz,OU=bim"; + DistinguishedName name = new DistinguishedName(dnString); + // First check the default assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); - System.setProperty(DistinguishedName.PRESERVE_KEY_CASE_PROPERTY, "true"); - name = new DistinguishedName("ou=foo,Ou=bar,oU=baz,OU=bim"); - assertEquals("ou=foo,Ou=bar,oU=baz,OU=bim", name.toString()); + + System.setProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY, DistinguishedName.KEY_CASE_FOLD_NONE); + name = new DistinguishedName(dnString); + System.out.println(dnString + " folded as \"" + DistinguishedName.KEY_CASE_FOLD_NONE + "\": " + name); + assertEquals(dnString, name.toString()); } finally { // Always restore the system setting - System.setProperty(DistinguishedName.PRESERVE_KEY_CASE_PROPERTY, ""); + System.clearProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); + } + } + + public void testKeyCaseFoldUpperShouldEqualUpperCasedKeys() throws Exception { + try { + String dnString = "ou=foo,Ou=bar,oU=baz,OU=bim"; + DistinguishedName name = new DistinguishedName(dnString); + + // First check the default + assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); + + System.setProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY, DistinguishedName.KEY_CASE_FOLD_UPPER); + name = new DistinguishedName(dnString); + System.out.println(dnString + " folded as \"" + DistinguishedName.KEY_CASE_FOLD_UPPER + "\": " + name); + assertEquals("OU=foo,OU=bar,OU=baz,OU=bim", name.toString()); + } + finally { + // Always restore the system setting + System.clearProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); + } + } + + public void testKeyCaseFoldLowerShouldEqualLowerCasedKeys() throws Exception { + try { + String dnString = "ou=foo,Ou=bar,oU=baz,OU=bim"; + DistinguishedName name = new DistinguishedName(dnString); + + // First check the default + assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); + + System.setProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY, DistinguishedName.KEY_CASE_FOLD_LOWER); + name = new DistinguishedName(dnString); + System.out.println(dnString + " folded as \"" + DistinguishedName.KEY_CASE_FOLD_LOWER + "\": " + name); + assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); + } + finally { + // Always restore the system setting + System.clearProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); + } + } + + public void testKeyCaseFoldNonsenseShoulddefaultToLowerCasedKeysAndLogWarning() throws Exception { + try { + String dnString = "ou=foo,Ou=bar,oU=baz,OU=bim"; + DistinguishedName name = new DistinguishedName(dnString); + + // First check the default + assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); + + System.setProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY, "whatever"); + name = new DistinguishedName(dnString); + System.out.println(dnString + " folded as \"whatever\": " + name); + assertEquals("ou=foo,ou=bar,ou=baz,ou=bim", name.toString()); + } + finally { + // Always restore the system setting + System.clearProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); } } } diff --git a/test/integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java b/test/integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java index 5aa4af33..5dbc6a2c 100644 --- a/test/integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java +++ b/test/integration-tests/src/test/java/org/springframework/ldap/LdapTemplateModifyITest.java @@ -173,7 +173,7 @@ public class LdapTemplateModifyITest extends AbstractLdapTemplateIntegrationTest tested.modifyAttributes(ctx); // without this, the member added above will not be removed and the test fails - System.setProperty(DistinguishedName.PRESERVE_KEY_CASE_PROPERTY, Boolean.TRUE.toString()); + System.setProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY, DistinguishedName.KEY_CASE_FOLD_NONE); ctx = tested.lookupContext(dn); ctx.removeAttributeValue("uniqueMember", new DistinguishedName(upperCasedName).toCompactString()); @@ -189,7 +189,7 @@ public class LdapTemplateModifyITest extends AbstractLdapTemplateIntegrationTest assertEquals("3", "cn=Some Person3,ou=company1,c=Sweden,dc=jayway,dc=se", attributes[3]); } finally { - System.setProperty(DistinguishedName.PRESERVE_KEY_CASE_PROPERTY, ""); + System.clearProperty(DistinguishedName.KEY_CASE_FOLD_PROPERTY); } }