Digital Edition

SYS-CON.TV
Connecting CF to LDAP
Connecting CF to LDAP

When I started using ColdFusion, one of the tags that piqued my curiosity most was <CFLDAP>. At the time, I had only a vague idea what LDAP was and when I started to write applications that extended beyond single pages, I realized I needed some kind of authentication. I tried the <CF_LOGIN> tag but wasn't thrilled with the idea of storing usernames and passwords in a table.

At the same time, I was setting up Netscape's Messaging Server. While it can use a local file for user information, it prefers an LDAP directory. I immediately saw the advantage of using the LDAP server for both Messaging Server and my ColdFusion applications. So I set about putting up Netscape's Directory Server.

I quickly learned that LDAP directories are significantly different from SQL databases. An LDAP directory is a specialized database for storing information about people and places. As such, the field names, called attributes, are predefined. Although the actual database structure is fairly flat, the directory itself is treelike. The tree metaphor is used frequently in the LDAP world: branches are where the tree splits, leaves are the terminal entries off a branch.

Populating my LDAP directory proved to be a trial by fire as I learned how to use the <CFLDAP> tag. I used ColdFusion to populate the directory and to maintain synchronization between it and the Oracle database it's based on.

 

LDAP Basics
LDAP queries differ from SQL queries. For example, search information is passed as parameters. While the format for the query is different, the data usually comes back from the server formatted like an SQL query. I say "usually" because it's possible for a given attribute to have multiple values. These are returned as comma-delimited lists.

LDAP directories use a fixed list of column names called attributes, which are defined in the LDAP specification. In addition to the attributes listed in the Advanced ColdFusion Development book, you can find a more complete list at www3.innosoft.com/ldapworld/rfc1617.txt.

Like <CFQUERY>, <CFLDAP> handles all interactions with the LDAP directory. You'll use it for querying, inserting, updating and deleting information from the directory. ColdFusion does some error-checking, but more advanced functions are simply passed directly to the directory server. You'll have to rely on the directory server logs for detailed information about bad LDAP operations.

Custom CF LDAP Tags
A check of the Developer's Exchange turns up two tags for working with LDAP: LDAP_Group and LDIFToQuery. Of the two, the LDAP_Group has proven the most useful. It allows you to maintain directory groups via ColdFusion, rather than the UNIX commands or the server console.

LDIFToQuery will convert LDAP Directory Interchange Format files into queries. LDIF is generally used for bulk loading and unloading a directory server.

Querying an LDAP Directory
Building a basic query is fairly simple. You'll need to provide the name of your directory server, attributes you want, start and query names. The example below will get and print all the distinguished names (dn) at the United States branch in the University of Michigan's LDAP server:

<cfldap name="orglist" server="ldap.itd.umich.edu" attributes="dn" start="c=US"> <cfoutput query="orglist"> #dn#
</cfoutput>

The equivalent search in SQL would be:

select dn
from ldap
where c = "US'

Filters, the rough equivalent of SQL's WHERE, can be used to narrow the query. The syntax for filters is a bit tortured. To select, for instance, just the State University of New York entries in the example above, you'd add this:

filter="(o=State University of New York*)"

Implementing more complex filters is an interesting challenge. For instance, if I want all the people whose last names fall between A and D, I'd use a filter like:

filter="(|(sn=A*)(sn=B*)(sn=C*)(sn=D*))"

In this example, the | ORs the four subfilters, so it'll return everything from A to D. You can combine filters from different attributes and nest them as much as you'd like. The more complex your filter is, the longer it'll take the LDAP server to process. Filter performance is affected by attribute indexing. If you plan to search frequently on a particular attribute, make sure the directory server indexes it.

SCOPEing the Query
The examples above look for entries at the level you specify in the START option. In this case we've been looking at entries that fall immediately below the c=US portion of the directory. If there are entries under lower branches, we won't see them. To get to those entries, we need to use SCOPE.

SCOPE can have one of three values: onelevel, base and subtree. The default, onelevel, will go down the directory exactly one branch. That's why we get the organization entries. Base will return entries only at the base level, which is the same as the start.

Subtree, the most productive of all three, will traverse down all branches under the START option, looking for entries that match your filter. If you're not sure where an entry might be, subtree is the easiest way to find it. Since subtree searches the entire LDAP directory, it can take a little longer.

Sorting Queries
The ability to sort your queries is a bit limited. You can only sort on one field. For instance, to sort by last name add SORT= "sn ASC" to your query. The LDAP server will happily return the list to you, sorted by last name. Because names are stored in the commonname (cn) attribute in a human-readable manner, SORT="cn ASC" will return a list sorted by first name.

Other LDAP Peculiarities
Postal addresses are stored in the postalAddress attribute in an LDAP directory. The individual lines of a postal address are separated by a $. My work address, for instance, would be Plattsburgh SUNY$Computing Support$Feinberg Library$Plattsburgh, NY 12901. You could use ListToArray or a <CFLOOP> to properly output the list. The example below will print an address properly:

<cfif len(postaladdress) gt 0>
<cfloop index="addr" list="#postalAddress#" delimiters="$">
<cfoutput>#addr#</cfoutput>

</cfloop>
</cfif>

The standard for postal addresses specifies a maximum of 30 characters in six rows.

Other problems to watch out for are timeouts, maximum number of records and access controls lists (ACLs). The default timeout for queries through ColdFusion is 60 seconds. You can specify a longer timeout; however, you may run into the timeout of the server. There's no maximum number of records ColdFusion will ask for. You can specify a MAXROWS amount if you think your query may return an inordinately large result set. Most servers have an internal limit on the maximum number of entries they'll return. You may want to check the server settings.

Access control lists are a way of limiting who can access what information. For instance, the default ACLs in Netscape's Directory Server prevent a user from changing someone else's password and anonymous users from making any changes. You can implement ACLs that prevent certain fields from being returned or return only those fields to specific users. If you know the field exists and the data's there, chances are an ACL is preventing you from seeing it.

ColdFusion queries your LDAP server as an anonymous user. You can, and probably should, set up a user account in the LDAP directory that your CF server can use. You can give that account permission beyond that of an anonymous user but less than the directory administrator. You'll need to include the USERNAME and PASSWORD options in your <CFLDAP> queries.

Going Beyond Simple Queries
Okay. Now that you know how to query an LDAP directory, let's add a record. This is where things start to get really hairy. You have to think carefully about what information you want to store in the directory before you do it. Most LDAP server manuals have detailed guides for implementing a directory structure.

Actually, adding the record is relatively straightforward. You'll need to come up with a distinguished name that matches the one used in your directory server. Next, build the list of required object classes and the attributes list. To actually add the entry, you'll need a username that has sufficient permissions.

In Listing 1, I'm adding Rick Deckard to the Los Angeles Police Department directory. There's no real minimum to the information you must add other than the dn. Your directory server may do syntax checking to make sure you don't add attributes without the appropriate object classes, so it's always a good idea to make sure everything's okay before adding an entry.

In composing the attribute list to add an entry, the object classes come first, followed by a semicolon, then the attributes and their values. Each attribute is separated by a semicolon as well. I build the object class list and the attribute list separately because I sometimes need to add object classes based on the type of user. For instance, if one of my users has an e-mail account, I can easily add the appropriate object classes and attributes.

Modifying Existing Records
Modifying a directory entry is similar to an add. You'll need to construct a list of attributes, all separated by semicolons. If you're adding any object classes, you'll need to construct a new object class list. While it makes sense to us to just pass the additional object classes, most LDAP servers require the entire object class list. The program in Listing 2 will update Deckard's entry in the LAPD directory server.

Rather than trying to figure out what the dn of an entry should be, I do a query for the record I want to modify. I'll use the dn later in the modify query. The additional hit against the directory server doesn't slow down the process significantly.

In the program I use to keep my directory server in sync with our Oracle database, I'll query the Oracle database and do a lookup and modify for each entry. Even with an update of over 6,000 records, my program will usually run in just a few minutes.

Changing the DN
You may find at some time that you need to change the dn for an entry. In setting up my directory server, I initially used a number for the uid portion of the dn. After populating the database, I realized the folly of my ways and wrote a program to change the uid to something more reasonable: the individual's username on our mainframe. Listing 3 changes Deckard's uid from his last name and first initial to his BRU number.

When changing the dn, you can generally change only the leftmost portion. For instance, the dn for me is:

uid=andersdl, ou=people, o=plattsburgh.edu

Using the modifydn query, only the uid=andersdl portion can be changed. It's not possible to move entries around in the tree structure using modifydn. Instead, you'll have to delete the entry and re-create it under the new branch.

Also, you can't use modifydn to change the name of a branch. If you want to change the ou=people branch to ou=employees, you have to remove all the entries under people, then rename it.

Deleting Attributes from an Entry
While you can delete portions of an LDAP entry at the UNIX command line, it's not really possible with ColdFusion. Instead, I do an update and set the values of the fields to null. Listing 4 removes Deckard's manager.

If you've heavily modified an LDAP entry and are concerned that it may contain many unused attributes, simply delete the entire record and re-add it.

Deleting an Entry
If you know the dn, removing an entry requires only one query. To delete the entry, set the query type to "delete" and provide the dn. As with any other query that modifies the directory, you'll need the proper user permissions to delete the entry. Listing 5 deletes Deckard's entry.

The delete operation removes only one entry at a time and will not remove leaf entries. If you need to remove a range of entries, you'll have to loop over the list and do a delete query for each. To delete a branch that has leaf entries, remove all its leaf entries first.

Tying It Together
Your directory server information can be used for the same purposes you'd use an SQL database. Because directory servers aren't SQL servers, there are some caveats.

As I mentioned above, directory queries can be sorted on only one field. If you need to sort on last and first name, you'll need to write some code to postprocess the query.

In some of my applications I use groups on the server to grant access to applications. In certain applications I'll generate a select list from the group. Since group queries come back from the server as comma-delimited lists, they require some conversion before they can be used for further queries. In my application I'll get the list of unique members, then build a filter that'll return the users I want. The code below will take a list of uids and convert it to a filter:

<cfset uidq = "(|">
<cfloop index="value" list="#userlist.uniquemember#" delimiters=",">
<cfif left(trim(value),"3") eq "uid">
<cfset uidq = uidq & "(" & trim(value) & ")">
</cfif>
</cfloop>
<cfset uidq = uidq & ")">

One minor frustration is the inability to select attributes using AS, such as you can do in SQL. If you need the equivalent, you can certainly use the query functions (QueryNew, QueryAddRow, QuerySetCell) to reformat the directory. I've found it convenient when working with data from the directory server that's similar to a query from my Oracle server.

Using LDAP for Authentication
For me, the primary purpose of putting up a directory server was authentication. We're trying to reduce the number of passwords our users have to remember. At the same time, we're trying to expand the number of services we offer. Netscape's Directory Server, with its ability to synchronize Windows NT accounts, has proved to be a good fit for us.

I've configured my ColdFusion server to use the directory server for authentication. If you've skimmed the manual, you've probably noticed it can get complicated. Essentially, you can use your directory server as the source for user authentication. I won't go into how to set up security. Ben Forta's book, Advanced ColdFusion Application Development, covers setting up security rather well.

The only difficulty I've run into has been in adding and removing users and groups via the user administration screens in the CF Administrator. This is a known bug in the current version of ColdFusion Server. I have to type in the dn for users and groups that I want added. Unfortunately, removing users means I have to re-create my user lists.

Final Thoughts
Just as with any datasource, it pays to know as much about your directory schema as you can. Knowing what fields are used in your directory and the overall structure of the tree can help immensely in formulating queries. Knowing how LDAP servers work can also be informative. You may want to look at some of the LDAP Web pages listed below.

If you're just starting out with directory servers, try to set up a small test machine. A couple of free LDAP servers run on Linux, so if you have an extra machine, you might want to set it up as your testbed. (By the way, you may have noticed Barbara [Babs] Jansen used in many LDAP examples. If you don't know who she is, search the Internet Movie Database [www.imdb.com] for Martha Smith.)

References
LDAP FAQ:
www3.innosoft.com/ldapworld/ldapfaq.html
LDAP Roadmap & FAQ:
www.kingsmountain com ldapRoadmap.shtml
Lightweight Directory Access Protocol:
www.umich.edu/~dirsvcs/ldap/index.html
Netscape Directory Service Docs:
http://home.netscape.com/eng/server/ directory/4.0/

About David Anderson
David Anderson is a senior programmer/analyst at the University of Buffalo. He specializes in web user experience and technology.

In order to post a comment you need to be registered and logged in.

Register | Sign-in

Reader Feedback: Page 1 of 1



ADS BY GOOGLE
Subscribe to the World's Most Powerful Newsletters

ADS BY GOOGLE

Tapping into blockchain revolution early enough translates into a substantial business competitivene...
In his session at 21st Cloud Expo, Raju Shreewastava, founder of Big Data Trunk, provided a fun and ...
In his session at 23rd International CloudEXPO, Raju Shreewastava, founder of Big Data Trunk, will p...
In his session at 20th Cloud Expo, Scott Davis, CTO of Embotics, discussed how automation can provid...
I spend a lot of time helping organizations to “think like a data scientist.” My book “Big Data MBA:...
While some developers care passionately about how data centers and clouds are architected, for most,...
SYS-CON Events announced today that Secure Channels, a cybersecurity firm, will exhibit at SYS-CON's...
In his session at 20th Cloud Expo, Mike Johnston, an infrastructure engineer at Supergiant.io, discu...
"We work around really protecting the confidentiality of information, and by doing so we've develope...
Today’s AI cannot create an algorithm that satisfies a human’s intent in all but the simplest cases....
Most DevOps journeys involve several phases of maturity. Research shows that the inflection point wh...
Public clouds dominate IT conversations but the next phase of cloud evolutions are "multi" hybrid cl...
After years of investments and acquisitions, CloudBlue was created with the goal of building the wor...
Cloud-Native thinking and Serverless Computing are now the norm in financial services, manufacturing...
In today's always-on world, customer expectations have changed. Competitive differentiation is deliv...
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with exp...
Artifex Software began 25-years ago with Ghostscript, a page description language (PDL) interpreter ...
Darktrace is the world's leading AI company for cyber security. Created by mathematicians from the U...
DevOps is under attack because developers don’t want to mess with infrastructure. They will happily ...
Blockchain has shifted from hype to reality across many industries including Financial Services, Sup...