spring-projects / spring-ldap

Spring LDAP
https://spring.io/spring-ldap
Apache License 2.0
342 stars 481 forks source link

LDAP-136: Setting Context.REFERRAL to 'follow' results in DN parse exception #174

Closed spring-projects-issues closed 13 years ago

spring-projects-issues commented 15 years ago

["Nate Moser":https://jira.spring.io/secure/ViewProfile.jspa?name=nate_moser](Migrated from ["LDAP-136":https://jira.spring.io/browse/LDAP-136?redirect=false]) said:

In order to follow referrals, I'm using LdapContextSource with env.put(Context.REFERRAL, "follow"); ctx.setBaseEnvironmentProperties(env);

when a search includes results from following a referral, I get a exception

Caused by: org.springframework.ldap.BadLdapGrammarException: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 5. Encountered: ":" (58), after : "" at org.springframework.ldap.core.DistinguishedName.parse(DistinguishedName.java:145) at org.springframework.ldap.core.DistinguishedName.(DistinguishedName.java:100) at org.springframework.ldap.core.DirContextAdapter.(DirContextAdapter.java:139) at org.springframework.ldap.core.support.DefaultDirObjectFactory.getObjectInstance(DefaultDirObjectFactory.java:61) at javax.naming.spi.DirectoryManager.createObjectFromFactories(Unknown Source) at javax.naming.spi.DirectoryManager.getObjectInstance(Unknown Source)

Setting a breakpoint at DirContextAdapter shows that the DN in question is prefixed with the LDAP URL for the referred-to LDAP server. Apparently that's how JNDI prefixes results returned from followed referrals, see http://java.sun.com/products/jndi/tutorial/ldap/referral/follow.html

At Mattias' suggestion, I subclassed DefaultDirObjectFactory and was able to work around the situation by stripping of the LDAP URL prefix. This could conceivably lead to namespace collision if a referred-to server has the same DIT structure as the original server (e.g., they both have a cn=nate,ou=users,dc=example,dc=com entry), but a situation the directory administrator should resolve.

I've tested this with searches Active Directory 2003, SunONE, and OpenLDAP servers with a single-hop referral to another server.

spring-projects-issues commented 15 years ago

["Nate Moser":https://jira.spring.io/secure/ViewProfile.jspa?name=nate_moser] said:

The subclass of org.springframework.ldap.core.support.DefaultDirObjectFactory that strips LDAP URLs from DNs returned when JNDI follows referrals.

spring-projects-issues commented 15 years ago

["Mattias Hellborg Arthursson":https://jira.spring.io/secure/ViewProfile.jspa?name=marthursson] said:

Trying to create a test case for this I'm running into some ambiguities: it seems that creating a @CompositeName@ using the string @ldap://localhost:389/ou=People,o=JNDITutorial@ (i.e. @new CompositeName("ldap://localhost:389/ou=People,o=JNDITutorial"@)results in a an instance with not one, but four parts. Could you just verify for me that you are indeed receiving a @CompositeName@ with one part only (i.e. that @name.get(0)@ on the instance handed from JNDI gives you the full string representing the referral)?

spring-projects-issues commented 15 years ago

["Nate Moser":https://jira.spring.io/secure/ViewProfile.jspa?name=nate_moser] said:

That's worrying for my implementation... with OpenLDAP I'm indeed getting the whole

name = {javax.naming.CompositeName@1297}"cn=jamesB,ou=QA,dc=example,dc=com" impl = {javax.naming.NameImpl@1560}"cn=jamesB,ou=QA,dc=example,dc=com" components = {java.util.Vector@1564} size = 1 [0] = {java.lang.String@1298}"cn=jamesB,ou=QA,dc=example,dc=com"

for an entry from the original directory, and

name = {javax.naming.CompositeName@1308}"\"ldap://server2.example.com:389/cn=user1,ou=ou1,dc=example,dc=com\"" impl = {javax.naming.NameImpl@1552}"\"ldap://server2.example.com:389/cn=user1,ou=ou1,dc=example,dc=com\"" components = {java.util.Vector@1556} size = 1 [0] = {java.lang.String@1309}"ldap://server2.example.com:389/cn=user1,ou=ou1,dc=example,dc=com"

for an entry coming from a referral, so name.get(0) gets the whole String in either case.

I'm seeing the same results for SunONE and AD referrals. I haven't set up OpenLDAP using the LDIF from the JNDI tutorial, though, so I'll give that a try.

Also, I'm using

org.springframework.ldap.core.LdapTemplate#search(String base, String filter, SearchControls controls, ContextMapper mapper)

so perhaps the use of ContextMapper is a factor? I'd certainly prefer it to work with all LdapTemplate methods.

spring-projects-issues commented 15 years ago

["Mattias Hellborg Arthursson":https://jira.spring.io/secure/ViewProfile.jspa?name=marthursson] said:

I'm doing a pure unit test against @DefaultDirObjectFactory@ here, so I think it's the construction of the @CompositeName@ that is the problem (I'm constructing it manually in my test case).

Actually I think your results are quite reassuring. In my test case I was creating the @CompositeName@ instance passing the string directly to the constructor. I'm sure the JNDI code is doing it differently; at least your tests indicate that's the case.

I'll try to get this in place for you to test the real thing tomorrow.

spring-projects-issues commented 15 years ago

["Mattias Hellborg Arthursson":https://jira.spring.io/secure/ViewProfile.jspa?name=marthursson] said:

This is now fixed in the trunk. I decided against using regular expressions as we've previously had severe performance hits caused by using these.

Also, it didn't feel right to just discard the referral information, so I decided to add that information to @DirContextAdapter@. If an instance of @DirContextAdapter@ is created with referral information (which will now be the case if it is created by @DefaultDirObjectFactory@ and the @Name@ contains referral information) the referral server url will be accessible using @DirContextAdapter#getReferralUrl@ and @DirContextAdapter.isReferral@ methods.

spring-projects-issues commented 13 years ago

Rodrigo Dinis said:

Yes, it sounds like the issue I am having. But I don't know how to implement the change. Could someone clarify how to build the solution?