Spring Lldap Authentication And Pooled Connections

I have a new project at work, which I’ve been pretty pumped about: create an Authentication and Self Registration Web Service to be used first by a mobile device, then by a plethora of apps.

I’m most familiar with the Jersey RESTful framework, so I started there.  I ran into some Spring @Autowired issues with my automated tests, but I found a way to get around that for now (an acceptable solution, but not the way I want it long term, more on this later).

So far the biggest hurdle I have hit was trying to get Spring Ldap’s ldapTemplate.authenticate(user, pwd) to work. The documentation makes it look pretty easy, and I already use Spring Ldap throughout my code base, so using the authenticate() method appears to be straight forward.

A few hours later, it was obvious it was not straight forward, and I needed to start from scratch to see why it was failing.

My first appContext.xml

<bean id="corporateConnectionDetails" class="org.springframework.ldap.core.support.LdapContextSource" scope="prototype">
<property name="url" value="${ldap.url}" />
<property name="userDn" value="${ldap.userDn}" />
<property name="password" value="${ldap.password}" />
<property name="pooled" value="false"/>
<property name="referral" value="${ldap.referral}"/>
</bean>

<bean id="corporateContextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="corporateConnectionDetails" />
<property name="dirContextValidator" ref="dirContextValidator" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
</bean>

The above code uses a basic LdapContextSource, which is then wrapped by a PoolingContextSource to create the pool from which LDAP queries are used.

To authenticate users, you should simply call ldapTemplate.authenticate(user, pwd), but with the settings above, authentication does not work.

After much troubleshooting (mainly adjusting the XML config, and retrying my JUnit tests), began to debug the Spring Ldap code. Then starring me in the face was an OperationNotSupportedException from the authenticate() method.

I decided to start from the beginning and read the documentation.

At the bottom of Sprig Ldap’s documentation on Pooling, I found a little, but very important piece of information.

9.5. Known Issues
9.5.1. Custom Authentication

The PoolingContextSource assumes that all DirContext objects retrieved from ContextSource.getReadOnlyContext() will have the same environment and likewise that all DirContext objects retrieved from ContextSource.getReadWriteContext() will have the same environment. This means that wrapping a LdapContextSource configured with an AuthenticationSource in a PoolingContextSource will not function as expected. The pool would be populated using the credentials of the first user and unless new connections were needed subsequent context requests would not be filled for the user specified by the AuthenticationSource for the requesting thread.

So far I have gotten around this issue by using a non-pooled connection. My service could use one, but it is not necessary for phase 1. So for now, I have fixed the issue with the following update to my appContext.xml:

The updated appContext.xml

<bean id="corporateLdapTemplate" class="org.springframework.ldap.core.LdapTemplate" scope="prototype">
<constructor-arg ref="corporateConnectionDetails" />
<property name="ignorePartialResultException" value="true"/>
</bean>

Note that the param has been changed to use the corporateConnectionDetails reference, which uses the LdapContextSource instead of the PoolingContextSource.

In the near future, I hope to find a way to implement and integrate my own Authentication service that will mesh with the PoolingContextSource in order to authenticate a single user.

Hope this helps someone else to not hit their thumb with the Spring Ldap hammer.

Names are important

“When I use a word,” Humpty Dumpty said, in a rather scornful tone, “It means just what I choose it to mean — neither more nor less. (Lewis Carroll, Through the Looking Glass)

Whether it be a variable, method, class, package, or especially an App’s name. The name is important. Get it wrong, and it can have repercussions that last years.

One of my current clients had a VP 10-15 years ago that went to a conference where some brilliant non-technical person informed him that naming apps in a Celestial manner would help foster creativity (or so the story goes).  However, the Celestial names have done nothing but create confusion ever since “Apus” and “Polaris” joined their software environment.  Since their inception, each app has had at least two names:

  • Polaris = ARS = Activity Reporting System
  • Apus = User Security and Application Profiles

This environment’s confusion is by no means limited to Celestial names.  There are also multiple apps that contain their version in the name: OP2 and CSR2.  However, so far my favorite is the so clearly defined “New XYZ” (e.g. New CRM).  There are well over 100 apps in this environment, and many of them have one or more of the aforementioned naming issues.

Renaming an app is fine, it happens all the time, especially as the requirements for an app evolve over time.  For example, “customer” could become “vendor”.   So the app could go from “Customer Management System” to “Vendor Management System”.  No problem, as long as everything else changes with the re-name as well: code repository names, links, build names, etc.  If it is not easy to change the names, at least create entirely new streams/branches/builds/whatever for the update.  If not, you then have multiple teams referring to the same app with completely different names.

Maintenance is a nightmare.  Users call into the Help Desk and say the CSM is having issues, so a member of the Infrastructure team is called to look at the issue, but he does not know of a CSM app.  The Infrastructure guy/gal has to spend 5-10 minutes trying to figure out what else the CSM app could be called (e.g. VMS) in order to look through logs to begin troubleshooting the issue.

I was migrating an app from an old app server to a new, and tried to get everything involved with the CMS app renamed to VMS as to prevent future confusion, but since this app had been renamed about 5 years ago, it was going to require Steve Jobs being resurrected to get everything changed.  In the end, nothing was changed, and name confusion persists.

For all that is holy, please pick the names of code and apps carefully.  Don’t be afraid to be too concise or too verbose; just be damn sure it is accurate.  No more, no less.