Chapter 6: LDAP APIs

 < Day Day Up > 



Until now, we have seen many LDAP examples using command-line tools, which are not a very user-friendly way of communicating with the LDAP server. In this chapter, we will first revisit the command-line tools to see whether they can be used in a more ergonomic way. Then we will see some examples of languages offering LDAP support, some of them just native support and some of them extensions in the form of libraries.

For the remainder of the chapter, we turn our attention to the most frequently used programming languages. The discussion focuses on languages available over a broad spectrum of platforms; languages available on only a single platform are not mentioned. As you will see, however, the manner of using an LDAP API is always the same, regardless of the programming language. Thus, if you have seen some examples in the C language, you will have no difficulty in recycling your knowledge when you are using Java instead.

LDAP Command-Line Tools

The command-line tools seem to be the most spartan API available to access an LDAP directory. They have been part of the standard distribution ever since the very first LDAP implementation written at the University of Michigan. Nevertheless, the command-line tools are available in nearly all distributions, and they all work in the same way. Furthermore, there are situations where these simple tools can come in handy. For example, an administrator might need to quickly control a certain entry in the directory or change an attribute of an entry. In such cases, an "on-the-fly" command-line filter can be a powerful tool. The command-line tools enable such quick tests without having to write an application (for example in Perl) to do the job.

The command-line tools can be helpful in other situations too. Because they behave like all of the other commands available in your shell, they can be used to write shell scripts. This is an interesting option in an existing system-administrating environment where the system administrator already has a library of scripts and wishes to integrate the LDAP administration jobs also. In this chapter, we will see some examples of using the command-line tools in collaboration with other tools available in the standard shell. The shell and their commands are available as standards in the UNIX environment only. However, they have also been ported to other systems that lack similar tools, for example, the Win32 environment.

The following sections show the command-line tools. The examples are based on the OpenLDAP implementation on UNIX. It is quite probable that your implementation will use a slightly different syntax, but the underlying logic will be the same. Have a look at the documentation shipped with your implementation or the online documentation (manual pages in UNIX, "help" in the MS World).

Selected Commands

On the OpenLDAP implementation, you will find the following tools in the script directory:

  • ldapadd: Add an entry

  • ldapdelete: Delete an entry

  • ldapmodrdn: Modify the relative distinguished name (RDN) of an entry

  • ldapsearch: Search for an entry

  • ldapcompare: Verify the presence of an entry

  • ldapmodify: Modify an entry

Depending on your specific implementation, you may see more tools or less. The most important tools, however, are available in all implementations:

 ldapsearch and ldapmodify 

With these two tools, you can do everything the other tools can do. The functions "ldapadd," "ldapdelete," and "ldapmodrdn" can be seen as convenience functions because ldapmodify can do exactly the same things, as we will see in the next paragraph.

ldapmodify

You can use ldapmodify in two ways:

    1. Type in all data interactively

    1. Use a file in LDIF format for the data input

The syntax is exactly the same, so the choice is a matter of personal preference. I recommend the second method. It helps you to avoid typos, and you can keep track of what you did and can undo the actions very easily in case of errors. A further reason why you might prefer the second method is that it may be possible for you to generate automatically the input file, which is very handy if you insert more than one similar entry. At the end of this paragraph, you will see a sample shell script that generates the LDIF file automatically. But first let have us a look at the syntax. Exhibit 1 shows the ldapmodify call with the most important parameters. It is possible that your implementation offers more parameters or switches. The OpenLDAP implementation does. The following is a brief description of the most frequently available parameters. For a complete description, consult your documentation.

start figure

 ldapmodify [-a] -h <host> -p <port>   -D "<bindDN>"   -w "<password>"   -w   -f <InputFileName> 

end figure

Exhibit 1: Syntax of ldapmodify with Frequently Used Parameters

  • host: Hostname the directory server is running on

  • port: Port number the directory server is listening

  • bindDN: Distinguished name you will be authenticated as

  • password: Corresponding password

  • InputFileName: Name of the LDIF file from which the command gets its input

The "-a" switch is a shortcut to indicate that you want to add entries. Using this, you can omit the "changetype: add line" command. On most implementations, there is an "ldapadd" function as a synonym for "ldapmodify -a."

If you omit "-f <InputFileName>," ldapmodify will wait for you to enter line by line the LDIF instructions.

The "-W" switch indicates that you do not wish to type the password as clear text. You simply omit the -w "password" and ldapmodify will prompt you for the password, protecting it with "*" characters when you are typing it in.

Some implementations also have an "-n" switch, analogous to the "make" utility, which shows what it would do but does not actually execute the command.

Some Examples of ldapmodify

Let us now see a few examples of the use of ldapmodify. Exhibit 2 shows how to add an entry to the directory. Exhibit 3 shows how to delete an entry from the directory. Examining Exhibits 2 and 3, you see that the command lines are identical (the file names are different, of course). Thus, if you still have the file from the first example, you can recycle it as a handcrafted undo mechanism, guaranteeing the DNs are identical. The syntax of the LDIF files was described in Chapter 4 when we spoke about LDIF. See Chapter 4 for more details.

start figure

 ldapmodify -D "cn=admin,o=LdapAbc.org" -w password1 -f add1.ldif the file addl.ldif contains: dn: uid=RHaller,ou=IT,o=LdapAbc.org changetype: add objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson sn: Haller cn: Rudy Haller givenName: Rudy uid: RHaller mail: RHaller@LdapAbc.org 

end figure

Exhibit 2: Example of ldapmodify— Add an Entry

start figure

 ldapmodify -D "cn=admin,o=LdapAbc.org" -w password1 -f delete1.ldif the file delete1.ldif contains: dn: uid=RHaller,ou=IT,o=LdapAbc.org changetype: delete 

end figure

Exhibit 3: Example of ldapmodify— Delete an Entry

start figure

 01 #!/bin/sh 02 ######################################################### 03 # 04 # Name:         GenerateLDIF.sh 05 # Version:      1.0 06 # Date:         10.08.2002 07 # Author:       Reinhard E. Voglmaier 08 # Description:  Generates LDIF File from CSV File 09 # 10 ######################################################### 11 12 InputFile=EmployeesMarketing.csv 13 cat $InputFile | awk ' BEGIN { 14    # Input File Separator: 15      FS = "," ; 16    # Parameters: 17      Organization="LdapAbc.org"; 18      OC[1] = "top" ; 19      OC[2] = "person" ; 20      OC[3] = "organizationalPerson" ; 21      OC[4] = "inetOrgPerson" ; 22    } 23 24    { 25    # From input: 26      GivenName = $1 ; 27      sn                     = $2 ; 28      Department  = $3; 29 30    # Construct Composite Variable 31      uid      = sprintf("%s%s", subbstr(GivenName,1,1),sn); 32 33    # Output Section: 34      printf("dn: uid=%s, ou=%s, o=%s\n", uid,Depart- ment,Organization); 35 36      for (i=1;i<5;i++) 37         printf("objectClass: %s\n",OC[i]); 38      printf("sn: %s\n",sn); 39      printf("cn: %s %s\n",GivenName,sn); 40      printf("givenName: %s\n",GivenName); 41      printf("uid: %s\n",uid); 42      printf("mail:%s@%s\n\n", uid,Organization); 43    } ' 

end figure

Exhibit 4: Example of Script Preparing the Input File for ldapmodify

To conclude the discussion about the usage of ldapmodify, we will examine a little shell script that generates a file in the LDIF format suitable for the ldapmodify command (Exhibit 4). The script gets its input from a csv (comma-separated values) file, such as the format exported from Excel files.

The script reads the file "InputFile." The heart of the script is the AWK utility. AWK got its name from its creators Aho, Weinberger, and Kernighan. It reads the input line by line and splits the lines up in records. The single records are available inside the script using the built-in variables $1, $2, ..., $n. The default record separator is one or more space characters, although you can use other characters as well. For example, we set the record separator to comma (FS = ",") in line 15 of the script. The BEGIN section is used to set up the parameters for this script.

In this example we have an input file that consists only of three words per line: surname, givenName, and Department. AWK reads the input line by line and splits every line into records. The records are denominated $1, $2, and so on. In lines 26 to 28 are assigned more human-friendly variable names. You can easily adapt this script for more variables. After these lines, all data that has been read in from the csv file can be printed out in the desired form in the lines 34 to 42.

AWK is available on all UNIX systems (Win32 versions are also available from Cygnus). AWK is easy to read and understand, and because it is a scripting language, you do not have to compile it. It is very handy for little scripts such as this one. In the next section, we will expand it a little to show you how to work with ldapmodify and ldapsearch using such tools as AWK and the stream editor (sed), a further handy tool available in the UNIX environment and ported to other operating systems.

ldapsearch

Like the ldapmodify function, ldapsearch is also part of every (or nearly every) LDAP server distribution. Initially it was intended as an example of how to write an LDAP client. If you are curious about its implementation, you can download the source code of the OpenLDAP distribution. If you are interested in better understanding what is going on behind the scenes, it should be instructive to look over the source code of ldapsearch and the other utilities shipped with the OpenLDAP distribution. Also instructive is the test suite used to test all the utilities of OpenLDAP after the compilation.

Now let us have a brief look at the syntax of ldapsearch. As in the previous example, we will consider only the parameters and switches that should reasonably exist on all LDAP implementations. Exhibit 5 shows the syntax plus the parameters and their explanation.

start figure

 ldapsearch -h <host> -p <port>  -D "<bindDN>"  -w "<password>"  -b "<searchbase>"  <filter> <result-attributes> 

  • host: the hostname the directory server is running on

  • port: the port number the directory server is listening on

  • bindDN: the distinguished name you will be authenticated as

  • password: the corresponding password

  • filter: query filter for the search

  • result-attributes: attributes of the result entries to be returned

end figure

Exhibit 5: Syntax of "ldapsearch" Function

Some Examples of ldapsearch

The simplest form of a search delivers the entry knowing the sn attribute:

 ldapsearch -h ldap.LdapAbc.org -p 389 -b "o=LdapAbc.org" "(sn=Parker)" 

If the search runs on the same machine as the directory server, you can leave out the "-h" attribute. We will assume that it does, and furthermore we will use for these examples an installation using the standard port (389), so we can also omit the "-p 389" parameter.

If we wished to have only the sn, cn, uid, and mail attribute to be returned from the search, we would say:

 ldapsearch "o=LdapAbc.org" "(sn=Parker)" sn,cn,uid,mail 

As shown in Chapter 2, when we made our very first experiments with the LDAP command-line tools, we can also express more-complicated queries, for example:

 ldapsearch "o=LdapAbc.org" objectclass=inetOrgPerson  (&(&(sn=Parker)(givenName=J*))(mail=*)) 

This query would deliver all entries with object class "inetOrgPerson" that has sn Parker AND the given name beginning with J (e.g., James, Joseph) AND that has the mail attribute (used here as a so-called presence filter). To learn more about the search function, see the discussion in Chapter 4 about the filter attribute.

Now you will learn how the command-line tools can be used together with the shell and the tools sed and AWK. The first script extends the previous script for ldapmodify. It not only prepares a file suitable for the insertion of new users from a csv file, but it queries the directory to see if the uids already exist. If they do, the script tries to create unique uids. The first 29 lines of both scripts are identical. Line 31 in the original script, which builds up the uid from sn and givenName, is replaced by the following line:

 uid = GenUid(givenName,sn); 

"GenUid" is a user-defined function that searches the database to see if the distinguished name exists. If it does, it recreates the uid in a different manner. In the first trial, it takes the first letter of the given name and attaches it to the surname. If this uid also exists in the directory, the function starts a second trial, taking the first two letters of the given name and attaching these to the surname. Again it tests to see if the distinguished name exists, and so on. The function also maintains a hash, where it stores the userIDs assigned during the run of the script, since the data will he inserted only in a second run. Exhibit 6 shows the additional functions.

start figure

 01 function GenUid (givenName,sn) { 02 03 # Construct Composite Variable 04   Status = 0 ; 05   L      = 1 ; 06 07 # Lookup in the database if the name exists 08  while (Status == 0) { 09    uid        = sprintf("%s%s", 10    substr(givenName,1,L++),sn) ; 11    Base = sprintf("  \"uid=%s, ou=%s, o=%s\" ", 12                        uid,Department,Organization); 13   Cmd = sprintf("ldapsearch -b %s | grep %s > /dev/null", 14                  Base,sn); 15    Status = system(Cmd); 16    } 17 18   # Lookup in the array of assigned uids 19   if (UidList[uid]) { 20        Status = 0; 21    } else { 22      UidList[uid] = "1"; 23    } 24 25    while (Status == 0) { 26      uid       = sprintf("%s%s", 27      substr(GivenName,1,L++),sn) ; 28      if (!UidList[uid]) { 29           UidList[uid] = "1"; 30           Status = 1 ; 31      } 32    } 33    return(uid); 34 } 

end figure

Exhibit 6: Example Shell that Looks Up in the Directory and Generates Unique Distinguished Names

Line 11 constructs the search base as:

 <SearchBase> = "uid=<UserId>, ou=<Department>,o=<Organization>" 

Line 13 constructs the search as:

 Ldapsearch -b <base> | grep sn 

For example, for James Parker in the IT department of o = LdapAbc.org, the search would read:

 Ldapsearch -b "uid=JParker, ou=IT, o=LdapAbc.org | grep Parker 

The system call reports the exit status of the command, bringing the grep command into play. The exit status of the search command always reports no error, whether the item was found or not, because it is not the command that failed. The grep command, on the other hand, exits with status OK if the sn is contained in the results set, or it exits with status ERROR if sn is not contained in the results set.

In the following example, we want to list the members of the various organizational units: IT department, the marketing division, etc. We do not want to look up every entry, since this could be time consuming. What we can do instead is to create a special object for every organizational unit, the object "groupOfUniqueNames." For the IT department, for example, this would be:

 dn: cn=IT, o=LdapAbc.org objectClass: top objectClass: groupOfUniqueNames cn: IT ou: IT uniquemember: uid=JParker, ou=IT, o=LdapAbc.org ... 

The script has to perform the following steps:

  • Search for all organizational units and generate a list (in a temporary file)

  • Generate for each organizational unit:

    • The groupOfUniqueNames list

    • All "uniquemember:" entries

You might expect a complicated program with a lot of "if," "else," and "while" constructs. Instead, look at how simple the shell script is. The following discussion breaks the script down into its logical units.

Exhibit 7 shows the first part of the script, the search functionality. The ldapsearch command lists all organizational units. The "grep dn" prints only the lines of the ldapsearch output we are interested in, and the expression

start figure

 01 #! /bin/sh 02 03 DepartmentList="DepartmentList" 04 05 ldapsearch -b "o=LdapAbc.org" objectclass=organizationalUnit dn | \ 06                        grep dn:  | sed 's/dn: //' > DepartmentList 07 

end figure

Exhibit 7: Group Generation Script— Part One, Searching

 sed 's/dn://' 

substitutes "dn:" with an empty string. Thus, we get a temporary file containing only the distinguished names of the various organizational units.

Exhibit 8 shows how we proceed further with the temporary file. In the next step, we generate the LDIF file. The record separator is the "," (ou = Marketing, o = LdapAbc.org). Line 18 calls the function that prints out the header of the groupOfUniqueNames list, and line 19 calls the function that prints out all of the group members.

start figure

 08 cat DepartmentList |                   \ 09   awk ' BEGIN  { # Input Record Separator 10                    FS="," ; 11                    OutputFile="OUT"; 12                  } 13                  { 14                    #Gather information from the two fields: 15                    OU = $1 ; 16                    Base = $2 ; 17                    PrintGroup(OU,Base); 18                    PrintUserList(OU,Base); 19                  } 

end figure

Exhibit 8: Group Generation Script— Part Two, Creating Groups

The function PrintGroup in Exhibit 9 simply prints out the required fields. Line 22 strips the "ou =" prefix from the organizationalUnit attribute.

start figure

 21 function PrintGroup(Dep,Base) { 22   sub("ou= ","",Dep) ; 23   printf("dn: cn=%s, %s\n",Dep,Base) >> Out ; 24   printf("objectClass: top\n") >> Out ; 25   printf("objectClass: groupofUniqueNames\n") >> Out ; 26   printf("cn: %s\n",Dep) >> Out ; 27   printf("ou: %s\n",Dep) >> Out ; 28 } 29 

end figure

Exhibit 9: Group Generation Script— Part Two, PrintGroup Function

The last function listed in Exhibit 10 is more interesting because it executes a query in the directory and prints the output into the temp file. The ldapsearch command is constructed in line 33. It searches using the base organizational unit and the filter "objectclass = inetOrgPerson." It further specifies to return only the distinguished name (split into cmd2 and cmd to keep the length of the line shorter). It applies two filters on the output produced by the ldapsearch call. The first one filters out only the lines containing the distinguished name (grep dn:). And the second one substitutes the "dn:" with "uniquemember:" that is needed for use later on in the object class "groupOfUniqueNames."

start figure

 30 function PrintUserList (OU,Base) { 31   Post1  = | grep dn: " ; 32   Post2  = " | sed 's/dn:/uniquemember:/i' " ; 33   Cmd2   = "objectclass=inetOrgPerson dn "; 33   Cmd    = sprintf("ldapsearch -b \"%s, %s\" %s %s>> %s", 34                        OU,Base,Cmd2,Post1,Post2,Out); 35   system(Cmd); 36   printf("\n") >> Out ; 37   }' 

end figure

Exhibit 10: Group Generation Script— Part Two, PrintUserList Function

All we need to do now is to execute ldapmodify:

 ldapmodify -D "cn=admin, o=LdapAbc.org" -w password1 -f Out 

At the end, we could clean up the temporary files.

Command-Line Tools: Conclusion

We have seen only two command-line tools in this section, but these two alone are enough to manipulate the directory and extract whatever information you need. To use the command-line tools effectively, you need to understand the LDIF format and know how to construct search filters. Both concepts were covered in Chapter 4. If you download a C software development kit (SDK) and the OpenLDAP distribution, you can use the source code written in C to build command-line tools that exactly fit your needs.

We have also seen examples of shell scripts demonstrating how to integrate the command-line tools. These scripts only hint at what is possible. For example, they do not distinguish between lowercase and uppercase text, which can be an issue. Nor are they very robust in the event of failures of a single commands.

In the next few sections, we will learn how to access the directory in more sophisticated ways.

LDAP and Programming Language Support

The command-line tools may come in handy if you just want to type an on-the-fly test or an update. They are also useful additions to the system administrator's toolbox. Still, if you typically execute complicated scripts or even a whole application, you will rarely use them. Instead, you have a large assortment of programming languages offering support for LDAP.

The scope of this book prevents a description of all of the available languages. Instead, we focus on a handful of languages — C, Java, Perl, and PHP — that are available on more than one platform and are popular in the networking environment. The classic C language was the first API and has an RFC of its own. The ubiquitous Java language offers such interesting possibilities as the use of applets to run in a browser, servlets to be executed on the server side, and the handy JavaBeans that allow the user to combine bits of prefabricated code without knowing anything about its inner workings. Perl and PHP, sometimes called scripting languages, are both used frequently in Internet/intranet environments, a field where LDAP unleashes its full strength.

Exhibit 11 is a pseudocode representation of a typical LDAP program flow. As stated previously, the schema underlying all of the APIs is always the same; it derives directly from the functional model as described in Chapter 3. The parameters are omitted in this pseudocode representation. Before you can actually bind to the LDAP server, you have to establish a connection to the LDAP server. In the connect-request you specify the server name and the port the server is listening on. If the port number is omitted, the standard port number is assumed. If you do not specify the host name, a connection to "localhost" is requested. Once you have established the connection, you authenticate yourself to gain the correct access rights. If you omit the userID and password, an anonymous logon is arranged, giving you the access right of an anonymous user, as defined in the server configuration files. Indeed, the server configuration may not allow anonymous access at all. If you are authenticated with userID and password, the server looks up the access rights defined for this particular UserID.

start figure

 // establish connection to the LDAP server connect(Server,Port); // logon anonymous or with user/password bind(); // do some work, search, update, delete .. while (some-condition) {   do some stuff    } // logoff and close the connection unbind(); 

end figure

Exhibit 11: Typical LDAP Program Flow

Exhibit 12 shows a typical query program flow in pseudocode. It reflects the object-oriented philosophy of LDAP, which most programming languages follow very closely. You get a result object from the query, and this result object allows you to handle exceptions. Remember that the LDAP server delivers not only the result but also an error code. With this result object, you can get the number of hits your query produced. From the result object, you further can iterate along the entries matching your query. The entry object allows you to iterate inside the corresponding attributes, giving you the number of attributes together with the distinguished name. Note that the distinguished name has a particular importance and is not handled like the other attributes. The DN is the index that allows the directory server to access the entry. If an ordinary attribute is changed, the hierarchical position in the directory tree does not change. However, modifying the distinguished name could change the hierarchical position, or the requested change might even be impossible. This is why the distinguished name is handled differently. There are particular methods (or functions) available for access to the distinguished name. Once you are connected and authenticated, you can execute any commands to which you have access rights. After that, you unbind to close the connection and release the resources the directory server had allocated for you.

start figure

 QueryResultObject = query (BaseDN,Filter) ; while (Entry = QueryResultObject->getNextEntry()) {   print Entry->getDistinctName ;   while (Attribute = Entry->getNextAttribute()) {     print Attribute->getValues ;   } } 

end figure

Exhibit 12: Typical Query Program Flow

This chapter is not intended to serve as a manual for the APIs presented here. It should give you only an understanding of the philosophy underlying each API. As such, it provides a few working examples for each of the programming language treated here. You have just to try them out. As always, play around with them, extend the code, and see what happens. This is the best way to gain confidence with the programming language API.

The chapter focuses on languages that are available for a wide variety of platforms. Thus, we will see SDKs for the C language, Java, Perl, and PHP. Every API is available from more than one supplier. Most of them are available for free, and some are even open source. For security reasons, many companies and government agencies require open-source software to be installed in their systems. So my choice is to use open-source software whenever possible.

Note that the section covering LDAP and PHP is very extensive. We use PHP as a paradigm to illustrate how LDAP works together with a programming language. Because PHP is an easy-to-use language, you can concentrate fully on the new topic at hand, the LDAP API. The paradigm presented in the following section applies to all of the other programming languages.



 < Day Day Up > 



The ABCs of LDAP. How to Install, Run, and Administer LDAP Services
The ABCs of LDAP: How to Install, Run, and Administer LDAP Services
ISBN: 0849313465
EAN: 2147483647
Year: 2003
Pages: 149

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net