Lightweight Directory - Directories are kind of like a database but not really. A directory is a specialized database that is optimized for lookups. Unlike a traditional RDBMS, LDAP is not designed to show complex relationships between relations. Imagine if 99% of your actions on were going to be simple "selects", and you wanted anyone, anywhere to be able to do these selects over the Internet. This is where LDAP excels. Examples of directories are the TVGuide, the phone book, a library card catalog, and DNS. "Give me the phone number of John Smith." "Give me all the tv shows that are on tonight on the Sci-Fi channel." Access Protocol - LDAP is an outgrowth of the x.500 standard. LDAP is an open standard, unlike many other proprietary directory solutions. Most of the directory-like solutions that were out on the market are now very similar to LDAP. Some of these solution providers, Sun and Microsoft specifically, have designed JNDI and ADSI APIs so that you can connect with any kind of directory service. This is kind of like ODBC or JDBC is to an RDBMS. Cool things you can do with LDAP -Contact Management -Users and Security -Image storage -Document Management -Store business logic - actual code or SQL statements -Your ideas? How does LDAP work? Client connects to server --> operations --> disconnect from server These operations include: 1. binding to server 2. searching for an entry 3. comparing entries 4. adding an entry 5. modifying existing entries 6. removing an entry
What about LDAP heirarchies and schemas? The directory schema is the outline of how the directory will be laid out and what kind of information it will hold. You design your directorys schema by modifying some text .conf files. Netscape has a gui to help you with this. Example1: slapd.oc.conf objectclass conf file Here is the file in which objectclasses are defined. ###################################################### objectclass person oid 2.5.6.6 superior top requires sn, cn allows description, seeAlso, telephoneNumber, userPassword Example 2: slapd.at.conf attribute conf file Here is the file in which we define attributes of each objectclass are defined. ####################################################### # X.500(93) User Schema for use with LDAP # Taken from <draft-ietf-asid-ldapv3schema-x500-00.txt> ####################################################### attribute objectClass 2.5.4.0 cis attribute aliasedObjectName 2.5.4.1 dn attribute knowledgeInformation 2.5.4.2 cis attribute cn commonName 2.5.4.3 cis attribute sn surName 2.5.4.4 cis attribute serialNumber 2.5.4.5 cis attribute c countryName 2.5.4.6 cis ... The format of this file is: attribute attribute-name [attribute-aliases] [attribute-oid] syntax Example 3: building a directory Uniqueness and Heirarchy. Each entry in the LDAP directory is expected to be unique and heirarchical. All entries in an LDAP directory structure are uniquely identified through their DN (distinguished name). Since the directory structure is potentially the entire world, this means that each DN has to hold information that will make it unique in the directory and in the world. The DN is therefore a kind of unique key. In addition, each layer of the directory builds on the layer before it. All this and more info goes into the LDIF file (LDAP Data Interchange Format). There are a bunch of scripts and utilities to help you modify these files if you have a server. You can make your own attributes. First you should make a new objectclass. Then add your new attribute to the objectclass. For example we would add to the slapd.oc.conf file: airiusPerson superior inetOrgPerson allowed dateOfBirth, preferredOS airiusOrganization superior organization allowed buildingFloor, vicePresident OR airiusEntry allowed dateOfBirth, preferredOS, buildingFloor, vicePresident More information can be found at "Customizing the Schema" http://developer.netscape.com/docs/manuals/directory/deploy30/data.htm
How do you make an LDAP client? There are SDKs in many languages including perl, C, C++ and Java, to help you make an LDAP client. Go get the SDK for your language and use the functions that it provides to connect to and operate on an LDAP server. More details below. How do you make an LDAP server? There are many options, including Sun, Netscape, Microsoft, Qualcomm, OpenLDAP(free). If you don't want to install your own directory service, but just want to play with LDAP, instructions for using publically available LDAP servers, such as bigfoot and four11, are below.
Getting the client environment set up for perl (1) Linux and perl 5.004 alone a. Install perl b. Install the LDAP SDK for C from http://developer.netscape.com/directory (binary) c. Install the LDAP SDK for perl from http://www.mozilla.org (you build) or Netscape (binary) (2) Windows NT and perl a. Install the non-ActiveState perl version ftp://ftp.cs.colorado.edu/pub/perl/CPAN/ports/win32/Standard/x86/perl5.00402- bindist04-bc.zip b. Install the LDAP SDK for C from Netscape c. Install the LDAP SDK for perl from Netscape Getting the server environment set up (1) Installing your own LDAP server a. Linux: OpenLDAP http://www.openldap.org b. Windows NT: Netscape Directory Server. Free evals from Netscape? (2) Using a publically available LDAP server This is generally easier to start with since you know it is set up properly and is functioning correctly. Plus you don't really need anything special other than a standard version 4 web browser. Publically available LDAP servers: ldap.bigfoot.com ldap.four11.com (now Yahoo People?) ldap.switchboard.com (used by AltaVista people searches?) usdsa.psi.net ldap.itd.umich.edu mailhub.jpl.nasa.gov
How to connect your client to a server We will use a publically available LDAP server in these examples, since that is very easy and everyone can participate. There are 4 ways to get started: (1) LDAP url from the browser (no perl or SDK required) (2) sample perl scripts that come with the SDK (perl and SDK required) (3) write a little perl client to connect (perl and SDK required) (4) write a little perl client to connect through an URL (perl and SDK required) (1) You can use an LDAP url with IE 4+or Netscape 4+. In Netscape, you can put the search strings right on the URL line, but in Explorer, you just put the ldap address, and you are prompted for the rest (Explorer is kind of lame for this reason, and I do not recommend using it while you are trying to learn about LDAP). Helpful info http://www.ogre.com/mirror/nscp/jsdk3/url.htm. This is the format of an LDAP URL: ldap[s]://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter> Component Description <hostname> Name (or IP address in dotted format) of the LDAP server (for example, ldap.netscape.com or 192.202.185.90). <port> Port number of the LDAP server. If no port is specified, the standard LDAP port (389) is used. <base_dn> Distinguished name (DN) of an entry in the directory. This DN identifies the entry that is starting point of the search. If this component is empty, the search starts at the root DN. <attributes> The attributes to be returned. To specify more than one attribute, use commas to delimit the attributes (for example, "mail,telephoneNumber"). If no attributes are specified in the URL, all attributes are returned. <scope> The scope of the search, which can be one of these values: base: retrieves information only about the distinguished name (<base_dn>) specified in the URL. one: retrieves information about entries one level below the distinguished name (<base_dn>) specified in the URL. The base entry is not included in this scope. sub: retrieves information about entries at all levels below the distinguished name (<base_dn>) specified in the URL. The base entry is included in this scope. If no scope is specified, the server performs a base search. <filter> Search filter to apply to entries within the specified scope of the search. If no filter is specified, the server uses the filter (objectClass=*). Example 1 ldap://ldap.bigfoot.com:389/o=airius.com?cn,mail,?sub?(cn=billybob thornton) In this example we are asking for specific fields (cn, mail). Bigfoot returns: billybob thornton Name billybob thornton Email punkguyz@hotmail.com Four11 returns: Billybob Thornton Email big_bad_bill@yahoo.com Example 2: ldap://ldap.bigfoot.com:389/o=airius.com??sub?(cn=billybob thornton) In this example, we left attributes blank, so we will end up with all the fields, which is interesting because then you can see that layer of the directory schema. Bigfoot returns: billybob thornton Email punkguyz@hotmail.com Name billybob thornton Organization hotmail.com First Name billybob surname thornton Four11 returns: Billybob Thornton City Fargo c US Email big_bad_bill@yahoo.com First Name Billybob Last Name Thornton st ND Name Billybob Thornton
(2) You can use the sample scripts that come with the LDAPSDK Just use the qsearch program and customize the parameters appropriately. C:\ldapsdk\perldap-1.0\examples>perl qsearch.pl h ldap.bigfoot.com -b "o=airius.com" "cn=billybob thornton" Searched for `cn=billybob thornton': dn: cn="billybob thornton",mail=punkguyz@hotmail.com,c=US,o=hotmail.com mail: punkguyz@hotmail.com cn: billybob thornton o: hotmail.com surname: thornton
(3) You can write a little script to connect. This is good for testing your install. I lifted this code from Mark Wilcox's book. Thanks Mark. SCRIPT VARIATION #1 This is the simplest test. Just search for your own email address in the bigfoot directory, and return all the information that is listed for that record. In the RDBMS world this would be kind of like a SELECT statement, i.e. SELECT * FROM directory WHERE mail='megan@gate.net' #!/usr/local/bin/perl use Mozilla::LDAP::Conn; my $host = "ldap.bigfoot.com"; my $port = 389; my $dn = ""; my $password = ""; my $base = "o=airius.com"; my $scope = "subtree"; my $filter = "(mail=punkguyz\@hotmail.com)"; my $ldap = new Mozilla::LDAP::Conn($host,$port,$dn,$password) || die("Failed to open LDAP connection\n"); my $entry = $ldap->search($base, $scope, $filter); if(! $entry) { print "Search failed. Try again.\n"; } else { while($entry) { $entry->printLDIF(); $entry= $ldap->nextEntry(); } } SCRIPT VARIATION #2 This one shows an array of attribs being returned instead of the entire attributes list being returned by default. This could be a substantial savings in a directory with large records. In the RDBMS world, this operation is kind of like a "project" in which you choose a few columns to display from a larger set of columns: SELECT cn,mail,telephonenumber FROM directory WHERE mail='megan@gate.net' The 0 means "do you want to return both values and attribute names or just the attribute names?" 0 is the default and means that you want to return both. In the RDBMS world, an attribute name is a column. And a value is the field value for that attribute. The @attribs is the list of attribs you are asking for. #!/usr/local/bin/perl use Mozilla::LDAP::Conn; my $host = "ldap.bigfoot.com"; my $port = 389; my $dn = ""; my $password = ""; my $base = "o=airius.com"; my $scope = "subtree"; my $filter = "(mail=punkguyz\@hotmail.com)"; my @attribs; push (@attribs,"cn"); push (@attribs,"mail"); push (@attribs,"telephonenumber"); my $ldap = new Mozilla::LDAP::Conn($host,$port,$dn,$password) || die("Failed to open LDAP connection\n"); >> my $entry = $ldap->search($base,$scope,$filter,0,@attribs); if(! $entry) { print "Search failed. Try again.\n"; } else { while($entry) { $entry->printLDIF(); $entry= $ldap->nextEntry(); } }
(4) You could use an LDAP url from within a perl script. Use your @attribs array to build the url, then searchURL(). #!/usr/local/bin/perl use Mozilla::LDAP::Conn; use Mozilla::LDAP::Utils; use Mozilla::LDAP::Entry; my $host = "ldap.bigfoot.com"; my $port = 389; my $dn = ""; my $password = ""; my $base = "o=airius.com"; my $filter = "(cn=BillyBob Thornton)"; my $scope = "sub"; my $url = "ldap://$host:$port/$base?"; my @attribs; push (@attribs, "cn"); push (@attribs, "mail"); push (@attribs, "sn"); my $ldap = new Mozilla::LDAP::Conn($host,$port,$dn,$password) || die("Failed to open LDAP connection.\n"); my $attribute; foreach $attribute(@attribs) { $url .= $attribute. ","; } $url .= "?".$scope; $url .= "?".$filter; print "url: $url\n"; my $entry = new Mozilla::LDAP::Entry(); if ($ldap->isURL($url)) { $entry=$ldap->searchURL($url); } else { die("$url is not a valid LDAP URL\n"); } if (! $entry) { print "Search failed. Try again."; } else { while ($entry) { $entry->printLDIF(); $entry = $ldap->nextEntry(); } }
Bigfoot returns a nicely formatted list like this: url: ldap://ldap.bigfoot.com:389/o=airius.com?cnmailsn?sub?(cn=BillyBob Thornton) dn: cn="billybob thornton",mail=punkguyz@hotmail.com,c=US,o=hotmail.com mail: punkguyz@hotmail.com cn: billybob thornton o: hotmail.com surname: thornton You could even add a portion of code that accepted a filter from the commandline and searched on that. c:\> perl test.pl "(cn=billybob thornton)" ... my $term = shift; my $filter = $term; ...
More operations on the LDAP directory 1. Bind. You must be able to bind (authenticate) as "Directory Manager" in some cases. 2. Search. We already did this. 3. Compare. We already did this. 4. Create. You create (add) a new entry using Mozilla::LDAP::Entry, and use setDN() to set up the DN of the entry. Assign. You assign attributes to your new entry in name-value pairs with $entry->addValue("cn", "BillyBob Thornton") 5. Modify. You can modify existing values using these same create/assign methods also. If an entry has two values for an attribute, when you replace the attribute, you will write over both values. 6. Delete. You can delete entries with the delete(dn) method. Example 1: replacing one of many attributes #!/usr/local/bin/perl use Mozilla::LDAP::Conn; >> my $host = "ldap.myldapserver.com"; my $port = 389; >> my $dn = "Directory Manager"; >> my $password = "secret"; >> my $base = "uid=bthornton, ou=People,o=airius.com"; >> my $scope = "base"; >> my $filter = "uid=bthornton"; my @attribs; attribs[0] = "cn"; my $goodValue = "William Robert Thornton"; my $badValue = "billybob thornton"; my $ldap = new Mozilla::LDAP::Conn($host,$port,$dn,$password) || die("Failed to open LDAP connection\n"); my $entry = $ldap->search($base, $scope, $filter,0,@attribs); if(! $entry) { print "Search failed. Try again.\n"; } else { my @cn = @{$entry->{cn}}; $entry->removeValue("cn",$badValue); $entry->addValue("cn",$goodValue); $ldap->update($entry); if ($ldap->getErrorCode()) { print $ldap->printError(); } my $newEntry = $ldap->search($base,$scope,$filter,0,@attribs); if(! $newEntry) { die("Search failed. Try again.\n"); } else { $newEntry->printLDIF(); } }
Putting It All Together 1. Building an LDAP Gateway in Perl. You can write a cgi script take data from a to search or update an LDAP gateway. You could even make it a pure client-side (non-cgi) implementation since you can use javascript to build a URL based on form input. GQ (http://biot.com/gq/) is a small GTK-based LDAP client which lets you query any LDAP server with a graphical interface. 2. Hooking LDAP to an email client. Its even easier if the client is web based, again because we can use the URL method. 3. LDAP for user authentication. 4. LDAP for holding snippets of business logic. If you put your SQL statements in LDAP, you could call them from anywhere in the world. 5. LDAP for tax calculation. This is my idea that I think would be really cool.