LDAP-258: Updated documentation to reflect DistinguishedName deprecation.

This commit is contained in:
Mattias Hellborg Arthursson
2013-08-30 12:46:10 +02:00
parent 31ea71e61e
commit d7a9609d16
6 changed files with 169 additions and 142 deletions

View File

@@ -141,94 +141,121 @@ filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", cn));</programlisting>
</example>
<para>
<note>
In addition to simplifying building of complex search filters,
the <literal>Filter</literal> classes also provide proper escaping
of any unsafe characters. This prevents &quot;ldap injection&quot;,
where a user might use such characters to inject unwanted operations
into your LDAP operations.
</note>
<para>
<note>
In addition to simplifying building of complex search filters,
the <literal>Filter</literal> classes also provide proper escaping
of any unsafe characters. This prevents &quot;ldap injection&quot;,
where a user might use such characters to inject unwanted operations
into your LDAP operations.
</note>
</para>
</sect1>
<sect1>
<title>Building Dynamic Distinguished Names</title>
<title>Dynamically Building Distinguished Names</title>
<para>
The standard Java implementation of Distinguished Name, <ulink
url="http://docs.oracle.com/javase/6/docs/api/javax/naming/ldap/LdapName.html">LdapName</ulink>,
performs very well when it comes to parsing of Distinguished Names. However, in practical use
this implementation has a number of shortcomings:
<itemizedlist>
<listitem>
<para>
The <literal>LdapName</literal> implementation is mutable, which is badly suited for an object
representing identity.
</para>
</listitem>
<listitem>
<para>
Despite its mutable nature, the API for dynamically building or modifying Distinguished Names using
<literal>LdapName</literal> is cumbersome. Extracting values of indexed or (particularly)
named components is also a little bit awkward.
</para>
</listitem>
<listitem>
<para>
Many of the operations on <literal>LdapName</literal> throw checked Exceptions, requiring unnecessary
try-catch statements for situations where the error is typically fatal and cannot be repaired in
a meaningful manner.
</para>
</listitem>
</itemizedlist>
<para>The standard <ulink
url="http://download.oracle.com/javase/1.5.0/docs/api/javax/naming/Name.html">Name</ulink>
interface represents a generic name, which is basically an ordered
sequence of components. The <literal>Name</literal> interface also
provides operations on that sequence; e.g., <literal>add</literal> or
<literal>remove</literal>. LdapTemplate provides an implementation of the
<literal>Name</literal> interface: <literal>DistinguishedName</literal>.
Using this class will greatly simplify building distinguished names,
especially considering the sometimes complex rules regarding escapings and
encodings. As with the <literal>Filter</literal> classes this helps preventing
potentially malicious data being injected into your LDAP operations.
</para>
<para>
The following example illustrates how
<literal>DistinguishedName</literal> can be used to dynamically construct
a distinguished name:</para>
<example>
<title>Building a distinguished name dynamically</title>
<programlisting>package com.example.dao;
import org.springframework.ldap.core.support.DistinguishedName;
To simplify working with Distinguished Names, Spring LDAP provides an <literal>LdapNameBuilder</literal>, as
well as a number of utility methods in <literal>LdapUtils</literal> that helps working with
<literal>LdapName</literal>.
</para>
<para>
Below are a couple of examples of how these utilities can simplify handling of distinguished names.
<example>
<title>Dynamically building an LdapName using LdapNameBuilder</title>
<programlisting>package com.example.dao;
import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonDaoImpl implements PersonDao {
public static final String BASE_DN = "dc=example,dc=com";
...
protected Name buildDn(Person p) {
<emphasis role="bold"> DistinguishedName dn = new DistinguishedName(BASE_DN);
dn.add("c", p.getCountry());
dn.add("ou", p.getCompany());
dn.add("cn", p.getFullname());
</emphasis> return dn;
}
}</programlisting>
</example>
public static final String BASE_DN = "dc=example,dc=com";
...
protected Name buildDn(Person p) {
<emphasis role="bold"> return LdapNameBuilder.newInstance(BASE_DN)
.add("c", p.getCountry())
.add("ou", p.getCompany())
.add("cn", p.getFullname())
.build();
</emphasis>
}
</programlisting>
</example>
Assuming that a Person has the following attributes:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>country</literal></entry>
<entry>Sweden</entry>
</row>
<row>
<entry><literal>company</literal></entry>
<entry>Some Company</entry>
</row>
<row>
<entry><literal>fullname</literal></entry>
<entry>Some Person</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Assuming that a Person has the following attributes:</para>
<para>The code above would then result in the following distinguished
name:</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>country</literal></entry>
<para><programlisting>cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com</programlisting></para>
<para>
<example>
<title>Extracting values from a distinguished name using LdapUtils</title>
<programlisting>package com.example.dao;
import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonDaoImpl implements PersonDao {
...
protected Person buildPerson(Name dn, Attributes attrs) {
Person person = new Person();
person.setCountry(<emphasis>LdapUtils.getStringValue(dn, "c")</emphasis>);
person.setCompany(<emphasis>LdapUtils.getStringValue(dn, "ou")</emphasis>);
person.setFullname(<emphasis>LdapUtils.getStringValue(dn, "cn")</emphasis>);
// Populate rest of person object using attributes.
<entry>Sweden</entry>
</row>
<row>
<entry><literal>company</literal></entry>
<entry>Some Company</entry>
</row>
<row>
<entry><literal>fullname</literal></entry>
<entry>Some Person</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>The code above would then result in the following distinguished
name:</para>
<para><programlisting>cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com</programlisting></para>
<para>In Java 5, there is an implementation of the Name interface: <ulink
url="http://download.oracle.com/javase/1.5.0/docs/api/javax/naming/ldap/LdapName.html">LdapName</ulink>.
If you are in the Java 5 world, you might as well use
<literal>LdapName</literal>. However, you may still use
<literal>DistinguishedName</literal> if you so wish.</para>
return person;
}
</programlisting>
</example>
Since Java version &lt;=1.4 didn't provide any public Distinguished Name implementation at all, Spring LDAP
1.3.2 and lower provided its own implementation, <literal>DistinguishedName</literal>. This implementation
suffered from a couple of shortcomings of its own, and have been deprecated in version 2.0.
Users are now recommended to use <literal>LdapName</literal> along with the utilities described above instead.
</para>
</sect1>
<sect1 id="basic-binding-unbinding">

View File

@@ -282,23 +282,23 @@
<para>For that reason, Spring LDAP has a mechanism by which any Spring controlled bean may be supplied
the base path on startup. For beans to be notified of the base path, two things need to be in place:
First of all, the bean that wants the base path reference needs to implement the
<literal>BaseLdapPathAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
<literal>BaseLdapNameAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
needs to be defined in the application context</para>
<example>
<title>Implementing <literal>BaseLdapPathAware</literal></title>
<title>Implementing <literal>BaseLdapNameAware</literal></title>
<programlisting>package com.example.service;
public class PersonService implements PersonService, <emphasis role="bold">BaseLdapPathAware</emphasis> {
public class PersonService implements PersonService, <emphasis role="bold">BaseLdapNameAware</emphasis> {
...
<emphasis role="bold">private DistinguishedName basePath;
<emphasis role="bold">private LdapName basePath;
public void setBaseLdapPath(DistinguishedName basePath) {
public void setBaseLdapPath(LdapName basePath) {
this.basePath = basePath;
}</emphasis>
...
private DistinguishedName getFullPersonDn(Person person) {
return new DistinguishedName(<emphasis role="bold">basePath</emphasis>).append(person.getDn());
private LdapName getFullPersonDn(Person person) {
return LdapNameBuilder.newInstance(<emphasis role="bold">basePath</emphasis>)
.append(person.getDn())
.build();
}
...
}</programlisting>
@@ -321,6 +321,7 @@ public class PersonService implements PersonService, <emphasis role="bold">BaseL
<para>The default behaviour of the <literal>BaseLdapPathBeanPostProcessor</literal> is to use the base path of the single
defined <literal>BaseLdapPathSource</literal> (<literal>AbstractContextSource</literal> )in the <literal>ApplicationContext</literal>.
If more than one <literal>BaseLdapPathSource</literal> is defined, you will need to specify which one to use with the
<literal>baseLdapPathSourceName</literal> property.</para>
<literal>baseLdapPathSourceName</literal> property.
</para>
</sect1>
</chapter>

View File

@@ -59,31 +59,31 @@ public class PersonDaoImpl implements PersonDao {
<para>The above code shows that it is possible to retrieve the attributes
directly by name, without having to go through the
<literal>Attributes</literal> and <literal>BasicAttribute</literal>
classes. This is particularly useful when working with multi-value attributes. Extracting values from
multi-value attributes normally requires looping through a <literal>NamingEnumeration</literal> of
attribute values returned from the <literal>Attributes</literal> implementation. The
<literal>DirContextAdapter</literal> can do this for you, using the <literal>getStringAttributes()</literal>
classes. This is particularly useful when working with multi-value attributes. Extracting values from
multi-value attributes normally requires looping through a <literal>NamingEnumeration</literal> of
attribute values returned from the <literal>Attributes</literal> implementation. The
<literal>DirContextAdapter</literal> can do this for you, using the <literal>getStringAttributes()</literal>
or <literal>getObjectAttributes()</literal> methods:</para>
<example>
<title>Getting multi-value attribute values using <literal>getStringAttributes()</literal></title>
<programlisting>private static class PersonContextMapper implements ContextMapper {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter)ctx;
Person p = new Person();
p.setFullName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setDescription(context.getStringAttribute("description"));
// The roleNames property of Person is an String array
<emphasis role="bold">p.setRoleNames(context.getStringAttributes("roleNames"));</emphasis>
return p;
}
}
</programlisting>
</example>
<sect2>
<title>The AbstractContextMapper</title>
<example>
<title>Getting multi-value attribute values using <literal>getStringAttributes()</literal></title>
<programlisting>private static class PersonContextMapper implements ContextMapper {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter)ctx;
Person p = new Person();
p.setFullName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setDescription(context.getStringAttribute("description"));
// The roleNames property of Person is an String array
<emphasis role="bold">p.setRoleNames(context.getStringAttributes("roleNames"));</emphasis>
return p;
}
}
</programlisting>
</example>
<sect2>
<title>The AbstractContextMapper</title>
<para>Spring LDAP provides an abstract base implementation of <literal>ContextMapper</literal>,
<literal>AbstractContextMapper</literal>. This automatically takes care of the casting of the supplied
<literal>Object</literal> parameter to <literal>DirContexOperations</literal>.
@@ -104,13 +104,13 @@ public class PersonDaoImpl implements PersonDao {
}
</programlisting>
</example>
</sect2>
</sect2>
</sect1>
<sect1>
<title>Binding and Modifying Using DirContextAdapter</title>
<para>While very useful when extracting attribute values, <literal>DirContextAdapter</literal> is even more
<para>While very useful when extracting attribute values, <literal>DirContextAdapter</literal> is even more
powerful for hiding attribute details when binding and modifying data.</para>
<sect2>
@@ -141,14 +141,14 @@ public class PersonDaoImpl implements PersonDao {
}</programlisting>
</example>
<para>Note that we use the <literal>DirContextAdapter</literal> instance
as the second parameter to bind, which should be a <literal>Context</literal>.
<para>Note that we use the <literal>DirContextAdapter</literal> instance
as the second parameter to bind, which should be a <literal>Context</literal>.
The third parameter is <literal>null</literal>, since we're not using any
<literal>Attributes</literal>.</para>
<para>Also note the use of the <literal>setAttributeValues()</literal> method when setting the
<literal>objectclass</literal> attribute values. The <literal>objectclass</literal> attribute is
multi-value, and similar to the troubles of extracting muti-value attribute data, building multi-value
attributes is tedious and verbose work. Using the <literal>setAttributeValues()</literal> mehtod you can
<literal>Attributes</literal>.</para>
<para>Also note the use of the <literal>setAttributeValues()</literal> method when setting the
<literal>objectclass</literal> attribute values. The <literal>objectclass</literal> attribute is
multi-value, and similar to the troubles of extracting muti-value attribute data, building multi-value
attributes is tedious and verbose work. Using the <literal>setAttributeValues()</literal> mehtod you can
have <literal>DirContextAdapter</literal> handle that work for you.</para>
</sect2>
@@ -157,11 +157,11 @@ public class PersonDaoImpl implements PersonDao {
<para>The code for a <literal>rebind</literal> would be pretty much
identical to <xref linkend="example-binding-contextmapper" />, except
that the method called would be <literal>rebind</literal>. As we saw in
<xref linkend="modify-modifyAttributes"/> a more correct approach would be to
build a <literal>ModificationItem</literal> array containing the actual
modifications you want to do. This would require you to determine the actual
modifications compared to the data present in the LDAP tree. Again, this
that the method called would be <literal>rebind</literal>. As we saw in
<xref linkend="modify-modifyAttributes"/> a more correct approach would be to
build a <literal>ModificationItem</literal> array containing the actual
modifications you want to do. This would require you to determine the actual
modifications compared to the data present in the LDAP tree. Again, this
is something that <literal>DirContextAdapter</literal> can help you with; the
<literal>DirContextAdapter</literal> has the ability to keep track of
its modified attributes. The following example takes advantage of this
@@ -186,11 +186,11 @@ public class PersonDaoImpl implements PersonDao {
<emphasis role="bold">ldapTemplate.modifyAttributes(context);</emphasis>
}
}</programlisting>
</example>
<para>When no mapper is passed to a <literal>ldapTemplate.lookup()</literal> operation,
the result will be a <literal>DirContextAdapter</literal> instance.
While the <literal>lookup</literal> method returns an <literal>Object</literal>, the convenience
method <literal>lookupContext</literal> method automatically casts the return value to
</example>
<para>When no mapper is passed to a <literal>ldapTemplate.lookup()</literal> operation,
the result will be a <literal>DirContextAdapter</literal> instance.
While the <literal>lookup</literal> method returns an <literal>Object</literal>, the convenience
method <literal>lookupContext</literal> method automatically casts the return value to
a <literal>DirContextOperations</literal> (the interface that <literal>DirContextAdapter</literal> implements.</para>
<para>The observant reader will see that we have duplicated code in the
<literal>create</literal> and <literal>update</literal> methods. This
@@ -241,18 +241,17 @@ public class PersonDaoImpl implements PersonDao {
<title>A complete PersonDao class</title>
<programlisting>package com.example.dao;
import java.util.List;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.support.DistinguishedName;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.WhitespaceWildcardsFilter;
@@ -289,12 +288,12 @@ public class PersonDaoImpl implements PersonDao {
public List findByName(String name) {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new WhitespaceWildcardsFilter("cn",name));
return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(), getContextMapper());
return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper());
}
public List findAll() {
EqualsFilter filter = new EqualsFilter("objectclass", "person");
return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(), getContextMapper());
return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper());
}
protected ContextMapper getContextMapper() {
@@ -306,11 +305,11 @@ public class PersonDaoImpl implements PersonDao {
}
protected Name buildDn(String fullname, String company, String country) {
DistinguishedName dn = new DistinguishedName();
dn.add("c", country);
dn.add("ou", company);
dn.add("cn", fullname);
return dn;
return LdapNameBuilder.newInstance()
.add("c", country)
.add("ou", company)
.add("cn", fullname)
.build();
}
protected void mapToContext(Person person, DirContextOperations context) {

View File

@@ -47,7 +47,7 @@
<para><token>LdapAttribute</token> objects represent options as a
<token>Set&lt;String&gt;</token>. The DN support added to the
<token>LdapAttributes</token> object employs the
<token>org.springframework.ldap.core.DistinguishedName</token> class. </para>
<token>javax.naming.ldap.LdapName</token> class. </para>
</section>
<section id="ldif-parsing-parser">

View File

@@ -273,7 +273,7 @@ public class App {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OdmManager manager = (OdmManager) context.getBean("odmManager");
List&lt;SimplePerson&gt; people = manager.search(SimplePerson.class,
new DistinguishedName("dc=example,dc=com"), "uid=*", searchControls);
LdapUtils.newLdapName("dc=example,dc=com"), "uid=*", searchControls);
log.info("People found: " + people.size());
for (SimplePerson person : people) {
log.info( person );

View File

@@ -36,7 +36,7 @@
<para><programlisting>private String getDnForUser(String uid) {
Filter f = new EqualsFilter("uid", uid);
List result = ldapTemplate.search(DistinguishedName.EMPTY_PATH, f.toString(),
List result = ldapTemplate.search(LdapUtils.emptyLdapName(), f.toString(),
new AbstractContextMapper() {
protected Object doMapFromContext(DirContextOperations ctx) {
return ctx.getNameInNamespace();