The need to query LDAP directories arises surprisingly rarely for me. As such, each time, I essentially need to relearn how to do this.
I recently contributed to a PHP based project in which LDAP queries would be needed in certain circumstances, once per session. But in the deployment environment, PHP had not been compiled with LDAP support, and I couldn’t ask for this to be updated.
So, I decided to write the needed function in Java, knowing that LDAP support is part of the Java standard libraries and the server we were deploying to was a Solaris machine, so I knew Java would be available. I could then call the Java program from PHP. Clearly, this approach is far from perfect, but its portable and ultimately I felt it was the best way to work within the constraints of the system. And because such queries would only be executed once a session–and only for certain users–the performance hit of this approach was acceptable.
Google is definitely my friend, and I quickly found a great turorial (unfortunately from Google cache–I can’t find it on the original site) to refresh my knowledge of querying LDAP, and started modifying the Java example given there. In my situation, I would know a user’s identifier and want to get their status at Cornell University, whether they are faculty, staff, a student, an alumnus, etc. So, I want to query for an attribute of a known record, which is to say, I only needed a narrowly defined way of querying LDAP that fit my particular situation, but I suspect this situation is common enough that others may find it useful:
import javax.naming.*; import javax.naming.directory.*; import java.util.Hashtable; public class SimpleQuery { public static void main(String[] args) { if (args.length != 2) { System.out.println(“Syntax: SimpleQuery query attribute”); return; } String query = args[0]; String attribute = args[1]; StringBuffer output = new StringBuffer(); try { String url = “ldap://directory.cornell.edu/o=Cornell%20University,c=US”; Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.ldap.LdapCtxFactory”); env.put(Context.PROVIDER_URL, url); DirContext context = new InitialDirContext(env); SearchControls ctrl = new SearchControls(); ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); NamingEnumeration enumeration = context.search(“”, query, ctrl); while (enumeration.hasMore()) { SearchResult result = (SearchResult) enumeration.next(); Attributes attribs = result.getAttributes(); NamingEnumeration values = ((BasicAttribute) attribs.get(attribute)).getAll(); while (values.hasMore()) { if (output.length() > 0) { output.append(“|”); } output.append(values.next().toString()); } } } catch (Exception e) { e.printStackTrace(); } System.out.print(output.toString()); } public SimpleQuery() {} }
Obviously, you will need to change the “url” variable for the LDAP directory you want to access, and you’ll probably feel much better if you populate this from an external property file. Also note that I am connecting anonymously to the LDAP directory, because in my case, I was only interested in retrieving publicly available information.
I wrote this in a way that compiles under Java 1.4, which was the version of Java on that server–another constraint I had to deal with. Therefore, I used casting instead of generics, StringBuffer instead of StringBuilder, etc. Ah, nostalgia. If you want to use this under Java 1.5, you can make the code a little more concise.
Cornell University uses Kerberos for centralized authentication, and they produce an Apache module that, once configured for a resource under Apache, redirects to a login page and upon successful authentication, returns to the resource with REMOTE_USER populated with the user’s identifier. Having REMOTE_USER populated then, one can assume that the user successfully authenticated and is a member of the Cornell University community. That user’s identifier is stored in the “uid” attribute of their LDAP record, so with it, I can get some information about that user using the Java program above:
<?php $netID = getenv(‘REMOTE_USER’); if (isset($netID) && $netID != ”) { $_SESSION[‘primary_affiliation’] = exec(‘java SimpleQuery uid=’ . $netID . ‘eduPersonPrimaryAffiliation’); } ?>
This PHP code is embedded within the resource configured to require authentication. In the Cornell LDAP directory, the “eduPersonPrimaryAffiliation” attribute contains strings like “alumni”, “staff”, etc. and I expect it to return one and only one value per person queried, so if you adopt this technique, you may need to perform extra checks.