From 08ce0a6407c48c1de6113cd6e4b95b664f84ff45 Mon Sep 17 00:00:00 2001 From: Mattias Hellborg Arthursson Date: Thu, 24 Oct 2013 12:01:29 +0200 Subject: [PATCH] LDAP-248: Documentation --- src/asciidoc/index.adoc | 215 ++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 77 deletions(-) diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 6dc92744..9f449865 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -193,6 +193,7 @@ Below is a list of the most important changes in Spring LDAP 2.0. * The ODM (Object-Directory Mapping) functionality has been moved to core and there are new methods in `LdapOperations`/`LdapTemplate` that uses this automatic translation to/from ODM-annotated classes. See <> for more information. * A custom XML namespace is now provided to simplify configuration of Spring LDAP. See <> for more information. * Spring Data Repository and QueryDSL support is now included in Spring LDAP. See <> for more information. +* `Name` instances as attribute values are now handled properly with regards to Distinguished Name equality in `DirContextAdapter` and ODM. See <> and <> for more information. * `DistinguishedName` and associated classes have been deprecated in favor of standard Java `LdapName`. See <> for information on how the library helps working with `LdapNames`. * Fluent LDAP query support has been added. This makes for a more pleasant programming experience when working with LDAP searches in Spring LDAP. See <> and <> for more information about the LDAP query builder support. * The old `authenticate` methods in `LdapTemplate` have been deprecated in favor of a couple of new `authenticate` methods that work with `LdapQuery` objects and __throw exceptions__ on authentication failure, making it easier for the user to find out what caused an authentication attempt to fail. @@ -712,7 +713,80 @@ public class PersonDaoImpl implements PersonDao { } ---- +[[dns-as-attribute-values]] +=== DirContextAdapter and Distinguished Names as Attribute Values. +When managing security groups in LDAP it is very common to have attribute values that are actually +distinguished names. Since distinguished name equality is not the same as String equality, handling these +attributes as normal strings when calculating attribute modifications will not work as expected. For instance, +if a `member` attribute has the value `cn=John Doe,ou=People` and we call `ctx.addAttributeValue("member", "CN=John Doe, OU=People")`, +the attribute will now be considered to have two values, even though the strings actually represent the same +distinguished name. + +As of version 2.0, if you supply `javax.naming.Name` instances to the attribute modification methods in `DirContextAdapter`, +modification calculation will use distinguished name equality, meaning that if we modify the example above to: +`ctx.addAttributeValue("member", LdapUtils.newLdapName("CN=John Doe, OU=People"))`, this will no longer be considered +a modification. + +.Group membership modification example +[source,java] +[subs="verbatim,quotes"] +---- +public class GroupDao implements BaseLdapNameAware { + private LdapTemplate ldapTemplate; + private LdapName baseLdapPath; + + public void setLdapTemplate(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + public void setBaseLdapPath(LdapName baseLdapPath) { + this.setBaseLdapPath(baseLdapPath); + } + + public void addMemberToGroup(String groupName, Person p) { + Name groupDn = buildGroupDn(groupName); + Name userDn = buildPersonDn( + person.getFullname(), + person.getCompany(), + person.getCountry()); + + DirContextOperation ctx = ldapTemplate.lookupContext(groupDn); + ctx.addAttributeValue("member", userDn); + + ldapTemplate.update(ctx); + } + + public void removeMemberFromGroup(String groupName, Person p) { + Name groupDn = buildGroupDn(String groupName); + Name userDn = buildPersonDn( + person.getFullname(), + person.getCompany(), + person.getCountry()); + + DirContextOperation ctx = ldapTemplate.lookupContext(groupDn); + ctx.removeAttributeValue("member", userDn); + + ldapTemplate.update(ctx); + } + + private Name buildGroupDn(String groupName) { + return LdapNameBuilder.newLdapName("ou=Groups") + .add("cn", groupName).build(); + } + + private Name buildPersonDn(String fullname, String company, String country) { + return LdapNameBuilder.newLdapName(baseLdapPath) + .add("c", country) + .add("ou", company) + .add("cn", fullname) + .build(); + } +} +---- + +In the example above we are implementing `BaseLdapNameAware`, in order to get hold of the base LDAP path as described in <>. +This is necessary because distinguished names as member attribute values must always be absolute from the directory root. === A Complete PersonDao Class To illustrate the power of Spring LDAP, here is a complete Person DAO implementation for LDAP in just 68 lines: @@ -862,82 +936,6 @@ The `@DnAttribute` annotation is used to map object class fields to and from com The `@Transient` annotation is used to indicate the field should be ignored by the object directory mapping and not mapped to an underlying LDAP property. Note that if a `@DnAttribute` is not to be bound to an Attribute, i.e. it is only part of the Distinguished Name and not represented by an object attibute, it must also be annotated with `@Transient`. -=== Type Conversion - -The object directory mapping relies on the `org.springframework.ldap.odm.typeconversion` package to convert LDAP attributes to Java fields. For simple setups, no particular configuraion is needed for this purpose. However, more complex mapping scenarios require the `ObjectDirectoryMapper` and its associated `ConverterManager` to be explicitly configured on the `LdapTemplate` instance. The default `ConverterManager` implementation uses the following algorithm when parsing objects to convert fields: - -. Try to find and use a `Converter` registered for the `fromClass`, `syntax` and `toClass` and use it. -. If this fails, then if the `toClass``isAssignableFrom` the `fromClass` then just assign it. -. If this fails try to find and use a `Converter` registered for the `fromClass` and the `toClass` ignoring the syntax. -. If this fails then throw a `ConverterException`. - - - -Implementations of the `ConverterManager` interface can be obtained from the `o.s.l.odm.typeconversion.impl.ConvertManagerFactoryBean`. The factory bean requires converter configurations to be declared in the bean configuration. - -The converterConfig property accepts a set of `ConverterConfig` classes, each one defining some conversion logic. A converter config is an instance of `o.s.l.odm.typeconversion.impl.ConverterManagerFactoryBean.ConverterConfig`. The config defines a set of source classes, the set of target classes, and an implementation of the `org.springframework.ldap.odm.typeconversion.impl.Converter` interface which provides the logic to convert from the `fromClass` to the `toClass`. A sample configuration is provided in the following example: - -.Configuring the Converter Manager Factory -[source,xml] -[subs="verbatim,quotes"] ----- - - - - - - - - - java.lang.String - - - - - java.lang.Byte - java.lang.Short - java.lang.Integer - java.lang.Long - java.lang.Float - java.lang.Double - java.lang.Boolean - - - - - - - - java.lang.Byte - java.lang.Short - java.lang.Integer - java.lang.Long - java.lang.Float - java.lang.Double - java.lang.Boolean - - - - - java.lang.String - - - - - - - - - - - - ----- - - === Execution When all components have been properly configured and annotated, the object mapping methods of `LdapTemplate` can be used as follows: @@ -995,13 +993,76 @@ public class OdmPersonDao { return ldapTemplate.findAll(Person.class); } - public List>Person< findByLastName(String lastName) { + public List findByLastName(String lastName) { return ldapTemplate.find(query().where("sn").is(lastName), Person.class); } } ---- +[[odm-dn-attributes]] +=== ODM and Distinguished Names as Attribute Values. + +Security groups in LDAP commonly contains a multi-value attribute where each of the values is the distinguished name +of a user in the system. The difficulties involved when handling these kinds of attributes are discussed in <>. + +ODM also has support for `javax.naming.Name` attribute values, making group modifications very easy: + +.Example Group representation +[source,java] +[subs="verbatim,quotes"] +---- +@Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=groups") +public class Group { + + @Id + private Name dn; + + @Attribute(name="cn") + @DnAttribute("cn") + private String name; + + @Attribute(name="uniqueMember") + private Set members; + + public Name getDn() { + return dn; + } + + public void setDn(Name dn) { + this.dn = dn; + } + + public Set getMembers() { + return members; + } + + public void setMembers(Set members) { + this.members = members; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void addMember(Name member) { + members.add(member); + } + + public void removeMember(Name member) { + members.remove(member); + } +} +---- + +Modifying group members using `setMembers`, `addMember` and `removeMember` above, and then calling `ldapTemplate.update()`, +attribute modifications will be calculated using distinguished name equality, meaning that the text formatting of +distinguished names will be disregarded when figuring out whether they are equal. + [[query-builder-advanced]] == Advanced LDAP Queries