Example 1: A Directory-Enabled finger ServiceA finger service is a simple client/server account lookup mechanism supported by all Unix operating systems. A finger client exists for most desktop operating systems, including all flavors of Unix, Microsoft Windows, and Apple MacOS. A finger server, included with all Unix systems, can return information about local accounts to all finger clients on the network (finger servers have been developed for non-Unix systems as well). The finger clients and servers communicate using a simple text-based protocol that was originally developed by the BSD Unix developers in the 1980s. Assuming that many end users have access to a finger client and know how to use it, it's sensible to make some of the people information stored in a directory service available via finger. This section presents source code and usage examples for a directory-enabled finger server written in Perl. The directory-enabled finger service searches an LDAP directory and returns information about people. The Integration ApproachThe goal is to leverage the knowledge and client software that users already have, so a gateway is used to integrate finger with LDAP. On most Unix systems, an executable called in.fingerd is executed in response to incoming finger protocol requests . Replacing that executable with a script that understands the finger protocol but uses LDAP to retrieve information establishes compatibility with existing clients. This is an example of bringing the directory service to your users; no new client software needs to be installed for people to access the directory via the finger gateway. The LDAP finger gateway is a standalone executable; it does not execute within the confines of an existing application. Therefore, nearly any programming language that provides access to LDAP could be used. The gateway example is written in the Perl 5 language, and it uses the PerLDAP object-oriented LDAP access module available from the Mozilla Web site at http://mozilla.org/directory/perldap. This choice allows rapid prototyping, provides maximum portability for the code, and performs acceptably. The lfingerd.pl script replaces the standard in.fingerd binary program executed by Unix's inetd process, which is responsible for managing the execution of a variety of TCP/IP services. Directory UseThe lfingerd.pl gateway uses a directory service in a simple way. The LDAP service is accessed anonymously; that is, without authentication. Given a query string that is sent by the finger client, a search for user entries is performed with a filter that includes an (objectClass=person) component. Each entry returned by the LDAP search is printed in a format that is familiar to finger users. Along with contact information such as name and telephone number, the lfingerd.pl gateway retrieves status information for AOL Instant Messenger (AIM) users. This feature relies on Netscape Directory Server 6, which allows retrieval of instant messaging online/offline status over LDAP for person entries that include appropriate attributes. Listing 22.1 shows a sample entry that includes an AIM ID. Listing 22.1 An Entry That Includes an AIM IDdn: uid=bjensen,ou=People,dc=example,dc=com cn: Barbara Jensen cn: Babs Jensen sn: Jensen givenName: Barbara objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: nsAIMPresence ou: Product Development ou: People L: Cupertino uid: bjensen mail: bjensen@example.com telephoneNumber: +1 408 555 1862 facsimileTelephoneNumber: +1 408 555 1992 roomNumber: 0209 userPassword: hifalutin nsAIMID:Babs Jensen The AIM- related attributes and values are shown in bold type. nsAIMPresence is an auxiliary object class that allows these three attribute types:
In Netscape Directory Server 6, the nsAIMStatusText and nsAIMStatusGraphic attributes are virtual, operational attributes. Recall from Chapter 2, Introduction to LDAP, that operational attributes must be retrieved by name when an LDAP search operation is used to retrieve them. The nsAIMStatusText and nsAIMStatusGraphic attributes are also virtual , which means the attribute values are not stored in the directory server's database on disk but instead are computed on demand. For these two attributes, the values are computed by a Presence plug-in that is bundled with Netscape Directory Server 6. The Presence plug-in connects to AOL's AIM service to compute the correct nsAIMStatusText and nsAIMStatusGraphic values for each entry returned by an LDAP search operation. The lfingerd.pl gateway avoids reading unnecessary attributes from the user entries. The additional load placed on the directory service may be estimated directly on the basis of the expected use of the finger gateway. There are no other significant directory architecture issues to consider for this application. The End-User ExperienceListing 22.2 shows a finger command (in bold type), along with the output produced by a standard, non-LDAP-enabled Unix finger service. Listing 22.2 A Non-LDAP-Enabled Unix finger Lookup for dsmith finger dsmith@unix.example.com Login name: dsmith In real life: Daniel Smith Directory: /u/dsmith Shell: /bin/tcsh On since Jul 14 09:05:10 on console from :0 8 minutes 42 seconds Idle Time No unread mail Project: Human Resources web site Plan: Send me e-mail at dsmith@example.com Listing 22.3 shows a lookup using the LDAP directory-enabled finger service. In this case, only one entry matches the query string. Listing 22.4 shows an LDAP-enabled finger example that returns an entry with more attributes. Babs's entry contains postalAddress and labeledURI attributes, as well as an nsAIMID attribute. Babs is currently logged in to AOL Instant Messenger. Listing 22.3 An LDAP finger Lookup for smith finger smith@example.com [example.com] Example.Com LDAP Finger Service Login name: dsmith In real life: Daniel Smith E-Mail: dsmith@example.com Phone: +1 408 555 9519 Department: Human Resources Room: 0368 One entry matched 'smith' Listing 22.4 An LDAP finger Lookup for bjensen finger bjensen@example.com [example.com] Example.Com LDAP Finger Service Login name: bjensen In real life: Barbara Jensen E-Mail: bjensen@example.com Phone: +1 408 555 1862 Department: Product Development Room: 0209 AIM ID: Babs Jensen (status: ONLINE) Address: 1234 Main St. $ Mountain View, CA $ 94043 URL: http://homepages.example.com/bjensen My Home Page One entry matched 'bjensen' Listing 22.5 shows one more example. Four entries match the query string "carter"; the finger LDAP gateway returns information about all of them. Listing 22.5 An LDAP finger Lookup That Returns Multiple Entries finger carter@example.com [example.com] Example.Com LDAP Finger Service Login name: scarter In real life: Sam Carter E-Mail: scarter@example.com Phone: +1 408 555 4798 Department: Accounting Room: 4612 AIM ID: Sam Carter Ex (status: OFFLINE) Login name: scarte2 In real life: Stephen Carter E-Mail: scarte2@example.com Phone: +1 408 555 6022 Department: Product Development Room: 2013 Login name: kcarter In real life: Karen Carter E-Mail: kcarter@example.com Phone: +1 408 555 4675 Department: Human Resources Room: 2320 AIM ID: KarenCarter42 (status: OFFLINE) Login name: mcarter In real life: Mike Carter E-Mail: mcarter@example.com Phone: +1 408 555 1846 Department: Accounting Room: 3819 AIM ID: MikeCarterCPA (status: ONLINE) 4 entries matched 'carter' The Source CodeThe lfingerd.pl source code consists of a single script, which is presented here in pieces to aid understanding. Listing 22.6 shows the prelude and main module. Listing 22.6 The lfingerd.pl Prelude and Main Module1. #!/usr/local/bin/perl 2. # 3. # lfingerd -- a Perl 5 script that implements the server side 4. # of the BSD finger protocol using an LDAP server as its 5. # database of user information. Also returns AIM 6. # online/offline status if Netscape Directory Server 6 or 7. # later is used. 8. # 9. # From the 2nd Edition of the book: 10. # "Understanding and Deploying LDAP Directory Services" 11. # by Timothy A. Howes, Mark C. Smith, and Gordon S. Good. 12. # 13. # Requires: PerLDAP 14. # 15. 16. use Mozilla::LDAP::Conn; 17. 18. # Banner: 19. $banner = "Example.Com LDAP Finger Service"; 20. 21. # LDAP server information: 22. $ldapbase = "dc=example,dc=com"; 23. $ldaphost = "ldap.example.com"; 24. $ldapport = "389"; 25. 26. # Constants: 27. $unknown = "???"; 28. $separator = "\n"; 29. @attrlist = ( "cn", "uid", "mail", "departmentNumber", 30. "telephoneNumber", "roomNumber", "postalAddress", 31. "ou", "title", "displayName", "description", 32. "labeledURI", "nsAIMID", "nsAIMStatusText" ); 33. 34. # Start of main: 35. $ = 1; # enable autoflush of output upon print 36. print "$banner\n\n"; 37. $ = 0; # disable autoflush 38. 39. # grab query string and chop off newline and return characters 40. $query = <STDIN>; 41. chop $query; 42. if ( $query =~ /\r$/ ) { 43. chop $query; 44. } 45. 46. # form a filter using the query string 47. if ( $query =~ /.*=.*/ ) { 48. $filter = "(&(objectClass=person)($query))"; 49. } else { 50. $filter = "(&(objectClass=person)" 51. . "((sn=$query)(cn=$query)(uid=$query)))"; 52. } 53. 54. 55. # open an anonymous connection to the LDAP server 56. $ldap = new Mozilla::LDAP::Conn( $ldaphost, $ldapport ); 57. die "Unable to connect to server at " 58. . "ldap://$ldaphost:$ldapport\n" unless $ldap; 59. 60. # search the directory 61. $entry = $ldap->search( $ldapbase, "subtree", $filter, 62. 0, @attrlist ); 63. 64. # display all the results 65. if ( $entry ) { 66. $count = 0; 67. do { 68. displayEntry( $entry ); 69. $entry = $ldap->nextEntry; 70. ++$count; 71. } while ( $entry ); 72. 73. if ( $count > 1 ) { 74. print $count, " entries matched '$query'\n"; 75. } else { 76. print "One entry matched '$query'\n"; 77. } 78. 79. } else { 80. print "No entries matched '$query'\n"; 81. } 82. 83. # close LDAP connection and clean up 84. $ldap->close; 85. 86. # End of main. 87. The following list summarizes the actions performed in Listing 22.6:
Listing 22.7 shows the displayEntry subroutine, which is used to produce a nicely formatted entry display. This routine is straightforward. It uses simple printf statements to produce a formatted display of an LDAP entry. The resulting text is sent to standard output, which the Unix inetd process has arranged to be sent back to the finger client. Listing 22.7 The lfingerd.pl displayEntry Subroutine88. 89. # Start of displayEntry: 90. sub 91. displayEntry { 92. local( $entry ) = @_; 93. local( $value, @attrs ); 94. 95. @attrs = ( "displayName", "cn" ); 96. printf( "Login name: %-8s " 97. . " In real life: %s\n", 98. getSimpleValue( $entry, "uid" ), 99. getFirstValue( $entry, *attrs )); 100. 101. printf( "E-Mail: %-32s Phone: %s\n", 102. getSimpleValue( $entry, "mail" ), 103. getSimpleValue( $entry, "telephoneNumber" )); 104. 105. @attrs = ( "ou", "departmentNumber" ); 106. printf( "Department: %-32s Room: %s\n", 107. getFirstValue( $entry, *attrs ), 108. getSimpleValue( $entry, "roomNumber" )); 109. 110. $value = getSimpleValue( $entry, "nsAIMID" ); 111. if ( $value ne $unknown ) { 112. printf( "AIM ID: %s (status: %s)\n", $value, 113. getSimpleValue( $entry, "nsAIMStatusText" )); 114. } 115. 116. $value = getSimpleValue( $entry, "postalAddress" ); 117. if ( $value ne $unknown ) { 118. print "Address: $value\n"; 119. } 120. 121. $value = getSimpleValue( $entry, "title" ); 122. if ( $value ne $unknown ) { 123. print "Title: $value\n"; 124. } 125. 126. $value = getSimpleValue( $entry, "description" ); 127. if ( $value ne $unknown ) { 128. print "Description: $value\n"; 129. } 130. 131. displayAllValues( $entry, "labeledURI", "URL: " ); 132. 133. print $separator; 134. } 135. # End of displayEntry. 136. Listing 22.8 shows the code for three subroutines that are called from displayEntry : displayAllValues , getSimpleValue , and getFirstValue . All of these display subroutines access values from an LDAP entry by using PerLDAP's attribute hash array. Listing 22.8 Additional lfingerd.pl Display Subroutines137. 138. # Start of displayAllValues: 139. sub 140. displayAllValues { 141. local( $entry, $attr, $prefix ) = @_; 142. local( $value ); 143. 144. if ( $entry->{$attr} ) { 145. foreach $value (@{$entry->{$attr}}) { 146. print $prefix, $value, "\n"; 147. } 148. } 149. } 150. # End of displayAllValues. 151. 152. # Start of getSimpleValue: 153. sub 154. getSimpleValue { 155. local( $entry, $attr ) = @_; 156. local( $value ); 157. 158. if ( $entry->{$attr} ) { 159. $value = $entry->{$attr}[0]; 160. } else { 161. $value = $unknown; 162. } 163. 164. $value; 165. } 166. # End of getSimpleValue. 167. 168. # Start of getFirstValue: 169. sub 170. getFirstValue { 171. local( $entry, *attrs ) = @_; 172. local( $a ); 173. local( $value ); 174. 175. $value = $unknown; 176. 177. foreach $a (@attrs) { 178. $value = getSimpleValue( $entry, $a ); 179. last if ( $value ne $unknown ); 180. } 181. 182. $value; 183. } 184. # End of getFirstValue. Finally, the system's /etc/inetd.conf configuration file must be modified to tell inetd to execute the lfingerd.pl script instead of the standard in.fingerd executable. Listing 22.9 shows the altered portion of the /etc/inetd.conf file used on a Sun Solaris Unix system. Listing 22.9 Changes to /etc/inetd.conf to Enable the lfingerd.pl Script1. #finger stream tcp nowait nobody /usr/sbin/in.fingerd in.fingerd 2. finger stream tcp nowait nobody /usr/local/etc/lfingerd.pl lfingerd.pl Line 1, which is commented out, is the line originally used to invoke the standard in.fingerd executable. Line 2 is the line that must be added to invoke the new lfingerd.pl Perl script; this line assumes the script is installed in the /usr/local/etc directory. Don't forget to restart the inetd process after making these changes. Ideas for ImprovementMany things could be done to improve the LDAP finger gateway. Here are some ideas:
|