Perl and LDAP

 < Day Day Up > 



The Perl language has grown in scope and popularity over the last ten years. The first Perl manual, "Programming Perl," was written by its creators Larry Wall and Randal L. Schwartz in 1991. Since then, Perl has developed object-oriented features and has advanced from version 4.0 to version 5.8. You can download it from CPAN (Comprehensive Perl Archive Network). CPAN provides an overwhelming number of libraries for this extremely powerful programming language. The invaluable advantage of Perl is its pattern-matching functions. These functions allow you to write in one line of code what would require ten lines in the C language. The language is interpreted, [1] and this makes it a rapid prototype development tool. Note that there is also a Perl-to-C compiler available.

As with all open-source software, Perl too has a library for LDAP. Actually, it has multiple libraries from which you can choose. If you search on the Internet it is easy to get confused, because sometimes the same software is known under different names. There are substantially three main threads:

  1. Net::LDAPapi from Clayton Donley, outdated (only LDAP [v2]), but still heavily used

  2. PerlLDAP available from http://www.mozilla.org/directory/perldap.html

  3. Net::LDAP bundle from Graham Barr, also available from Source-ForgeNet with the name "perl-ldap," at http://perl-ldap.source-forge.net

In this section, we will use the third option, Net::LDAP. The first two options rely on C, but Net::LDAP is completely native Perl code, so you need only Perl to make it work. However, the differences between the different libraries are not so great, so if you have some idea of the Perl language you should be able to use a different Perl library. Actually, the term "library" is not technically correct because most of these "libraries" use the object-oriented aspects of Perl. The term we should use for all the examples in this section on Perl is "package." If we use these terms interchangeably in this chapter, bear in mind that "package" is the correct term.

There are some interesting libraries worth checking out if you are interested in LDAP programming with Perl. One is the Tie-LDAP library from Taisuke Yamada, which allows you to treat an LDAP directory just as an associative array. The other is the DBD-LDAP library from Jim Turner. This library allows you to access a directory as if it were a database by using the DBI (database interface) and speaking SQL (structured query language).

Like every other API, the Perl API follows also the afore-mentioned procedure, i.e.:

  • Connecting to the LDAP server

  • Binding to the server

  • Doing some work

  • Unbinding to release the resources

Our First Perl LDAP Program

As with the other APIs, let us first look at a real-life program, this time written in Perl. Exhibit 29 shows a short Perl program using the Net::LDAP module. If you have read the previous section covering PHP and LDAP, you will see the same concepts presented here.

start figure

 01  #!/usr/local/bin/perl -w 02  use Net::LDAP ; 03 04  $User = $ARGV[0] ; 05  print "Searching: $User \n" ; 06 07  local $Base = "ldap_abc.de" ; 08  local $Filter = "uid=$User" ; 09  local $Host = "localhost" ; 10 11  $ldap = Net:: LDAP->new($Host) or die "$@" ; 12  $ldap->bind | | die "Could not bind to $Host" ; 13  $result = $ldap->search (base => $Base, filter => $Filter); 14  $result->code && die $result->error ; 15  if ($result->count == 0) { 16    print "NO ROW FOUND \n" ; 17  } 18  foreach $entry ($result->all_entries) { 19    $entry->dump; 20  } 21  $ldap->unbind ; 

end figure

Exhibit 29: Our First Perl LDAP Program

First you create an LDAP object specifying the host you will connect to. This allocates a structure in memory for you. With this LDAP object, you bind to the LDAP server. In this case, we bind anonymously. The search method delivers a result object. In the case of errors (the code () method has a return value not equal to 0) methods unequal 0), the method "error()" describes what went wrong. You can use a number of methods on the result object: "count()" delivers the number of entries satisfying the search, and "all_entries" delivers the whole array of entries. The "dump" method applied on a single entry dumps out all its attributes, including the distinguished name. In the rest of this section, we will review the most important objects in the Perl API and its methods.

Perl Objects

There are six objects you will use in a Perl program. These objects are:

  1. ldap: The base object that executes nearly all operations on the directory. You will find all the methods described in the functional model in Chapter 3. This object also provides access to the directory schema and the root_dse.

  2. search: The search method on behalf the LDAP object returns a search object. This object is a container object for the search result. It offers methods such as the count on the total number of found entries and fetch methods to get single entries from the results set.

  3. entry: This object lets you access the single attributes of an entry. An entry object can be created in two ways: first as the result of an explicit creation via the new operator, second via one of the fetch methods using the search object

  4. message: The message objects lets you analyze errors reported by LDAP operations. It holds the error number and the error message in a human-friendly format. It also has the sync method that allows you to run asynchronous requests on the server.

  5. reference: The reference object has only one method, "references," that displays a list of references returned by the directory server.

  6. schema: The schema object allows you to explore the schema.

The LDAP Object

First of all, we have to create an LDAP object, and this happens via the new constructor. (Most object-oriented languages use a special method to construct a new object. This method is called "construction.") The constructor must specify the host you wish to connect to. You can furthermore specify a number of options.

 $ldap = new Net::LDAP(<Host>,                       port     => <Port>,                       timeout  => <TimeOut>,                       async    => (0,1),                       version  => (2,3)                      ); 

Host, port, and timeout are straightforward, and the default value for timeout is 120 s. "Async 1" means that all the actions have to be executed asynchronously. With "version," you can specify whether you wish to use LDAP (v2), the default value, or LDAP (v3). There are still more parameters. For more information, refer to the manual pages of the Net::LDAP package.

The LDAP object recognizes the methods identified in the functional model plus some special ones.

Authentication/Control Methods

Authentication/control are achieved by the bind, unbind, and abandon methods. The bind can be anonymous or authenticated via the user's distinguished name and password.

 $Message = $ldap->bind([DN],                         password   => <password>,                         control    => <Control>,                         callback   => <Callback>,                         sasl       => <Sasl>                      ); 

We have already seen an anonymous bind, so let us see in Exhibit 30 an example of authentication using a distinguished name and password. The bind method has further arguments, which can be found in the manual pages of the Net::LDAP package. Some additional authentication types include:

  • no authentication: Indicates an anonymous connection, in which case you do not have to supply the DN

  • password: User supplies DN and the password

  • sasl: User binds using the SASL mechanism. You can obtain the argument via the Authen::SASL object. (See the Perl documentation for more details.)

start figure

 $host    = "ldap.AbcLdap.org" ; $port    = 389 ; $DN      = "cn=admin, o=LdapAbc.org" ; $pasword = "password1" ; $ldap = new Net::LDAP($host,port => $port); $ldap->bind($DN,                 password  => $password,                 version   => 3,               ) || die "Could not bind to server";     . . . $ldap->unbind(); 

end figure

Exhibit 30: Example of Authentication Using DN and Password

The unbind() method takes no argument at all. The abandon operation takes the messageID of a long-running command. This must be an asynchronous command because synchronous commands do not return a result until the command has been completed. The following example shows a typical abandon in action.

 $message = $ldap->search(@SearchArgs); $ldap->abandon($message); 

You could also write this abandon as a method acting upon the message object:

 $message = $ldap->search(@SearchArgs); $message->abandon(); 

Interrogation Methods

Here we have the usual search and compare methods. "Search" takes as arguments the search base, the scope, and the filter parameters. You can also specify the attributes to be returned.

 $Message = $ldap->search  (base      => <base>,                           scope      => <scope>,                           sizelimit  => <sizelimit>,                           timelimit  => <timelimit>,                           typesonly  => (true|false),                           filter     => <filter>,                           attrs      => <attrs>,                           control    => <control>,                           callback   => <callback>,                           ); 

The relevant arguments are:

  • base: Search base

  • scope: (base, one, and sub)

  • filter: Search filter

  • timelimit: Limit in seconds; 0 means no time limit

  • sizelimit: Limit in entries to be returned; 0 means no limit

For the other parameters, see the online documentation.

Exhibit 31 shows an example of the search method. The other interrogation method is "compare()." It reports "true" if the entry identified with DN has the specified attribute with the specified value. Here is an example:

 $DN = "uid=JParker, ou=IT, o=LdapAbc.org" ; if ($ldap->compare($DN,                    attr=>givenName,                    value=>"James") {    printf("Found correct Person\n"); } 

start figure

 $base = "ou=IT, o=LdapAbc.org" ; $filter = "(sn=Parker)" ; $attributes = ["sn","cn","uid","mail"] ; $Message = $ldap->search (base => $base,                           filter => $filter,                           attrs  => $attributes,                           ); $Message->code() && die "$Message->error()" ; foreach $entry ($mesg->all_entries) {   $entry->dump; } 

end figure

Exhibit 31: Example of Search Method

Update Methods

The update methods are add(), delete(), modify(), and moddn().

Add an Entry — There are two ways to add an entry:

  1. Using the Net::LDAP::Entry object, as depicted in Exhibit 32.

    start figure

     use Net::LDAP::Entry ; $entry = new Net::LDAP::Entry(); . . . (see entry object later) $ldap->add($entry); 

    end figure

    Exhibit 32: New Entry Creating a New Entry Object

  2. Specifying the attributes in the add() method, as seen in Exhibit 33.

    start figure

     $Msg = $ldap->add($DN,              attrs => [                   objectClass => ["top",                                  "person",                                  "organizationalPerson",                                  "inetOrgPerson",                                  ],                   sn          => "Voglmaier",                   givenName   => "Reinhard",                   cn          => "Reinhard E. Voglmaier",                   uid         => "RVoglmaier",                   mail        => Rvoglmaier@LdapAbc.org",                   ],               ); $Msg->code() && die "$Msg->error( )" ; 

    end figure

    Exhibit 33: New Entry Using the Add Method

Delete an Entry — The delete method is straightforward. You need only the DN. See Exhibit 34 for an example.

start figure

 $DN = "uid=JParker, ou=Marketing, o=LdapAbc.org" ; $Msg = $ldap->delete($DN); $Msg->code() && die "$Msg->error()" ; 

end figure

Exhibit 34: Delete an Entry

Modify an Entry — Exhibit 35 is an example showing how to modify an entry in a directory. Some comments on the code:

  • The add and replace key needs a pointer to an associative array.

  • The delete key needs a pointer to an associative array if you wish to delete only an attribute with a specified value. This is used mostly for multivalue attributes. For example, the e-mail address could have multiple values. Here we delete the value at ldapabc.de.

start figure

 $host     = "ldap.AbcLdap.org" ; $port     = 389 ; $BindDN  = "cn=admin, o=LdapAbc.org" ; $BindPW  = "password1" ; $ldap = new Net::LDAP($host,port => $port); $ldap->bind($BindDN,                 password => $BindPW,                 version  => 3,            ) || die "Could not bind to server"; $dn = "uid=JKahn,ou=Marketing,o=LdapABc.org" ; $ldap->modify($dn,               add => [ telephonenumber => '428' ],             ) ; $Msg->code() && die "$Msg->error()" ; $ldap->modify($dn,               delete => [mail => 'JKahn\@ldapabc.de'],             ); $Msg->code() && die "$Msg->error()" ; $ldap->modify($dn,               delete => [telexnumber],             ); $Msg->code() && die "$Msg->error( )" ; $ldap->modify($dn,               replace =>[ fax => '827' ],             ); $Msg->code() && die "$Msg->error( )" ; $ldap->unbind( ) ; 

end figure

Exhibit 35: Example Modifying Entries

If the whole update refers to one distinguished name, you can use the shortcut "changes" as shown in Exhibit 36.

start figure

 $Msg = ldap->modify($dn,                 changes => [                    add => [ telephonenumber => '428' ],                    delete => [mail => 'JKahn\@ldapabc.de'],                    delete => [telexnumber],                    replace =>[ fax => '827' ],                 ],                ); $ldap->unbind(); 

end figure

Exhibit 36: Further Possibility to Modify an Entry

Modify Distinguished Name — To modify the distinguished name, you can use the moddn() method. As discussed in Chapter 3 when we discussed the functional model and in Chapter 4 when we saw the LDIF format, there are a number of possibilities for changing the distinguished name. You can change the distinguished name while keeping the old distinguished name. Otherwise, you have to specify "deleteoldrn." In LDAP (v3) you can move the entry in the directory by specifying "newsuperior." In our new example in Exhibit 37, Mr. Kahn moves from Marketing to Human Resources.

start figure

 $dn = "uid=JKahn,ou=Marketing,o=LdapABc.org" ; $Msg = $ldap->moddn($dn,                 newrdn => "uid=JKahn",                 deleteoldrn => true,                 newsuperior => "ou=HR,o=LdapAbc.org",              ); 

end figure

Exhibit 37: Modify the Distinguished Name

Schema Exploring (LDAP [v3])

One of the new properties of LDAP (v3) is that it stores information about itself in the directory. That permits the client to obtain useful information about the directory and to adapt its actions on the directory structure it discovered. For example, can the client find out which version of LDAP the server uses (v2 or v3). The Net::LDAP object gives you two methods of retrieving information about the directory: the schema() method and the root_dse() method.

root_dse() — You get the root DSE simply by calling the root_dse() method. As a search method, you can supply a reference to the list of attributes to be returned.

 $ListPtr = ["subschemaSubentry","supportedExtension"] ; $root_dse = $ldap->root_dse(attrs => $ListPtr); 

The attrs with the pointer is optional

 $root_dse = $ldap->root_dse(); 

If you omit the attrs option, the method gives you information about following variables:

 subschemaSubentry namingContexts altServer supportedExtension supportedControl supportedSASLMechanisms supportedLDAPVersion 

Schema() — The schema() methods returns the schema object. You will learn more about the schema object in the corresponding paragraph later in this chapter. Exhibit 38 shows how to explore the schema using Perl LDAP objects.

start figure

 $schema = $ldap->schema(); # print out all objectClasses: foreach $OC ($schema->objectclasses()) {     printf("%s \n",$OC); } # print out all attributes: foreach $AT ( $schema->attributes()) {     printf("%s \n",$AT); } 

end figure

Exhibit 38: Exploring the Schema

Callback

Most of the methods accept a callback option. The value for the callback option should be a reference to a subroutine. Exhibit 39 shows an example of code registering the callback; Exhibit 40 shows the definition of the callback itself.

start figure

 $base = "ou=IT, o=LdapAbc.org" ; $filter = "(sn=Parker)" ; $attributes = ["sn","cn","uid","mail"] ; $Message = $ldap->search (base       => $base,                             filter    => $filter,                             attrs     => $attributes,                             callback  => \$SearchCB(),                           ); 

end figure

Exhibit 39: Using Callbacks

start figure

 sub SearchCB {   my $mesg = shift;   my $obj = shift;   if (!$obj) {     # Search complete     #   }   elsif ($obj->isa('Net::LDAP::Reference')) {     my $ref;     foreach $ref ($obj->references) {       # process ref     }   }   else {     # Process Net::LDAP::Entry   } } 

end figure

Exhibit 40: Callback for Exhibit 39

Remember that LDAP is a message-oriented protocol. The client sends a request to the server and gets one or more responses to its request. The subroutine referenced with callback will be called each time one of these responses arrives at the client. When the subroutine is called, it gets a message object as its first argument. If the method was a search, the second argument is a search object. If the server instead sends back a reference, the second argument is a reference object.

The Search Object

The search object is a container object that simply holds the result list of a query. There are several methods to access the entries satisfying the query, such as:

  • entries(): Returns an array of entries returned by the search

  • entry (INDEX): Returns entry with index number INDEX

  • shift_entry/pop_entry: Shifts/pops an entry from the internal result list

  • sorted([attribute list]): Returns the entries sorted by the attribute list

An interesting method is count(), which returns the number of entries found and references the reference objects returned by the directory server. Most of these methods will be seen in the next section.

The Entry Object

The entry object has two functions:

  1. Construct a new entry

  2. Access the individual attributes on an existing entry

Let us create an entry and add it to the directory. Exhibit 41 shows you the Perl code that does the job.

start figure

 #!/usr/bin/perl -w use Net::LDAP ; use Net::LDAP::Entry ; #Define some values: local $host     = "127.0.0.1" ; local $port     = 389 ; local $DN       = "cn=admin, o=LdapAbc.org" ; local $pass     = "password1" ; $ldap = Net: :LDAP->new($host, port => $port) or die "$@" ; printf("Server %s contacted, connection OK\n",$host); $ldap->bind($DN,                password => $pass,                version  => 3) || die "Could not bind" ; $ObjectClasses = ["top",                   "person",                   "organizationalPerson",                   "inetOrgPerson"] ; $entry = new Net::LDAP::Entry(); $entry->dn("uid=SPalmer, ou=Marketing, o=LdapAbc.org"); $entry->add(objectClass => $ObjectClasses ,               sn          =>  "Palmer" ,               givenName   =>  "Stephen",               cn          =>  "Stephen Palmer",               uid         =>  "SPalmer",               mail        =>  "SPalmer\@LdapAbc.org",             ); $mesg = $entry->update($ldap); $mesg->code && die $mesg->error ; $ldap->unbind(); 

end figure

Exhibit 41: Example Using the Entry Object

Next, we modify some existing entries found via the search object mentioned in the previous section. However, we print out only the values we retrieve via the "get method" call, as shown in Exhibit 42.

start figure

 # as the previous example: printf("Server %s contacted, connection OK\n",$host); $mesg = $ldap->search(base => "o=LdapAbc.org",                       filter => "(sn=P*)",                        ); my $max = $mesg->count(); printf("Found %d entries\n",$max); for($i = 0 ; $i < $max ; $i++)    my $entry = $mesg->entry($i);    printf("Distinct Name: %s\n",$entry->dn());    foreach my $attr ($entry->attributes) {        printf("%s: %s\n",$attr,                          $entry->get_value($attr));    } } 

end figure

Exhibit 42: Example Using the Entry Object and the Get Method

As seen in the example, you can access the individual attributes via the get_value() method. You also can set the attributes via the set_value() method. The access to the distinguished name is different; you get and set it via the dn() method. You set the distinguished name if you give it as parameter to the dn() method. See Exhibit 41.

The entry object also offers all the methods required to update entries, i.e., delete, add, or modify attributes. Again, refer to the documentation of your installation.

The Message Object

The most important methods of the message object are: the code() method that is set to "1" if an error occurs and the error() method that gives you the error message. We have seen these methods in action in the previous examples.

The message object has two methods of controlling asynchronous requests:

  1. sync(), to wait for the execution of the request

  2. done(), to ask if the request has been completed

The Reference Object

We have nothing new to add to the previous discussion of the reference object. The reference object is a container object to hold the references returned by the directory server.

The Schema Object

The schema object is interesting, inasmuch as it allows you to explore the schema. Exhibit 43 shows how to use the schema object. As you see from Exhibit 43, the schema object contains the complete schema information. In the example, we get a list of all object classes, attributes, matching rules, matching rules use, and syntaxes.

start figure

 #!/usr/bin/perl -w use Net::LDAP ; use Net::LDAP::Entry ; #Define some values: local $host   = "127.0.0.1" ; local $port   = 389 ; $ldap = Net::LDAP->new($host, port => $port) or die "$@" ; $schema = $ldap->schema(); # print out all objectClasses: printf("ObjectClasses:\n"); foreach $OC ($schema->objectclasses()) {     printf("%s oid: %s\n",$OC, name2oid($OC)) ; } printf("Attributes:\n"); foreach $AT ($schema->attributes()) {     printf("%s oid: %s\n",$AT, name2oid($AT)) ; } printf("Matching Rules:\n"); foreach $MR ($schema->matchingrules()) {     printf("%s oid: %s\n",$MR, name2oid($MR)) ; } printf("Matching Rules:\n"); foreach $MRU ($schema->matchingruleuse()) {     printf("%s oid: %s\n",$MRU, name2oid($MRU)) ; } printf("Syntaxes:\n"); foreach $SY ($schema->syntaxes()) {     printf("%s oid: %s\n",$SY,name2oid($SY)) ; } 

end figure

Exhibit 43: Schema Exploring

There are also functions to test whether a given element (object class, attribute, etc.) is part of the given schema. The methods is_objectclass, is_attribute, is_syntax, and is_matchingrule report "true" if the element is part of the schema and "false" if it is not.

In Exhibit 43, the method "name2oid" was used. This method prints out the oid of the given element. If you want all information available in the schema, you can simply dump it out using the dump() method, as shown in Exhibit 44.

start figure

 #! /usr/bin/perl -w use Net::LDAP ; use Net::LDAP::Entry ; #Define some values: local $host   = "127.0.0.1" ; local $port   = 389 ; $ldap = Net::LDAP->new($host, port => $port) or die "$@" ; $schema = $ldap->schema(); $schema->dump(); 

end figure

Exhibit 44: Dump Method

You can also explore single entries to understand which attributes are required (the must() method) and which are optional (the may() method). Look at the lines in Exhibit 45 that do this for the entry inetOrgPerson. Note, however, that the "may" and "must" methods do not print out attributes inherited from the parent. You can get the name of the parent with the method "superclass" and, in this way, construct a program that really does dump out all attributes.

start figure

 #! /usr/bin/perl -w use Net::LDAP ; use Net::LDAP::Entry ; #Define some values: local $host   = "127.0.0.1" ; local $port   = 389 ; $ldap = Net::LDAP->new($host, port => $port) or die "$@" ; $schema = $ldap->schema(); for $attribute ($schema->must("inetOrgPerson")) { $MustList .= $attribute ; } for $attribute ($schema->may("inetOrgPerson")) {       $MaytList .= $attribute ; } printf("Must: %s\n", $MustList); printf("May:  %s\n", $MayList); printf("Parent: %s\n", $schema->superior()); 

end figure

Exhibit 45: Dumping a Single Entry

Conclusion

In this section, we have seen the Perl library and learned that it provides a very comfortable interface to directory servers. Perl is often used in common gateway interface (CGI) scripts, which makes it easy to write gateways to access directory services via the HTTP protocol. Moreover, Perl is available on a wide variety of platforms, and many third-party tools offer Perl libraries, allowing you to connect directory services over a broad range of platforms and applications. Perl also has libraries to access RDBMS (Oracle, Informix, ODBC, DBM, and many others), so you can write connectors between your database applications and your directory server.

Like the LDAP protocol, the LDAP Perl interface has to be considered a work in progress. We could not include all methods available, as this would be beyond the scope of this book.

A last word of advice before closing this section: When you are developing LDAP software using the Perl libraries, it is a good idea to have a look at the CPAN Web site. There, under Net::LDAP, you can find a number of useful scripts that may be helpful in resolving problems you may encounter. The following section lists some examples. By the time you read this book, there may be many more, as the CPAN site is continuously evolving.

Scripts

  • jpegDisplay.pl

    • A script to display a jpeg picture from the jpegPhoto attribute of an LDAP directory entry

    • Author: Clif Harden <"><charden@pobox.com>>

  • jpegLoad.pl

    • A script to load a jpeg picture into the jpegPhoto attribute of a directory entry

    • Author: Clif Harden <"><charden@pobox.com>>

  • ldifdiff.pl

    • Generates LDIF "change diff" between two sorted LDIF files

    • Author: Kartik Subbarao <"><subbarao@computer.org>>

  • ldifsort.pl

    • Sorts an LDIF file by the specified key attribute. The sorted version is written to standard output.

    • Author: Kartik Subbarao <"><subbarao@computer.org>>

  • ldifuniq.pl

    • Culls unique entries from a reference file with respect to a comparison file

    • Author: Kartik Subbarao <"><subbarao@computer.org>>

[1]An "interpreted" language as opposed to a "compiled" language is a language that does not need to be compiled in order to be executed. Because compilation takes time, the development of programs in interpreted language is faster. Execution, however, is slightly slower.



 < 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