Referential Integrity using Spring LDAP

The directory servers of today are packed with a lot of nice features, one of them being Referential Integrity which performs integrity updates on attributes like member, uniqueMember, owner etc. Simpler put, when an entry updates its distinguished name, all references using the old distinguished name get updated to the new one.

However, there can be cases where your directory server setup might have to avoid using this feature. In our case we had a multi master environment and the plug-in cannot be enabled on both machines as it would create a circular update loop between the master servers. To have one machine in production with the plug-in wasn’t a good choice either as we want the master configurations to be same on both machines and if one master goes down then there is a 50-50 chance that the Referential Integrity goes down with it.

Another reason is that you might not store distinguished names on relations attributes, but values like guid or uid, and then the Referential Integrity won’t work.

Still this point to an interesting feature which should be pretty easy to implement in Java using Spring LDAP.

Scenario 1

A person who’s used as owner in some entries updates his/her uid from old.uid to new.uid, where uid is a part of the distinguished name. For example, Jane Unmarried changes here name to Jane Doe: “uid=jane.unmarried, dc=jayway, dc=com” -> “uid=jane.doe, dc=jayway, dc=com”.
Feature: Update all owner references to the new distinguished name.
Prerequisite: Single value attribute. The owner attribute should be indexed for best performance.

The owner attribute might be featured in many different objectclasses, so the tiresome way would be to make searches per objectclass and update them one by one. However, we can make use of a nice feature in LDAP: attributes are global and standardized according to an LDAP schema. The owner attribute is of the same type everywhere, so there is no risk of accidentally changing an owner attribute that is used for something else. Thus, a much better way would be to make a more general search:

EqualsFilter filter = new EqualsFilter("owner", "uid=old.uid, dc=jayway, dc=com");

And what do we want to return? The distinguished name should be sufficient:

protected AbstractContextMapper getAbstractMapper() {
        return new AbstractContextMapper() {

            /* Returns the distinguished name of the object it found. */
            protected Object doMapFromContext(DirContextOperations ctx) {
                return ctx.getNameInNamespace();

This is all the information we need; a list of pointers to all entries in the LDAP structure where there is an owner attribute with the value “uid=old.uid, dc=jayway, dc=com”. We don’t care which type of entries were found, as the only thing we’re interested in is updating the value of the owner attribute.

Now do the search:

List hits =

We need to iterate through the search hits and only update the attribute, not the whole object, as we do not know anything about the object type:

for (Name someObjectDn: hits) {
    DirContextOperations context = ldapTemplate.lookupContext(someObjectDn);
    context.setAttributeValue("owner", "uid=new.uid, dc=jayway, dc=com");'


And we’re done, the owner references are updated to uid=new.uid, dc=jayway, dc=com.

What if the owner person gets deleted? Well, then the owner values should be removed and all we need to do is replacing this:

context.setAttributeValue("owner", "uid=new.uid, dc=jayway, dc=com");

with this:

context.setAttributeValue("owner", null);

and the attribute owner will be removed from all the objects.


So we see that all we need for these kind of operations are:

    1. The attribute name
    2. The current value
    3. The new value, null if to remove the attribute

Scenario 1 handles a single value attribute but it shouldn’t be any harder with a multivalue attribute. The problem is that it’s optional to define in an LDAP Schema whether an attribute is singlevalue or multivalue, so we cannot be certain what kind of attribute we’re updating. The easiest way to solve this is to let the method have a boolean parameter for this:

    4. Is multivalue.

Scenario 2

An person updates his/her uid (from old.uid to new.uid) and uid is a part of the distinguished name.
Feature: Update person references in all object classes she is a member of.
Prerequisite: Multivalue attribute. The uniqueMember attribute should be indexed for best performance.

The filter is almost the same as above:

EqualsFilter filter = new EqualsFilter("uniqueMember", "uid=old.uid, dc=jayway, dc=com");

The context mapper is the same as above, it’s still the distinguished names of the objects that carry the attribute uniqueMember we’re interested in. The search is also the same as above. But as uniqueMember is a list we need to iterate all the members in the list and replace the old value with the new:

for (Name someObjectDn: hits) {
    DirContextOperations context = ldapTemplate.lookupContext(someObjectDn);
    String[] members = context.getStringAttributes("uniqueMember");

     for (int i = 0; i < members.length; i++) {
            if (members[i].equals("uid=old.uid, dc=jayway, dc=com")) {
                members[i] = "uid=new.uid, dc=jayway, dc=com";


    context.setAttributeValues("uniqueMember", members);



It's easy to get the same functionality as the Referential Integrity using only Spring LDAP with very little custom code. You can introduce relationship attributes in new object classes and one method call will handle them all. An upside is that the filters use string values, so your relations attributes are not forced to contain distinguished names, but might be other values like uid or guid. However, using distinguished name is recommended.

As the searches all start from base, the searches might be less efficient as it scans the whole directory structure for the attribute. So this isn't recommended on non-indexed attributes when having large data volumes. The same applies to using the Referential Integrity within the directory server.

Thanks to Ulrik Sandberg and Andreas Andersson for their inputs on this blog.

Leave a Reply