Although a multitude of general purpose LDAP tools are available, you may want to develop your own customized tools that reflect the way you do business. The good news is that there are many toolkits and application programming interfaces (APIs) to choose from. The bad news is that you need to decide which ones are best for you. Consider the following factors when choosing a toolkit: Providing an exhaustive list of tools and their capabilities would be a subject of another book. For example the Mark Wilcox book, Implementing LDAP , goes into detail on several of the LDAP APIs. The intent of this section is to introduce you to some of the most common toolkits, and to provide simple examples of how to implement them. The toolkits discussed are: The APIs discussed are: -
LDAP C Software Development Kit (SDK) -
Directory SDK for Java software -
Java Naming and Directory Interface (J.N.D.I.) API PerLDAP PerLDAP is an interface to the Sun ONE LDAP C SDK. Two types of interfaces are provided: The object-oriented interface provides a set of Perl modules that perform common LDAP operations. The direct interface uses the C library calls directly. The object-oriented interface is easier to use and will do most, if not all, of what you want it to do. The examples provided here are based on the object-oriented Perl modules. Why Use PerLDAP? Perl is the programming language of choice for many system administrators. While you could create Perl scripts that call the standard LDAP commands like, ldapsearch and ldapmodify , you would not have the same level of control. One example where you would use PerLDAP instead of Perl+LDAP commands, is to create a CGI gateway. This is useful if you want to build tools that can be accessed through web pages and you are already familiar with creating CGI scripts with Perl. There are some limitations of PerLDAP that should be noted. These include: LDAP Perl Modules These include: -
Mozilla::LDAP::Conn The main interface to LDAP functions. It includes creating a connection, searching for and modifying data. -
Mozilla::LDAP::Entry Class is used to create new entries, get and set attributes, and other functions pertaining to directory entries. -
Mozilla::LDAP::LDIF Provides routines for manipulating LDIF. -
Mozilla::LDAP::Utils Provides general purpose LDAP routines. -
Mozilla::LDAP::API Provides wrappers around C functions in the LDAP C SDK. To Set Up PerLDAP -
Obtain the following: -
PerLDAP modules These are located in: /usr/ds/v5.2/nsPerl5.006_01/lib/site/Mozilla/LDAP -
LDAP C SDK this can be downloaded from: http://www.sun.com/software/download/developer/ -
Copy the LDAP Perl modules to a location specified in your @INC search path . # perl -e 'print "@INC"' /usr/perl5/5.6.1/lib/sun4-solaris-64int /usr/perl5/5.6.1/lib /usr/perl5/site_perl/5.6.1/sun4-solaris-64int /usr/perl5/site_perl/5.6.1 /usr/perl5/site_perl /usr/perl5/vendor_perl/5.6.1/sun4-solaris-64int /usr/perl5/vendor_perl/5.6.1 /usr/perl5/vendor_perl. # cd /usr/ds/v5.2/nsPerl5.006_01/lib/site # cp -r Mozilla /usr/site_perl5/5.6.1 Note that the example shown here assumes you are running Solaris 9 OE 12/02, which includes Perl 5.6.1. Other OE releases may have different Perl versions, so the path names would be different. -
Install the LDAP C SDK as shown . # gunzip ldapcsdk5.10-SunOS5.8_OPT.OBJ.tar.gz # tar xf ldapcsdk5.10-SunOS5.8_OPT.OBJ.tar # pwd /ldapcsdk # ls README lib docs redist.txt etc release.gif examples relnotes_41.htm include relnotes_5x.htm ldapcsdk5.06-SunOS5.8_OPT.OBJ.tar tools -
Set the LD_Library_PATH variable: # LD_LIBRARY_PATH=/ldapcsdk/lib # export LD_LIBRARY_PATH -
Perform a simple user search . This example Perl script does a search for entries that match the uid entered on the command line. # cat getentry.pl #!/usr/bin/perl use Mozilla::LDAP::Conn; my $host = "localhost"; my $port = 389; my $dn = ""; my $passwd = ""; my $base = "ou=people,dc=example,dc=com"; my $filter = "uid=$ARGV[0]"; my $scope = "subtree"; # my $conn = new Mozilla::LDAP::Conn($host,$port,$dn,$passwd); die "Could't connect to LDAP server $host" unless $conn; my $entry = $conn->search($base, $scope, $filter); while($entry) { $entry->printLDIF(); $entry = $conn->nextEntry; } print "\n"; $conn->close if $conn; To Build an LDAP Gateway Using PerLDAP As described in the next section, the JSP Directory Gateway is available for creating web-based interfaces for updating data in the directory. However, you may feel more comfortable writing a CGI interface with Perl. In this section, creating a bare bones CGI program for creating entries in a directory is described. The assumption is that you are familiar with Perl and the CGI interface, so the emphasis is on how to use PerLDAP to create your interface. The following assumptions are made: -
The PerLDAP Perl Modules are installed. -
The CGI.pm module is installed. -
The LDAP C SDK is installed and the libldap50.so library is located where the PerLDAP modules can find it. -
The Solaris OE bundled Apache server is used. -
Enable the Apache Web Server: -
Create a /etc/apache/http.conf file . An easy way to create this file is to make a copy of the httpd.conf-example file, calling the new file /etc/apache/http.conf . -
Review the http.conf file, and make changes as needed . The defaults work in most cases. -
Start the httpd process . # /usr/apache/bin/httpd& -
Create a PerLDAP script: -
Using your favorite editor, create the following adduser.pl script . #!/usr/bin/perl use strict; use CGI; # CGI library use CGI::Carp qw(fatalsToBrowser); #use URI::URL; use Mozilla::LDAP::Conn; use Mozilla::LDAP::Utils; # Name of LDAP server, port, container for user accounts, # and default user password my $ldap_host = "localhost"; my $ldap_port = 389; my $ldap_base = "ou=people,dc=example,dc=com"; my $upasswd = "secret"; # Credentials used to create new user account entries my $dn = "cn=directory manager"; my $pwd = "netscape"; my $query = new CGI; # Print headers for HTML returned print $query->header; print $query->start_html(-title=>'New User'); # Retrieve parameters from form my $loginid = $query->param('loginid'); my $uidnumber = $query->param('uidnumber'); my $gidnumber = $query->param('gidnumber'); my $homedir = $query->param('homedir'); my $cn = $query->param('cn'); my $shell = $query->param('shell'); my $ldap = new Mozilla::LDAP::Conn($ldap_host,$ldap_port,$dn,$pwd) die("Failed to open LDAP connection.\n"); my $entry = $ldap->newEntry(); $entry->setDN("uid=$loginid,$ldap_base"); $entry->{objectclass} = ["top", "account", "posixaccount", "shadowaccount" ]; $entry->{uid} = [ "$loginid" ]; $entry->{uidnumber} = [ "$uidnumber" ]; $entry->{gidnumber} = [ "$gidnumber" ]; $entry->{homedirectory} = [ "$homedir" ]; $entry->{cn} = [ "$cn" ]; $entry->{loginshell} = [ "$shell" ]; $entry->{userpassword} = [ "$upasswd" ]; if (! $ldap->add($entry)) { die ("Can't create account for $loginid"); } # Check to see if the entry was created and print it my $filter = "uid=$loginid"; my $entry = $ldap->search($ldap_base, "subtree",$filter); if (! $entry) { print "Can't find user entry"; } else { print $query->h1("The following user account entry was created..."); print "Account created for $entry->{cn}[0]"; print "<PRE>"; $entry->printLDIF(); } print "</PRE>"; print $query->end_html(); -
Place the script in /var/apache/cgi-bin . -
To Create HTML for an Input Form -
Create some HTML that represents your input form . <html> <body> Add New User Form <br><form method=POST action="/cgi-bin/adduser.pl"> <br> Enter loginID:<br> <input name="loginid" size=20><br> Enter Full Name:<br> <input name="cn" size=40><br> Enter uidNumber:<br> <input name="uidnumber" size=10><br> Enter gidNumber:<br> <input name="gidnumber" size=10><br> Enter Home Directory:<br> <input name="homedir" size=20><br> Enter Login shell:<br> <input name="shell" size=10><br> <input type=submit VALUE="Submit"> <input type=reset VALUE="Clear Form"><p> </form> </body> </html> -
Place the HTML in /var/apache/htdocs . -
Test the script: -
In a browser, go to http:// myserver/myform.html . -
Fill in the form and click submit . Figure 6-12. Add User Form -
Observe the output . Figure 6-13. Example of Output Using the JDGW The JSP Directory Gateway (JDGW) is a sample phone book application that ships as part of the Sun ONE Directory Server Resource Kit 5.1 software. This application is based on JavaServer Pages (JSP) technology and can be customized to fit your environment. This section explains what the JDGW does, how it works, and how to customize it. Note The instructions provided in this chapter are based on the Sun ONE Directory Server Resource Kit version 5.1. Please check the instructions for the version of the resource kit that you are using. What Does It Do? The JDGW application creates HTML that can be displayed in any web browser. The HTML presents a form that can be used to initiate a directory search and forms to edit directory entries. While the sample application can be useful as is stands, you will probably want to customize it to include data fields particular to your organization. How Does It Work? The major components are shown in FIGURE 6-14: Figure 6-14. Major JDGW Components The web server used is the Tomcat server which, provides a JSP framework. This includes tags that are XML-like structures containing logic to create HTML pages. The Sun ONE Directory Server Resource Kit contains tag libraries that are used to access LDAP directories. Setting It Up The following components are required: -
Sun ONE Directory Server Resource Kit -
Tomcat v4.0 or later -
Java 2 Platform v1.3.1 software -
Sun ONE Directory Server, configured with user entries To Install the Software -
Download the JDK from java.sun.com/2je/1.3/ Install the JDK software in a directory such as /files/JDK . -
Download the Tomcat server from http://jakarta.apache.org/site/binindex.html -
Unzip and untar the jakarta-tomcat-4.1.12.tar.gz file . Use a directory such as /files/TOMCAT . -
Set the CATALINA_HOME and JAVA_HOME shell variables . # export CATALINA_HOME=/files/TOMCAT/jakarta-tomcat-4.1.12 # export JAVA_HOME=/files/JDK -
Run the startup.sh command . # $CATALINA_HOME/bin/startup.sh -
Edit the phone book properties sheet . Add information that is specific to your environment. # cd /opt/iPlanet/jdgw/phonebook-app # ls WEB-INF build.xml jsp ldif lib properties # cd /opt/iPlanet/jdgw/phonebook-app/WEB-INF/classes # more lookmeup.properties hostname=myserver.example.com port=389 base=ou=people,dc=example,dc=com country=US language=en -
Edit the server.xml file . Add the lines shown in the example. # cd /files/TOMCAT/jakarta-tomcat-4.1.12/conf # vi server.xml <Context path="/jdgw" docBase="/opt/iPlanet/jdgw/phonebook-app" debug="0" reloadable="true"> <Logger className="org.apache.catalina.logger.FileLogger" prefix="jdgw_log." suffix=".txt" timestamp="true"/> To Customize LookMeUp FIGURE 6-15 shows how the LookMeUp application is structured. Figure 6-15. LookMeUp Application Structure The easiest customization is to add additional attributes. In the example, posixAccount attributes are added under the Additional Information section header. Modifications are made to the following files: -
screen.properties -
person.jsp -
person.addinfo.jsp In this example, the application is expanded to retrieve the homedirectory , uidnumber , and gidnumber attributes from the entries searched. -
Edit the screen.properties file . This should be done while the application is not running because the changes take effect the next time the application is accessed. This file is referenced when the HTML of the search return screen is rendered. It defines the label written for the field corresponding to the attribute that is retrieved. screen.properties example: # cd /opt/iPlanet/jdgw/phonebook-app/WEB-INF/classes # vi screen.properties . . . person-head.title=Person Entry -- person-addinfo.title=Additional Information person-addinfo.description=Description: person-addinfo.seealso=See Also: person-addinfo.homedirectory=Home Directory: person-addinfo.uidnumber=UID Number: person-addinfo.gidnumber=GID Number: person-addinfo.url=URL: . . . -
Edit the person.jsp file . This file defines the attributes that are retrieved. person.jsp example: # cd /opt/iPlanet/jdgw/phonebook-app/jsp/lookmeup # vi person.jsp ... <ldap:search host="$app:hostname" srchScope="base" base="$request:dn" var="results" port="$app:port" filter="(objectclass=*)" attrs= "cnsngivennametelephonenumbermailpostofficeboxuidfacsimil etelephonenumberpagerhomephonemobilepostaladdressroomnumber physicaldeliveryofficenamebusinesscategorytitleoumanagerde partmentnumbercarlicensedescriptionseealsolabeledurihomedir ectoryuidnumbergidnumberemployeenumbermodifytimestampmodifi ersname" response="srchresp"/> ... -
Edit the person-addinfo.jsp file . This file creates the HTML table where the retrieved attribute values are placed. person-addinfo.jsp example: [View full width] [View full width] # cd /opt/iPlanet/jdgw/phonebook-app/jsp/lookmeup # vi person-addinfo.jsp ... <TR> <TD VALIGN="TOP"><B><util:prop prps="screen" key="personaddinfo.seealso" lang="$app :language" cntry= "$app:country"/></B></TD> <TD VALIGN="TOP" NOWRAP> <ldap:attr default=" " entry="$entry" name="seealso"> </TR> <TR> <TD VALIGN="TOP"><B><util:prop prps="screen" key=" personaddinfo.homedirectory" lang="$app :language"cntry= "$app:country"/></B></TD> <TD VALIGN="TOP" NOWRAP> <ldap:attr default=" " entry="$entry" name="homedirectory" > </TD></TR> <TR> <TD VALIGN="TOP"><B><util:prop prps="screen" key=" personaddinfo.uidnumber" lang="$app :language" cntry= "$app:country"/></B></TD> <TD VALIGN="TOP" NOWRAP> <ldap:attr default=" " entry="$entry" name="uidnumber" > </TR> <TR> <TD VALIGN="TOP"><B><util:prop prps="screen" key=" personaddinfo.gidnumber" lang="$app :language" cntry= "$app:country"/></B></TD> <TD VALIGN="TOP" NOWRAP> <ldap:attr default=" " entry="$entry" name= "gidnumber"/> </TR> . . . -
Stop and restart the Tomcat server . This is required so that the JSPs will be read. # cd /files/TOMCAT/jakarta-tomcat-4.1.12/bin # ./shutdown.sh Using CATALINA_BASE: /files/TOMCAT/jakarta-tomcat-4.1.12 Using CATALINA_HOME: /files/TOMCAT/jakarta-tomcat-4.1.12 Using CATALINA_TMPDIR: /files/TOMCAT/jakarta-tomcat-4.1.12/temp Using JAVA_HOME: /files/JDK/j2sdk1_3_1_06 # ./startup.sh Using CATALINA_BASE: /files/TOMCAT/jakarta-tomcat-4.1.12 Using CATALINA_HOME: /files/TOMCAT/jakarta-tomcat-4.1.12 Using CATALINA_TMPDIR: /files/TOMCAT/jakarta-tomcat-4.1.12/temp Using JAVA_HOME: /files/JDK/j2sdk1_3_1_06 # Note You need to set the CATALINA_BASE and JAVA_HOME shell variables before running the shutdown.sh and startup.sh scripts. -
Check to see if you can retrieve data . Bring up the user information. You should see additional fields that were added. FIGURE 6-16 shows an example. Figure 6-16. New User Fields LDAP APIs In addition to the scripting languages and toolkits mentioned earlier in this chapter, the C and Java programming languages can be used to develop directory enabled applications. The Application Programming Interfaces (APIs) that support these languages include: -
C LDAP API -
J.N.D.I. API -
Java API The C LDAP Software Development Kit (SDK) provides examples, documentation, header files, and pre-compiled libraries. The C LDAP SDK is available for different platforms including Windows, HP-UX, and AIX. You will have to download a version that matches your target platform. Updates to the SDK are continually made available to fix bugs and to provide access to LDAPv3 extensions. If binary portability between platforms is not your top priority, the C LDAP SDK is a good choice for creating directory enabled applications. The Java Naming and Directory Interface (J.N.D.I.) API is a Java extension that provides Java applications with a unified interface to multiple naming and directory services. J.N.D.I. API Service Providers are available for NIS, LDAP, DNS, and CORBA. By writing to the J.N.D.I. API, the programmer does not need to know the underlying interfaces for those services. The Java API differs from the J.N.D.I. API in that the interface performs LDAP operations directly. This provides greater control and allows the programmer to take advantage of LDAP features not accessible through the J.N.D.I. API. The following section provides details on how to create a program using the Java 2 SDK. Creating a Program With the LDAP SDK for Java Another way to develop your own administrative tools is to write them in Java programming language. In this section, a sample program is examined as a guide to creating Java programs that access LDAP directories. It is not meant to be a programming guide. There are many good books available on that topic. To write a Java-based tool, you need: To perform an LDAP operation on a server, you need to perform these tasks : -
Create a new LDAPConnection object. -
Use the connect method to connect to the LDAP server. -
Perform the LDAP operation. -
Use the disconnect method to disconnect from the server when done. To illustrate how a Java program can be written, the LDAPsubtdel program, available in the Sun ONE Directory Server Resource Kit 5.2 software, is used. The LDAPsubtdel Program The LDAPsubtdel program is written in Java and uses classes contained in LDAPJDK 4.1. The tool deletes a specified directory subtree including referrals. The source files for LDAPsubtdel are available from the Directory Server Resource Kit version 5.2 software. This program is also available for download. See "Obtaining the Downloadable Files for This Book" on page xxvii. There are two source files: -
LDAPBase.java -
LDAPSubtdel.java The LDAPBase.java file gathers parameters entered on the command line and performs some error checking. The LDAP- related operations are performed in LDAPSubtdel.java . The header for the file looks like this: import java.util.*; import netscape.ldap.*; import netscape.ldap.util.*; These directives cause the following packages to be imported: -
netscape.ldap -
netscape.ldap.beans -
netscape.ldap.ber.stream -
netscape.ldap.controls -
netscape.ldap.factory -
netscape.ldap.util The code that creates an LDAP connection structure and bind to the directory looks like this: /* perform an LDAP client connection operation */ try { client = new LDAPConnection(); client.connect( ldaphost, ldapport ); } catch(Exception e) { System.err.println("Error: client connection \ failed!"); System.exit(0); } /* perform an LDAP bind operation */ try { client.authenticate( version, binddn, ldappasswd ); } catch (Exception e) { System.err.println( e.toString() ); System.exit(0); } In the following code sample, the connect and authenticate methods associated with the LDAPConnection object are used to bind to the directory server. LDAPSearchResults res = client.search( dn, client.SCOPE_ONE, "((objectclass=*)(objectclass=ldapSubEntry))", new String[] {LDAPv3.NO_ATTRS}, false, cons ); In this code sample, a directory search is performed by the search method. The object class ldapSubEntry , is included in the search filter so all entries including those with CoS and roles definitions will be returned. The entries found are stored in the LDAPSearchResults object. // Recurse on entries under this entry while ( res.hasMoreElements() ) { try { // Next directory entry LDAPEntry entry = res.next(); theDN = entry.getDN(); // Recurse down if (! delete( theDN, client, doSubtreeDelete )) allChildrenDeleted = false; } catch ( LDAPReferralException e ) { // Do not follow referrals, just list them System.out.println( "Search reference: " ); LDAPUrl refUrls[] = e.getURLs(); for ( int i = 0; i < refUrls.length; i++ ) { System.out.println( " " + refUrls[i].getUrl() ); } allChildrenDeleted = false; continue; } catch ( LDAPException e ) { System.out.println( e.toString() ); continue; } } The LDAPentry object is used to hold the entries as they are examined. The actual deletion looks like the following: // At this point, the DN represents a leaf node, // so stop recursing and delete the node try { if ( doSubtreeDelete ) { if ( allChildrenDeleted ) { if ( !dn.equals(basedn) removebasedn ) { if ( verbose && doSubtreeDelete ) System.err.println("Deleting entry "+dn+"\n"); client.delete(dn, delcons); } The delete method is called to remove the entry specified by the DN. LDAP constraints are specified in delcons as shown below. LDAPSearchConstraints cons = client.getSearchConstraints(); LDAPConstraints delcons = client.getConstraints(); |