An XML gateway to a directory

DSML for XML access to a directory


DSMLGateway is an xml-rpc server. DSML is an XML markup language for LDAP (directory) content. At this point, it can represent directory entries (data) and schema, but it does not yet have a query mechanism. Queries must still be formulated and executed natively with LDAP.

The 1.0 specification of DSML was developed by a number of companies, including Bowstreet, IBM, Netscape, Microsoft, Novell, and Oracle, and submitted to OASIS 12/7/1999. Continued development will happen in OASIS. Read about DSML at http://www.dsml.org.
 
 

What is a directory?

The dominant directory protocol today is Lightweight Directory Access Protocol (LDAP). That is the protocol used to talk to the Netscape/iPlanet Directory Server, Microsoft's ActiveDirectory, Novell's recent versions of NDS, IBM's SecureWay, and Oracle's LDAP gateway. Older X.500 directories have LDAP gateways which allow clients to use LDAP.

Directories are primarily used to store information about users, for purposes of authentication and for querying user profiles. Read about LDAP deployment in Understanding and Deploying Ldap Directory Services and about programming LDAP in LDAP Programming with Java.
 
 

LDAP search results


Data in LDAP is stored as hierarchically organized entries. Generally, a single entry contains all the information (attributes) for a particular user, although a deployment may split up the information into multiple entries. A standard LDAP command-line search program reports the results in LDIF (Lightweight Directory Interchange Format), for example:

java LDAPSearch -b "o=mcom.com" "uid=scarter"

dn: uid=scarter, ou=People, o=mcom.com
cn: Sam Carter
sn: Carter
givenname: Sam
objectclass: top
ou: Accounting
l: Sunnyvale
uid: scarter
mail: scarter@mcom.com
telephonenumber: +1 408 555 4798
facsimiletelephonenumber: +1 408 555 9751
roomnumber: 4612

The format has each attribute name followed by a colon and a value. If an attribute has multiple values, there is a line for each one. In DSML, the format would be:

java LDAPSearch -X -b "o=mcom.com" "uid=scarter"

<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">
 <dsml:directory-entries>
  <dsml:entry dn="uid=scarter, ou=People, o=mcom.com">
    <dsml:attr name="cn">
      <dsml:value>Sam Carter</dsml:value>
    </dsml:attr>
    <dsml:attr name="sn">
      <dsml:value>Carter</dsml:value>
    </dsml:attr>
    <dsml:attr name="givenname">
      <dsml:value>Sam</dsml:value>
    </dsml:attr>
    <dsml:objectclass>top</dsml:objectclass>
    <dsml:objectclass>person</dsml:objectclass>
    <dsml:objectclass>organizationalPerson</dsml:objectclass>
    <dsml:objectclass>inetOrgPerson</dsml:objectclass>
    <dsml:attr name="ou">
      <dsml:value>Accounting</dsml:value>
      <dsml:value>People</dsml:value>
    </dsml:attr>
    <dsml:attr name="l">
      <dsml:value>Sunnyvale</dsml:value>
    </dsml:attr>
    <dsml:attr name="uid">
      <dsml:value>scarter</dsml:value>
    </dsml:attr>
    <dsml:attr name="mail">
      <dsml:value>scarter@mcom.com</dsml:value>
    </dsml:attr>
    <dsml:attr name="telephonenumber">
      <dsml:value>+1 408 555 4798</dsml:value>
    </dsml:attr>
    <dsml:attr name="facsimiletelephonenumber">
      <dsml:value>+1 408 555 9751</dsml:value>
    </dsml:attr>
    <dsml:attr name="roomnumber">
      <dsml:value>4612</dsml:value>
    </dsml:attr>
  </dsml:entry>
 </dsml:directory-entries>
</dsml:dsml>
 
 

DSML and xml-rpc

DSMLGateway is a prototype for accessing directory data over xml-rpc. It is roughly based on public source code from Userland. Two methods are implemented: search and authenticate.

Authenticate is straightforward and could have been easily implemented without changes to the Userland xml-rpc code. You send a username and password and the server returns success or failure.

Search is a different story. A directory search could return hundreds or thousands or millions of results. Having the server accumulate all these results and then send them back to the client as the return value of the method call would just not work. So for this, the server returns results in a streaming way as soon as each result is available (from the directory, which also supplies results in a streaming way) and the client must process them as they arrive. That required major changes on both the server and the client side. The client registers a ResultListener with the xml-rpc client implementation to receive results as they arrive.

The prototype includes a modified xml-rpc Web server, so it's ready to run from the command-line.
 

Running the DSML xml-rpc server

There is a batch file webserver.bat that sets up the CLASSPATH and launches the server. The server requires SAX and a SAX parser, and the Java LDAP SDK. I've only tested it with Jim Clark's XP parser. If you already have sax.jar, xp.jar, and ldapjdk.jar, you don't need to download them; just make sure they're in your CLASSPATH.

You start up the xml-rpc server with options to tell it where the LDAP server is and what its base DN is (the top of the data tree where there is user information):

java WebServer
Usage: java WebServer -b LDAPBASE [-h LDAPHOST] [-p LDAPPORT] [-w WEBPORT] [-D]

Defaults: LDAPHOST=localhost, LDAPPORT=389, WEBPORT=8080, no debug
Example: java WebServer -b "o=airius.com" -h ldap.airius.com -p 3890 -w 8000 -D

java WebServer -b "o=airius.com"
started web server on port 8080
 

The sample clients

The sample command-line clients are quite simple. This is Authenticate (minus some debugging code):

  DsmlClient client = new DsmlClient (url);
  Vector v = new Vector ();
  v.addElement( args[ind++] );
  v.addElement( args[ind++] );
  Object res = client.execute ("dsml.authenticate", v);
  System.out.println( "Result: " + res );

  DsmlClient requires the HTTP URL of the xml-rpc server. The arguments are a user ID and a password. The result is a string: "OK" or "Failed".

  Search requires a little more, since it processes streaming results instead of a return value:

  DsmlClient client = new DsmlClient (url);
  client.setResultListener( new ResultPrinter() );
  Vector v = new Vector ();
  v.addElement( args[ind++] );
  Object res = client.execute ("dsml.search", v);
  // The results were streamed back, so res only contains the final
  // result

/**
 * Implementation to print out results as they arrive. For LDAP
 * searching, it is called once for each returned entry.
 */
class ResultPrinter implements ResultListener {
 public void result( Object o ) {
  System.out.print( o.toString() );
  System.out.flush();
 }

Search takes an LDAP search expression as argument, for example:

"objectclass=person" for all users
"sn=carter" for all users with the last name carter
"cn=sam*" for users with names starting with "sam" (case-insensitive)
"(&(givenname=sam)(l=mountain view))" for users named "sam" living in "mountain view"

The method currently does not take credentials as an argument, so the assumption is that the directory allows anonymous searches (which is not always the case, but is quite common). Another desirable future extension would be to allow specifying which attributes to return for each user, rather than returning all possible attributes for each result. The prototype always does a subtree search from the specified starting point (when starting the server). LDAP allows specifying a one-level search as well, but that was considered unimportant for this prototype.
 

Running the sample client command-line programs

There are batch files to set up the CLASSPATH and run each sample program:

authenticate

java Authenticate
Usage: java Authenticate [-D] ServerURL USERNAME PASSWORD

search

java Search
Usage: java Search [-D] ServerURL SEARCHEXPRESSION
 

Downloads

dsmlgw.zip
xp.jar
sax.jar
ldapjdk.jar
 
 

What do you think?

The prototype was originally done last year as a way for me to evaluate xml-rpc. If you think it's interesting or have ideas on how to make it more useful, fire away! Use the www.xmlrpc.com discussion group.

Rob Weltman
robw@worldspot.com