From 040496cbf6d34cc7ce9e1eb6012682dc96c0bfe7 Mon Sep 17 00:00:00 2001 From: Mattias Arthursson Date: Wed, 22 Oct 2008 08:10:13 +0000 Subject: [PATCH] Fix for LDAP-134: DefaultDirObjectFactory now properly handles referrals that are passed to it. Added referralUrl property to DirContextAdapter, and accessor methods to DirContextOperations and DirContextAdapter. Fixed some svn ignores as well. --- core/docs/upgrade/src/upgrade.xml | 4 +- .../ldap/core/DirContextAdapter.java | 114 +++++-- .../ldap/core/DirContextOperations.java | 312 +++++++++--------- .../core/support/DefaultDirObjectFactory.java | 199 +++++++---- .../support/DefaultDirObjectFactoryTest.java | 231 ++++++++----- test-support/pom.xml | 189 ++++++----- .../conf/ldapTemplateTestContext-tls.xml | 18 +- .../conf/ldapTemplateTestContext.xml | 4 +- 8 files changed, 626 insertions(+), 445 deletions(-) diff --git a/core/docs/upgrade/src/upgrade.xml b/core/docs/upgrade/src/upgrade.xml index 1e947dcf..4c131aa2 100644 --- a/core/docs/upgrade/src/upgrade.xml +++ b/core/docs/upgrade/src/upgrade.xml @@ -422,7 +422,7 @@ - DefaultNamingExceptionTranslator + DefaultNamingExceptionTranslator ldap @@ -446,7 +446,7 @@ - NamingExceptionTranslator + NamingExceptionTranslator ldap diff --git a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java index a145ae45..2a112f74 100644 --- a/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java +++ b/core/src/main/java/org/springframework/ldap/core/DirContextAdapter.java @@ -38,21 +38,21 @@ import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.support.LdapUtils; +import org.springframework.util.StringUtils; /** * Adapter that implements the interesting methods of the DirContext interface. * In particular it contains utility methods for getting and setting attributes. * Using the * {@link org.springframework.ldap.core.support.DefaultDirObjectFactory} in your - * ContextSource (which is the default) you will receive - * instances of this class from searches and lookups. This can be particularly - * useful when updating data, since this class implements + * ContextSource (which is the default) you will receive instances + * of this class from searches and lookups. This can be particularly useful when + * updating data, since this class implements * {@link AttributeModificationsAware}, providing a * {@link #getModificationItems()} method. When in update mode, an object of * this class keeps track of the changes made to its attributes, making them @@ -76,6 +76,8 @@ import org.springframework.ldap.support.LdapUtils; */ public class DirContextAdapter implements DirContextOperations { + private static final String EMPTY_STRING = ""; + private static final boolean ORDER_DOESNT_MATTER = false; private static Log log = LogFactory.getLog(DirContextAdapter.class); @@ -90,6 +92,8 @@ public class DirContextAdapter implements DirContextOperations { private Attributes updatedAttrs; + private String referralUrl; + /** * Default constructor. */ @@ -124,6 +128,19 @@ public class DirContextAdapter implements DirContextOperations { * @param base the base name. */ public DirContextAdapter(Attributes attrs, Name dn, Name base) { + this(attrs, dn, base, null); + } + + /** + * Create a new adapter from the supplied attributes, dn, base, and referral + * url. + * @param attrs the attributes. + * @param dn the dn. + * @param base the base. + * @param referralUrl the referral url (if this instance results from a + * referral). + */ + public DirContextAdapter(Attributes attrs, Name dn, Name base, String referralUrl) { if (attrs != null) { this.originalAttrs = attrs; } @@ -142,6 +159,12 @@ public class DirContextAdapter implements DirContextOperations { else { this.base = new DistinguishedName(); } + if (referralUrl != null) { + this.referralUrl = referralUrl; + } + else { + this.referralUrl = EMPTY_STRING; + } } /** @@ -157,8 +180,8 @@ public class DirContextAdapter implements DirContextOperations { } /** - * Sets the update mode. The update mode should be false for - * a new entry and true for an existing entry that is being + * Sets the update mode. The update mode should be false for a + * new entry and true for an existing entry that is being * updated. * * @param mode Update mode. @@ -178,7 +201,8 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.DirContextOperations#getNamesOfModifiedAttributes() + * @seeorg.springframework.ldap.support.DirContextOperations# + * getNamesOfModifiedAttributes() */ public String[] getNamesOfModifiedAttributes() { @@ -220,7 +244,8 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.AttributeModificationsAware#getModificationItems() + * @seeorg.springframework.ldap.support.AttributeModificationsAware# + * getModificationItems() */ public ModificationItem[] getModificationItems() { if (!updateMode) { @@ -350,15 +375,15 @@ public class DirContextAdapter implements DirContextOperations { /** * Compare the existing attribute name with the values on the - * array values. The order of the array must be the same - * order as the existing multivalued attribute. + * array values. The order of the array must be the same order + * as the existing multivalued attribute. *

* Also handles the case where the values have been reset to the original * values after a previous change. For example, changing * [a,b,c] to [a,b] and then back to * [a,b,c] again must result in this method returning - * true so the first change can be overwritten with the - * latest change. + * true so the first change can be overwritten with the latest + * change. * * @param name Name of the original multi-valued attribute. * @param values Array of values to check if they have been changed. @@ -491,14 +516,18 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.DirContextOperations#getStringAttribute(java.lang.String) + * @see + * org.springframework.ldap.support.DirContextOperations#getStringAttribute + * (java.lang.String) */ public String getStringAttribute(String name) { return (String) getObjectAttribute(name); } /* - * @see org.springframework.ldap.support.DirContextOperations#getObjectAttribute(java.lang.String) + * @see + * org.springframework.ldap.support.DirContextOperations#getObjectAttribute + * (java.lang.String) */ public Object getObjectAttribute(String name) { Attribute oneAttr = originalAttrs.get(name); @@ -514,8 +543,9 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.DirContextOperations#setAttributeValue(java.lang.String, - * java.lang.Object) + * @see + * org.springframework.ldap.support.DirContextOperations#setAttributeValue + * (java.lang.String, java.lang.Object) */ public void setAttributeValue(String name, Object value) { // new entry @@ -536,8 +566,9 @@ public class DirContextAdapter implements DirContextOperations { /* * (non-Javadoc) * - * @see org.springframework.ldap.core.DirContextOperations#addAttributeValue(java.lang.String, - * java.lang.Object) + * @see + * org.springframework.ldap.core.DirContextOperations#addAttributeValue( + * java.lang.String, java.lang.Object) */ public void addAttributeValue(String name, Object value) { if (!updateMode && value != null) { @@ -576,8 +607,9 @@ public class DirContextAdapter implements DirContextOperations { /* * (non-Javadoc) * - * @see org.springframework.ldap.core.DirContextOperations#removeAttributeValue(java.lang.String, - * java.lang.Object) + * @see + * org.springframework.ldap.core.DirContextOperations#removeAttributeValue + * (java.lang.String, java.lang.Object) */ public void removeAttributeValue(String name, Object value) { if (!updateMode && value != null) { @@ -605,16 +637,18 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.DirContextOperations#setAttributeValues(java.lang.String, - * java.lang.Object[]) + * @see + * org.springframework.ldap.support.DirContextOperations#setAttributeValues + * (java.lang.String, java.lang.Object[]) */ public void setAttributeValues(String name, Object[] values) { setAttributeValues(name, values, ORDER_DOESNT_MATTER); } /* - * @see org.springframework.ldap.support.DirContextOperations#setAttributeValues(java.lang.String, - * java.lang.Object[], boolean) + * @see + * org.springframework.ldap.support.DirContextOperations#setAttributeValues + * (java.lang.String, java.lang.Object[], boolean) */ public void setAttributeValues(String name, Object[] values, boolean orderMatters) { Attribute a = new BasicAttribute(name, orderMatters); @@ -670,7 +704,9 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.core.DirContextOperations#getStringAttributes(java.lang.String) + * @see + * org.springframework.ldap.core.DirContextOperations#getStringAttributes + * (java.lang.String) */ public String[] getStringAttributes(String name) { String[] attributes; @@ -695,7 +731,8 @@ public class DirContextAdapter implements DirContextOperations { } /* - * @see org.springframework.ldap.support.DirContextOperations#getAttributeSortedStringSet(java.lang.String) + * @seeorg.springframework.ldap.support.DirContextOperations# + * getAttributeSortedStringSet(java.lang.String) */ public SortedSet getAttributeSortedStringSet(String name) { TreeSet attrSet = new TreeSet(); @@ -752,7 +789,7 @@ public class DirContextAdapter implements DirContextOperations { * @see javax.naming.directory.DirContext#getAttributes(String) */ public Attributes getAttributes(String name) throws NamingException { - if (!StringUtils.isEmpty(name)) { + if (StringUtils.hasLength(name)) { throw new NameNotFoundException(); } return (Attributes) originalAttrs.clone(); @@ -769,7 +806,7 @@ public class DirContextAdapter implements DirContextOperations { * @see javax.naming.directory.DirContext#getAttributes(String, String[]) */ public Attributes getAttributes(String name, String[] attrIds) throws NamingException { - if (!StringUtils.isEmpty(name)) { + if (StringUtils.hasLength(name)) { throw new NameNotFoundException(); } @@ -1170,7 +1207,9 @@ public class DirContextAdapter implements DirContextOperations { /* * (non-Javadoc) * - * @see org.springframework.ldap.support.DirContextOperations#setDn(javax.naming.Name) + * @see + * org.springframework.ldap.support.DirContextOperations#setDn(javax.naming + * .Name) */ public final void setDn(Name dn) { if (!updateMode) { @@ -1243,4 +1282,21 @@ public class DirContextAdapter implements DirContextOperations { return buf.toString(); } + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.DirContextOperations#getReferralUrl() + */ + public String getReferralUrl() { + return referralUrl; + } + + /* + * (non-Javadoc) + * + * @see org.springframework.ldap.core.DirContextOperations#isReferral() + */ + public boolean isReferral() { + return StringUtils.hasLength(referralUrl); + } } diff --git a/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java b/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java index 9a2cfceb..03dbfe60 100644 --- a/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java +++ b/core/src/main/java/org/springframework/ldap/core/DirContextOperations.java @@ -27,176 +27,174 @@ import javax.naming.directory.DirContext; * @author Mattias Arthursson * @see DirContextAdapter */ -public interface DirContextOperations extends DirContext, - AttributeModificationsAware { +public interface DirContextOperations extends DirContext, AttributeModificationsAware { - /** - * Gets the update mode. An entry in update mode will keep track of its - * modifications so that they can be retrieved using - * {@link AttributeModificationsAware#getModificationItems()}. The update - * mode should be true for a new entry and true - * for an existing entry that is being updated. - * - * @return update mode. - */ - public boolean isUpdateMode(); + /** + * Gets the update mode. An entry in update mode will keep track of its + * modifications so that they can be retrieved using + * {@link AttributeModificationsAware#getModificationItems()}. The update + * mode should be true for a new entry and true + * for an existing entry that is being updated. + * + * @return update mode. + */ + boolean isUpdateMode(); - /** - * Creates a String array of the names of the attributes which have been - * changed. - * - * If this is a new entry, all set entries will be in the list. If this is - * an updated entry, only changed and removed entries will be in the array. - * - * @return Array of String - */ - public String[] getNamesOfModifiedAttributes(); + /** + * Creates a String array of the names of the attributes which have been + * changed. + * + * If this is a new entry, all set entries will be in the list. If this is + * an updated entry, only changed and removed entries will be in the array. + * + * @return Array of String + */ + String[] getNamesOfModifiedAttributes(); - /** - * Get the value of a String attribute. If more than one attribute value - * exists for the specified attribute, only the first one will be returned. - * - * @param name - * name of the attribute. - * @return the value of the attribute. - * @throws ClassCastException - * if the value of the entry is not a String. - */ - public String getStringAttribute(String name); + /** + * Get the value of a String attribute. If more than one attribute value + * exists for the specified attribute, only the first one will be returned. + * + * @param name name of the attribute. + * @return the value of the attribute. + * @throws ClassCastException if the value of the entry is not a String. + */ + String getStringAttribute(String name); - /** - * Get the value of an Object attribute. If more than one attribute value - * exists for the specified attribute, only the first one will be returned. - * - * @param name - * name of the attribute. - * @return the attribute value as an object if it exists, or - * null otherwise. - */ - public Object getObjectAttribute(String name); + /** + * Get the value of an Object attribute. If more than one attribute value + * exists for the specified attribute, only the first one will be returned. + * + * @param name name of the attribute. + * @return the attribute value as an object if it exists, or + * null otherwise. + */ + Object getObjectAttribute(String name); - /** - * Set the with the name name to the value. - * - * @param name - * name of the attribute. - * @param value - * value to set the attribute to. - */ - public void setAttributeValue(String name, Object value); + /** + * Set the with the name name to the value. + * + * @param name name of the attribute. + * @param value value to set the attribute to. + */ + public void setAttributeValue(String name, Object value); - /** - * Sets a multivalue attribute, disregarding the order of the values. - * - * If value is null or value.length == 0 then the attribute will be removed. - * - * If update mode, changes will be made only if the array has more or less - * objects or if one or more object has changed. Reordering the objects will - * not cause an update. - * - * @param name - * The id of the attribute. - * @param values - * Attribute values. - */ - public void setAttributeValues(String name, Object[] values); + /** + * Sets a multivalue attribute, disregarding the order of the values. + * + * If value is null or value.length == 0 then the attribute will be removed. + * + * If update mode, changes will be made only if the array has more or less + * objects or if one or more object has changed. Reordering the objects will + * not cause an update. + * + * @param name The id of the attribute. + * @param values Attribute values. + */ + void setAttributeValues(String name, Object[] values); - /** - * Sets a multivalue attribute. - * - * If value is null or value.length == 0 then the attribute will be removed. - * - * If update mode, changes will be made if the array has more or less - * objects or if one or more string has changed. - * - * Reordering the objects will only cause an update if orderMatters is set - * to true. - * - * @param name - * The id of the attribute. - * @param values - * Attribute values. - * @param orderMatters - * If true, it will be changed even if data was - * just reordered. - */ - public void setAttributeValues(String name, Object[] values, - boolean orderMatters); + /** + * Sets a multivalue attribute. + * + * If value is null or value.length == 0 then the attribute will be removed. + * + * If update mode, changes will be made if the array has more or less + * objects or if one or more string has changed. + * + * Reordering the objects will only cause an update if orderMatters is set + * to true. + * + * @param name The id of the attribute. + * @param values Attribute values. + * @param orderMatters If true, it will be changed even if data + * was just reordered. + */ + void setAttributeValues(String name, Object[] values, boolean orderMatters); - /** - * Add a value to the Attribute with the specified name. If the Attribute - * doesn't exist it will be created. - * - * @param name - * the name of the Attribute to which the specified value should - * be added. - * @param value - * the Attribute value to add. - */ - public void addAttributeValue(String name, Object value); + /** + * Add a value to the Attribute with the specified name. If the Attribute + * doesn't exist it will be created. + * + * @param name the name of the Attribute to which the specified value should + * be added. + * @param value the Attribute value to add. + */ + void addAttributeValue(String name, Object value); - /** - * Remove a value from the Attribute with the specified name. If the - * Attribute doesn't exist, do nothing. - * - * @param name - * the name of the Attribute from which the specified value - * should be removed. - * @param value - * the value to remove. - */ - public void removeAttributeValue(String name, Object value); + /** + * Remove a value from the Attribute with the specified name. If the + * Attribute doesn't exist, do nothing. + * + * @param name the name of the Attribute from which the specified value + * should be removed. + * @param value the value to remove. + */ + void removeAttributeValue(String name, Object value); - /** - * Update the attributes.This will mean that the getters (getStringAttribute - * methods) will return the updated values, and the modifications will be - * forgotten (i.e. - * {@link AttributeModificationsAware#getModificationItems()} will return an - * empty array. - */ - public void update(); + /** + * Update the attributes.This will mean that the getters ( + * getStringAttribute methods) will return the updated values, + * and the modifications will be forgotten (i.e. + * {@link AttributeModificationsAware#getModificationItems()} will return an + * empty array. + */ + void update(); - /** - * Get all values of a String attribute. - * - * @param name - * name of the attribute. - * - * @return all registered values of the attribute. - */ - public String[] getStringAttributes(String name); + /** + * Get all values of a String attribute. + * + * @param name name of the attribute. + * + * @return all registered values of the attribute. + */ + String[] getStringAttributes(String name); - /** - * Get all String values of the attribute as a SortedSet. - * - * @param name - * name of the attribute. - * @return a SortedSet containing all values of the - * attribute. - */ - public SortedSet getAttributeSortedStringSet(String name); + /** + * Get all String values of the attribute as a SortedSet. + * + * @param name name of the attribute. + * @return a SortedSet containing all values of the attribute. + */ + SortedSet getAttributeSortedStringSet(String name); - /** - * Returns the DN relative to the base path. - * - * @return The distinguished name of the current context. - * - * @see DirContextAdapter#getNameInNamespace() - */ - public Name getDn(); + /** + * Returns the DN relative to the base path. + * + * @return The distinguished name of the current context. + * + * @see DirContextAdapter#getNameInNamespace() + */ + Name getDn(); - /** - * Set the dn of this entry. - * - * @param dn - * the dn. - */ - public void setDn(Name dn); + /** + * Set the dn of this entry. + * + * @param dn the dn. + */ + void setDn(Name dn); - /* - * (non-Javadoc) - * - * @see javax.naming.Context#getNameInNamespace() - */ - public String getNameInNamespace(); + /* + * (non-Javadoc) + * + * @see javax.naming.Context#getNameInNamespace() + */ + String getNameInNamespace(); + + /** + * If this instance results from a referral, this method returns the url of + * the referred server. + * + * @return The url of the referred server, e.g. + * ldap://localhost:389, or the empty string if this is not a + * referral. + */ + String getReferralUrl(); + + /** + * Checks whether this instance results from a referral. + * + * @return true if this instance results from a referral, + * false otherwise. + */ + boolean isReferral(); } \ No newline at end of file diff --git a/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java b/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java index 61df8948..646afe0a 100644 --- a/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java +++ b/core/src/main/java/org/springframework/ldap/core/support/DefaultDirObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2007 the original author or authors. + * Copyright 2005-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.ldap.core.support; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Hashtable; import javax.naming.CompositeName; @@ -24,8 +26,11 @@ import javax.naming.Name; import javax.naming.directory.Attributes; import javax.naming.spi.DirObjectFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; +import org.springframework.util.StringUtils; /** * Default implementation of the DirObjectFactory interface. Creates a @@ -34,74 +39,140 @@ import org.springframework.ldap.core.DistinguishedName; * @author Mattias Arthursson */ public class DefaultDirObjectFactory implements DirObjectFactory { - /** - * Key to use in the ContextSource implementation to store the value of the - * base path suffix, if any, in the Ldap Environment. - * - * @deprecated Use {@link BaseLdapPathAware} and {@link BaseLdapPathBeanPostProcessor} instead. - */ - public static final String JNDI_ENV_BASE_PATH_KEY = "org.springframework.ldap.base.path"; + private static final Log log = LogFactory.getLog(AbstractContextSource.class); - /* - * (non-Javadoc) - * - * @see javax.naming.spi.DirObjectFactory#getObjectInstance(java.lang.Object, - * javax.naming.Name, javax.naming.Context, java.util.Hashtable, - * javax.naming.directory.Attributes) - */ - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment, Attributes attrs) throws Exception { + /** + * Key to use in the ContextSource implementation to store the value of the + * base path suffix, if any, in the Ldap Environment. + * + * @deprecated Use {@link BaseLdapPathAware} and + * {@link BaseLdapPathBeanPostProcessor} instead. + */ + public static final String JNDI_ENV_BASE_PATH_KEY = "org.springframework.ldap.base.path"; - try { - String nameInNamespace = null; - if (nameCtx != null) { - nameInNamespace = nameCtx.getNameInNamespace(); - } else { - nameInNamespace = ""; - } + private static final String LDAP_PROTOCOL_PREFIX = "ldap://"; - if(name instanceof CompositeName){ - // Which it most certainly will be, and therein lies the problem. - // CompositeName.toString() completely screws up the formatting in some cases, - // particularly when backslashes are involved. - CompositeName compositeName = (CompositeName) name; - name = new DistinguishedName(compositeName.get(0)); - } - - DirContextAdapter dirContextAdapter = new DirContextAdapter(attrs, - name, new DistinguishedName(nameInNamespace)); - dirContextAdapter.setUpdateMode(true); + private static final String LDAPS_PROTOCOL_PREFIX = "ldaps://"; - return dirContextAdapter; - } finally { - // It seems that the object supplied to the obj parameter is a - // DirContext instance with reference to the same Ldap connection as - // the original context. Since it is not the same instance (that's - // the nameCtx parameter) this one really needs to be closed in - // order to correctly clean up and return the connection to the pool - // when we're finished with the surrounding operation. - if (obj instanceof Context) { + /* + * (non-Javadoc) + * + * @see + * javax.naming.spi.DirObjectFactory#getObjectInstance(java.lang.Object, + * javax.naming.Name, javax.naming.Context, java.util.Hashtable, + * javax.naming.directory.Attributes) + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment, Attributes attrs) + throws Exception { - Context ctx = (Context) obj; - try { - ctx.close(); - } catch (Exception e) { - // Never mind this - } + try { + String nameInNamespace = null; + if (nameCtx != null) { + nameInNamespace = nameCtx.getNameInNamespace(); + } + else { + nameInNamespace = ""; + } - } - } - } - - /* - * (non-Javadoc) - * - * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, - * javax.naming.Name, javax.naming.Context, java.util.Hashtable) - */ - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) throws Exception { - return null; - } + return constructAdapterFromName(attrs, name, nameInNamespace); + } + finally { + // It seems that the object supplied to the obj parameter is a + // DirContext instance with reference to the same Ldap connection as + // the original context. Since it is not the same instance (that's + // the nameCtx parameter) this one really needs to be closed in + // order to correctly clean up and return the connection to the pool + // when we're finished with the surrounding operation. + if (obj instanceof Context) { + + Context ctx = (Context) obj; + try { + ctx.close(); + } + catch (Exception e) { + // Never mind this + } + + } + } + } + + /** + * Construct a DirContextAdapter given the supplied paramters. The + * name is normally a JNDI CompositeName, which + * needs to be handled with particuclar care. Specifically the escaping of a + * CompositeName destroys proper escaping of Distinguished + * Names. Also, the name might contain referral information, in which case + * we need to separate the server information from the actual Distinguished + * Name so that we can create a representing DirContextAdapter. + * + * @param attrs the attributes + * @param name the Name, typically a CompositeName, possibly + * including referral information. + * @param nameInNamespace the Name in namespace. + * @return a {@link DirContextAdapter} representing the specified + * information. + */ + DirContextAdapter constructAdapterFromName(Attributes attrs, Name name, String nameInNamespace) { + String nameString = ""; + String referralUrl = ""; + + if (name instanceof CompositeName) { + // Which it most certainly will be, and therein lies the + // problem. CompositeName.toString() completely screws up the + // formatting + // in some cases, particularly when backslashes are involved. + CompositeName compositeName = (CompositeName) name; + nameString = compositeName.get(0); + } + else { + log.warn("Expecting a CompositeName as input to getObjectInstance but received a '" + + name.getClass().toString() + "' - using toString and proceeding with undefined results"); + nameString = name.toString(); + } + + if (nameString.startsWith(LDAP_PROTOCOL_PREFIX) || nameString.startsWith(LDAPS_PROTOCOL_PREFIX)) { + if (log.isDebugEnabled()) { + log.debug("Received name '" + nameString + "' contains protocol delimiter; indicating a referral." + + "Stripping protocol and address info to enable construction of a proper DistinguishedName"); + } + try { + URI url = new URI(nameString); + String pathString = url.getPath(); + referralUrl = nameString.substring(0, nameString.length() - pathString.length()); + + if (StringUtils.hasLength(pathString) && pathString.startsWith("/")) { + // We don't want any slash in the beginning of the + // Distinguished Name. + pathString = pathString.substring(1); + } + + nameString = pathString; + } + catch (URISyntaxException e) { + throw new IllegalArgumentException("Supplied name starts with protocol prefix indicating a referral," + + " but is not possible to parse to an URI", e); + } + if (log.isDebugEnabled()) { + log.debug("Resulting name after removal of referral information: '" + nameString + "'"); + } + } + + DirContextAdapter dirContextAdapter = new DirContextAdapter(attrs, new DistinguishedName(nameString), + new DistinguishedName(nameInNamespace), referralUrl); + dirContextAdapter.setUpdateMode(true); + + return dirContextAdapter; + } + + /* + * (non-Javadoc) + * + * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, + * javax.naming.Name, javax.naming.Context, java.util.Hashtable) + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { + return null; + } } diff --git a/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java b/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java index b501f103..e1947d24 100644 --- a/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java +++ b/core/src/test/java/org/springframework/ldap/core/support/DefaultDirObjectFactoryTest.java @@ -17,7 +17,9 @@ package org.springframework.ldap.core.support; import java.util.Hashtable; +import javax.naming.CompositeName; import javax.naming.Context; +import javax.naming.InvalidNameException; import javax.naming.Name; import javax.naming.directory.BasicAttributes; @@ -29,128 +31,183 @@ import org.springframework.ldap.core.DistinguishedName; public class DefaultDirObjectFactoryTest extends TestCase { - private MockControl contextControl; + private MockControl contextControl; - private Context contextMock; + private Context contextMock; - private static final Name DN = new DistinguishedName( - "ou=some unit, dc=jayway, dc=se"); + private static final Name DN = new DistinguishedName("ou=some unit, dc=jayway, dc=se"); - private DefaultDirObjectFactory tested; + private static final String DN_STRING = "ou=some unit, dc=jayway, dc=se"; - private MockControl contextControl2; + private DefaultDirObjectFactory tested; - private Context contextMock2; + private MockControl contextControl2; - protected void setUp() throws Exception { - super.setUp(); + private Context contextMock2; - contextControl = MockControl.createControl(Context.class); - contextMock = (Context) contextControl.getMock(); + protected void setUp() throws Exception { + super.setUp(); - contextControl2 = MockControl.createControl(Context.class); - contextMock2 = (Context) contextControl2.getMock(); + contextControl = MockControl.createControl(Context.class); + contextMock = (Context) contextControl.getMock(); - tested = new DefaultDirObjectFactory(); - } + contextControl2 = MockControl.createControl(Context.class); + contextMock2 = (Context) contextControl2.getMock(); - protected void tearDown() throws Exception { - super.tearDown(); + tested = new DefaultDirObjectFactory(); + } - contextControl = null; - contextMock = null; + protected void tearDown() throws Exception { + super.tearDown(); - contextControl2 = null; - contextMock2 = null; + contextControl = null; + contextMock = null; - tested = null; - } + contextControl2 = null; + contextMock2 = null; - protected void replay() { - contextControl.replay(); - contextControl2.replay(); - } + tested = null; + } - protected void verify() { - contextControl.verify(); - contextControl2.verify(); - } + protected void replay() { + contextControl.replay(); + contextControl2.replay(); + } - public void testGetObjectInstance() throws Exception { - BasicAttributes expectedAttributes = new BasicAttributes(); - expectedAttributes.put("someAttribute", "someValue"); + protected void verify() { + contextControl.verify(); + contextControl2.verify(); + } - contextMock.close(); + public void testGetObjectInstance() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); - replay(); + contextMock.close(); - DirContextAdapter adapter = (DirContextAdapter) tested - .getObjectInstance(contextMock, DN, null, new Hashtable(), - expectedAttributes); + replay(); - verify(); + DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(contextMock, DN, null, + new Hashtable(), expectedAttributes); - assertEquals(DN, adapter.getDn()); - assertEquals(expectedAttributes, adapter.getAttributes()); - } + verify(); - public void testGetObjectInstance_nullObject() throws Exception { - BasicAttributes expectedAttributes = new BasicAttributes(); - expectedAttributes.put("someAttribute", "someValue"); + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } - replay(); + public void testGetObjectInstance_CompositeName() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); - DirContextAdapter adapter = (DirContextAdapter) tested - .getObjectInstance(null, DN, null, new Hashtable(), - expectedAttributes); + contextMock.close(); - verify(); + replay(); - assertEquals(DN, adapter.getDn()); - assertEquals(expectedAttributes, adapter.getAttributes()); - } + CompositeName name = new CompositeName(); + name.add(DN_STRING); + + DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(contextMock, name, null, + new Hashtable(), expectedAttributes); - public void testGetObjectInstance_ObjectNotContext() throws Exception { - BasicAttributes expectedAttributes = new BasicAttributes(); - expectedAttributes.put("someAttribute", "someValue"); + verify(); - replay(); + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } - DirContextAdapter adapter = (DirContextAdapter) tested - .getObjectInstance(new Object(), DN, null, new Hashtable(), - expectedAttributes); + public void testGetObjectInstance_nullObject() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); - verify(); + replay(); - assertEquals(DN, adapter.getDn()); - assertEquals(expectedAttributes, adapter.getAttributes()); - } + DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(null, DN, null, new Hashtable(), + expectedAttributes); - /** - * Make sure that the base suffix is stripped off from the DN. - * - * @throws Exception - */ - public void testGetObjectInstance_BaseSet() throws Exception { - BasicAttributes expectedAttributes = new BasicAttributes(); - expectedAttributes.put("someAttribute", "someValue"); + verify(); - contextControl2.expectAndReturn(contextMock2.getNameInNamespace(), - "dc=jayway, dc=se"); - contextMock.close(); + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } - replay(); + public void testGetObjectInstance_ObjectNotContext() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); - DirContextAdapter adapter = (DirContextAdapter) tested - .getObjectInstance(contextMock, new DistinguishedName( - "ou=some unit"), contextMock2, new Hashtable(), - expectedAttributes); + replay(); - verify(); + DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(new Object(), DN, null, + new Hashtable(), expectedAttributes); - assertEquals("ou=some unit", adapter.getDn().toString()); - assertEquals("ou=some unit, dc=jayway, dc=se", adapter - .getNameInNamespace()); - assertEquals(expectedAttributes, adapter.getAttributes()); - } + verify(); + + assertEquals(DN, adapter.getDn()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } + + /** + * Make sure that the base suffix is stripped off from the DN. + * + * @throws Exception + */ + public void testGetObjectInstance_BaseSet() throws Exception { + BasicAttributes expectedAttributes = new BasicAttributes(); + expectedAttributes.put("someAttribute", "someValue"); + + contextControl2.expectAndReturn(contextMock2.getNameInNamespace(), "dc=jayway, dc=se"); + contextMock.close(); + + replay(); + + DirContextAdapter adapter = (DirContextAdapter) tested.getObjectInstance(contextMock, new DistinguishedName( + "ou=some unit"), contextMock2, new Hashtable(), expectedAttributes); + + verify(); + + assertEquals("ou=some unit", adapter.getDn().toString()); + assertEquals("ou=some unit, dc=jayway, dc=se", adapter.getNameInNamespace()); + assertEquals(expectedAttributes, adapter.getAttributes()); + } + + public void testConstructAdapterFromName() throws InvalidNameException { + CompositeName name = new CompositeName(); + name.add("ldap://localhost:389/ou=People,o=JNDITutorial"); + DefaultDirObjectFactory tested = new DefaultDirObjectFactory(); + DirContextAdapter result = tested.constructAdapterFromName(new BasicAttributes(), name, ""); + + assertEquals("ou=People, o=JNDITutorial", result.getDn().toString()); + assertEquals("ldap://localhost:389", result.getReferralUrl().toString()); + } + + public void testConstructAdapterFromName_Ldaps() throws InvalidNameException { + CompositeName name = new CompositeName(); + name.add("ldaps://localhost:389/ou=People,o=JNDITutorial"); + DefaultDirObjectFactory tested = new DefaultDirObjectFactory(); + DirContextAdapter result = tested.constructAdapterFromName(new BasicAttributes(), name, ""); + + assertEquals("ou=People, o=JNDITutorial", result.getDn().toString()); + assertEquals("ldaps://localhost:389", result.getReferralUrl().toString()); + } + + public void testConstructAdapterFromName_EmptyName() throws InvalidNameException { + CompositeName name = new CompositeName(); + name.add("ldap://localhost:389"); + DefaultDirObjectFactory tested = new DefaultDirObjectFactory(); + DirContextAdapter result = tested.constructAdapterFromName(new BasicAttributes(), name, ""); + + assertEquals("", result.getDn().toString()); + assertEquals("ldap://localhost:389", result.getReferralUrl().toString()); + } + + + public void testConstructAdapterFromName_OnlySlash() throws InvalidNameException { + CompositeName name = new CompositeName(); + name.add("ldap://localhost:389/"); + DefaultDirObjectFactory tested = new DefaultDirObjectFactory(); + DirContextAdapter result = tested.constructAdapterFromName(new BasicAttributes(), name, ""); + + assertEquals("", result.getDn().toString()); + assertEquals("ldap://localhost:389", result.getReferralUrl().toString()); + } } diff --git a/test-support/pom.xml b/test-support/pom.xml index a9b96cc1..ce935c41 100644 --- a/test-support/pom.xml +++ b/test-support/pom.xml @@ -1,91 +1,104 @@ - - - - org.springframework.ldap - spring-ldap-parent - 1.3-SNAPSHOT - - 4.0.0 - spring-ldap-test - jar - Spring LDAP Automated Test Support - - - - - maven-compiler-plugin - - 1.5 - 1.5 - - - - - - - org.springframework.ldap - spring-ldap-core - + + + org.springframework.ldap + spring-ldap-parent + 1.3-SNAPSHOT + + 4.0.0 + spring-ldap-test + jar + Spring LDAP Automated Test Support + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + org.springframework.ldap + spring-ldap-core + - - com.google.code.typica - typica - 1.3 - - - commons-io - commons-io - 1.4 - - - org.apache.commons - commons-io - 1.3.2 - - - org.apache.directory.server - apacheds-server-main - 1.0.2 - - - org.slf4j - nlog4j - - - - - org.slf4j - slf4j-log4j12 - 1.0.1 - + + com.google.code.typica + typica + 1.3 + + + commons-io + commons-io + 1.4 + + + javax.xml + jsr173 + 1.0 + + + javax.activation + activation + 1.1 + + + javax.xml.bind + jaxb-api + 2.1 + + + javax.xml + jaxb-impl + 2.1 + + + org.apache.directory.server + + apacheds-server-main + 1.0.2 + + + org.slf4j + nlog4j + + + + + org.slf4j + slf4j-log4j12 + 1.0.1 + - - org.springframework - spring-core - - - org.springframework - spring-beans - - - org.springframework - spring-context - - - org.springframework - spring-test - - - log4j - log4j - - - junit - junit - provided - - - + + org.springframework + spring-core + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.springframework + spring-test + + + log4j + log4j + + + junit + junit + provided + + + \ No newline at end of file diff --git a/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext-tls.xml b/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext-tls.xml index d1b6914b..c6975291 100644 --- a/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext-tls.xml +++ b/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext-tls.xml @@ -11,8 +11,8 @@ - - + + @@ -21,20 +21,6 @@ - - diff --git a/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext.xml b/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext.xml index 37630723..cc69d3ae 100644 --- a/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext.xml +++ b/test/integration-tests-openldap/src/test/resources/conf/ldapTemplateTestContext.xml @@ -10,8 +10,8 @@ - - + +