-2

Gooday,

Im having a rather bizarre problem with Spring-LDAP what in its essence seems more a JSNI problem then a Spring problem.

The Problem:

I have the following LDAP structure:

dc=company,dc=com,dc=productname
    |-o=TESTORG1
    |     |-ou=UserGroupName
    |     |-ou=users
    |           |-ou=user1@TESTORG1
    |-o=TESTORG2
    |     |-ou=User/Group/Name
    |     |-ou=users
    |           |-ou=user1@TESTORG2

So I can create this all quite neatly with LdapTemplate given trough Spring. However I run in to some weird problems if I try to recursively delete an organisation.

  @Test
  public void createSimpleOrganisationWithSpecialChars() throws NamingException {
    LdapOrganisation originalOrg = new LdapOrganisation("TESTORG2");
    LdapUserGroup userGroup = new LdapUserGroup("User/Group/Name");
    userGroup.addUser(new User("user1",originalOrg ));
    originalOrg.addUserGroup(userGroup);
    dataAccess.updateLdap(originalOrg);
    OrganisationDao org = new OrganisationDao(this.ldapTemplate);
    org.unbind(organisation,true);
  }

The implementation of the unbind is as follows:

    public void unbind(LdapOrganisation organisation, boolean recursive) {
      if(organisation != null) {
        DirContextAdapter context = new DirContextAdapter(buildDn(organisation));
        mapToContext(organisation, context);
        ldapTemplate.unbind(context.getDn(), recursive);
      }
    }

Now if I run this code I get the following error:

  org.springframework.ldap.InvalidNameException: Invalid name: "ou=User/Group/Name"; nested exception is javax.naming.InvalidNameException: Invalid name: "ou=User/Group/Name"

    at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:136)
    at org.springframework.ldap.support.LdapUtils.newLdapName(LdapUtils.java:416)
    at org.springframework.ldap.core.LdapTemplate.deleteRecursively(LdapTemplate.java:1103)
    at org.springframework.ldap.core.LdapTemplate$25.executeWithContext(LdapTemplate.java:1074)
    at org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:817)
    at org.springframework.ldap.core.LdapTemplate.executeReadWrite(LdapTemplate.java:812)
    at org.springframework.ldap.core.LdapTemplate.doUnbindRecursively(LdapTemplate.java:1072)
    at org.springframework.ldap.core.LdapTemplate.unbind(LdapTemplate.java:1036)
    at net.thadir.dataservices.ldap.dao.OrganisationDao.unbind(OrganisationDao.java:108)
    at net.thadir.dataservices.ldapaccess.LdapDataAccessTest.cleanTest(LdapDataAccessTest.java:93)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
  Caused by: javax.naming.InvalidNameException: Invalid name: "ou=User/Group/Name"
    at javax.naming.ldap.Rfc2253Parser.parseAttrType(Rfc2253Parser.java:155)
    at javax.naming.ldap.Rfc2253Parser.doParse(Rfc2253Parser.java:108)
    at javax.naming.ldap.Rfc2253Parser.parseDn(Rfc2253Parser.java:70)
    at javax.naming.ldap.LdapName.parse(LdapName.java:785)
    at javax.naming.ldap.LdapName.<init>(LdapName.java:123)
    at org.springframework.ldap.support.LdapUtils.newLdapName(LdapUtils.java:414)
    ... 37 more

Now if I just do a normal unbind in the test it self on just the usergroup. There is no real problem.. But it is actually a problem. Because it seems Spring uses for recursive deletes the JDNI patern (thats stricter then what LDAP allows) and for normal unbinds. It works perfectly.

Now I really want to make my recursive work out of the box. I tried to delete the groups with the following code:

In the OrganisationDoa:

  public void unbind(LdapOrganisation organisation, boolean recursive) {
    if(organisation != null) {
      UserGroupDao ugu = new UserGroupDao(ldapTemplate);
      ugu.delete(organisation);
      DirContextAdapter context = new DirContextAdapter(buildDn(organisation));
      mapToContext(organisation, context);
      ldapTemplate.unbind(context.getDn(), recursive);
    }
  }

And in the UserGroupDao:

  public void delete(LdapOrganisation ldapOrganisation, String ldapUserGroup) {
        ldapTemplate.unbind(buildDn(ldapOrganisation.getOrganisationShortname(), ldapUserGroup));
  }

  public void delete(LdapOrganisation ldapOrganisation) {
        for (LdapUserGroup userGroup : ldapOrganisation.getUserGroups()) {
              delete(ldapOrganisation,userGroup.getGroupName());
        }
  }

But this seems to not work, seeing i still get the same stack trace (so trying to do it in the same tread seems not to work).

I have been reading up on all these RFC specs and the seem quite old. And LDAP seems to allow it its just that because Spring seems to use JDNI for the recursive part that I get this problem. And doing a pre clean up on parts that you expect to be corrupted seems not to work as well. So I would love to hear some suggestions/ideas how to fix this.

Thadir
  • 201
  • 2
  • 16
  • JDNI? JSNI? JNDI? The DIT you list is impossible. You can't have `o=TESTORG1` twice under the same parent. It also doesn't agree with your code. 'JDNI patern' is undefined. Unclear what you're asking. – user207421 Feb 24 '17 at 23:11
  • Your rigch typo in me filting out real names.. Fixed it thx. And im talking about that Spring is using inbedded the LdapName from javax that uses the Rfc2253 for validation for DN's witch is only needed for a whole other subject. An LDAP allouws / in the cnname. – Thadir Feb 25 '17 at 00:17

1 Answers1

0

In the end I had to make my own implementation of the org.springframework.ldap.core.LdapTemplate to solve the probmen the fix is as folows:

package net.thadir.dataservices.ldap.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;

import javax.naming.Binding;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapName;

public class FixedLdapTemplate extends LdapTemplate {
  private static final Logger LOGGER = LoggerFactory.getLogger(FixedLdapTemplate.class);

  public FixedLdapTemplate(LdapContextSource contextSource) {
    super(contextSource);
  }

  /**
   * Delete all subcontexts including the current one recursively.
   *
   * @param ctx The context to use for deleting.
   * @param name The starting point to delete recursively.
   * @throws NamingException if any error occurs
   */
  @Override
  protected void deleteRecursively(DirContext ctx, Name name) {
    NamingEnumeration enumeration = null;
    try {
      enumeration = ctx.listBindings(name);
      while (enumeration.hasMore()) {
        Binding binding = (Binding) enumeration.next();
        String bindingName = binding.getName();
        LdapName childName = org.springframework.ldap.support.LdapUtils.newLdapName(fixJDNIBug(bindingName));
        childName.addAll(0, name);
        deleteRecursively(ctx, childName);
      }
      ctx.unbind(name);
      LOGGER.debug("Entry {} deleted", name);
    }
    catch (javax.naming.NamingException e) {
      throw org.springframework.ldap.support.LdapUtils.convertLdapException(e);
    }
    finally {
      try {
        if (enumeration != null) {
          enumeration.close();
        }
      }
      catch (Exception e) {
        LOGGER.trace("Never mind this", e);
      }
    }
  }

  /**
   * Becouse somhow JDNI things that if you have a binding with some special characters it adds some extra " to the beginnin and the end. We have to remove it.
   *
   * @param bindingName
   * @return
   */
  private static String fixJDNIBug(String bindingName) {
    String result = bindingName;
    if(result.contains("/") && result.startsWith("\"") && result.endsWith("\"")) {
      result = result.substring(1,bindingName.length()-1);
    }
    return result;
  }
}

The bug in question has been part of JDNI for the last 10 years, so I hope it will be fixed in Spring it self soon. Becouse its sad that I have to extend there classes for sutch a small thing.

Thadir
  • 201
  • 2
  • 16