[ Team LiB ] |
10.2 Connecting, Binding, and SearchingTo get started with the Net::LDAP module, we will write a basic LDAP query script named search.pl. This script illustrates the methods used to connect to a directory server and retrieve information. It begins by importing the Net::LDAP symbols via the use pragma: #!/usr/bin/perl use Net::LDAP; After the module has been included, you can create a new instance of a Net::LDAP object. To create a new Net::LDAP instance, you need the hostname of the LDAP server to which the script should connect. The constructor allows several optional arguments, of which the most common and useful are:
The next line of code establishes a connection to the host ldap.plainjoe.org on port 389 using Version 3 of the protocol. The returned value is a handle to a Net::LDAP object that can be used to retrieve and modify data in the directory. $ldap = Net::LDAP->new ("ldap.plainjoe.org", port =>389, version => 3 ); The script can bind to the directory after it obtains a handle to the LDAP server. By default, Net::LDAP uses an implicit anonymous bind, but it supports all the standard binds defined by the LDAPv3 RFCs (anonymous, simple, and SASL). For now, we only examine how to use a simple bind. However, before binding to the server, call start_tls( ) to encrypt the connection; you don't want to send the user DN and password across the network in clear text. In its simplest form, the start_tls( ) method requires no parameters and appears as: $ldap->start_tls( ); It is a good idea to check for errors after attempting to establish a secure communication channel; if start_tls( ) fails, and the script continues blindly, it might inadvertently transmit sensitive account information in the clear. To do so, save the result object returned by start_tls( ), and then use the code( ) method to find out whether start_tls( ) succeeded: $result = $ldap->start_tls( ); die $result->error( ) if $result->code( ); If the script tries to establish transport layer security with a server that does not support this extended operation, the error check displays an error message and exits: Operations error at ./search.pl line XXX. The actual error from Net::LDAP::Constant is LDAP_OPERATIONS_ERROR. Now you can safely send the sensitive data to the server. A simple authenticated bind requires only a DN and a password. If neither are provided, the call attempts to establish an explicit anonymous binding (as opposed to the implicit bind used when bind( ) is not called at all). The following line seeks to bind your client to the directory as the entry cn=Gerald Carter,ou=people,dc=plainjoe,dc=org using the password hello. Once again, you use error( ) and code( ) to check the return status: $result = $ldap->bind("cn=Gerald Carter,ou=people,dc=plainjoe,dc=org", password => "hello"); die $result->error( ) if $result->code( ); If there is no error, the script is free to search the directory. The search( ) method accepts the standard parameters that are expected from an LDAP query tool. At this point, we're interested only in the base, scope, and filter parameters. To make the script more flexible, use the first argument passed in from the command line (i.e., search.pl "Gerald Carter") to build a filter string that searches for the user's common name (cn): $msg = $ldap->search( base => "ou=people,dc=plainjoe,dc=org", scope => "sub", filter => "(cn=$ARGV[0])" ); The return value of the search is an instance of the Net::LDAP::Search object. You can manipulate this object to retrieve any entries that matched the search. This object has a count( ) method that returns the number of entries, and an all_entries( ) method that returns the results as an array of Net::LDAP::Entry objects, each of which represents information associated with a single directory node. You can view the results of this query by dumping each entry from the array: if ( $msg->count( ) > 0 ) { print $msg->count( ), " entries returned.\n"; foreach $entry ( $msg->all_entries( ) ) { $entry->dump( ); } } The output for a single entry looks like this: dn:cn=Gerald Carter,ou=people,dc=plainjoe,dc=org objectClass: inetOrgPerson cn: Gerald Carter sn: Carter givenName: Gerald o: Hewlett-Packard mobile: 256.555.5309 mail: [email protected] postalAddress: 103 Somewhere Street l: Some Town st: AL postalCode: 55555-5555 The dump( ) routine is not meant to generate valid LDIF output, as can be seen from the extra whitespace added to center the attribute/value pairs; another module, aptly named Net::LDAP::LDIF, handles that feature. We'll discuss working with LDIF files later in this chapter. For now, just printing the attribute/value pairs in any form is good enough. What if you're interested only in a person's email address? Some entries contain many attributes, and asking a user to look through all this output in search of an email address could qualify as cruel and unusual punishment. How can you modify the script so that it displays only the attributes you want? The search( ) function has an optional parameter that allows the caller to define an array of attribute names. The search returns only the values of attributes that match names in the list. Here's how to modify the script so that it retrieves only the mail and cn attributes: $msg = $ldap->search( base => "ou=people,dc=plainjoe,dc=org", scope => "sub", filter => "(cn=$ARGV[0])", attrs => [ "cn", "mail" ] ); And here's what you get when you dump the entry returned by the modified query: dn:cn=Gerald Carter,ou=people,dc=plainjoe,dc=org cn: Gerald Carter mail: [email protected] The last line of the script invokes the unbind( ) method to disconnect from the directory: $ldap->unbind( ); This routine effectively destroys the connection. The most portable means to rebind to an LDAP server using a new set of credentials is to call bind( ) again with the new DN and password (but only when using LDAPv3). Once the unbind( ) subroutine has been invoked, the connection should be thrown away and a new one created if needed. The following listing shows the search.pl script in its entirety: #!/usr/bin/perl ## ## Usage: ./search.pl name ## ## Author: Gerald Carter <[email protected]> ## use Net::LDAP; ## Connect and bind to the server. $ldap = Net::LDAP->new ("ldap.plainjoe.org", port =>389, version => 3 ) or die $!; $result = $ldap->start_tls( ); die $result->error( ) if $result->code( ); $result = $ldap->bind( "cn=Gerald Carter,ou=people,dc=plainjoe,dc=org", password => "hello"); die $result->error( ) if $result->code( ); ## Query for the cn and mail attributes. $msg = $ldap->search( base => "ou=people,dc=plainjoe,dc=org", scope => "sub", filter => "(cn=$ARGV[0])", attrs => [ "cn", "mail" ] ); ## Print resulting entries to standard output. if ( $msg->count( ) > 0 ) { print $msg->count( ), " entries returned.\n"; foreach $entry ( $msg->all_entries( ) ) { $entry->dump( ); } } ## Unbind and exit. $ldap->unbind( ); |
[ Team LiB ] |