An Introduction to perl-ldap

A beginner's guide to using Net::LDAP
Adding Entries - add()

If you have a large number of users on your system, it's likely you do not want to add each new user to the system one by one, through a GUI. So, one of the first things you write is a script that quickly adds lots of users in bulk. Or, perhaps you'll write a Web-based system where users can enter their personal information for themselves, with LDAP entries being added automatically. The add() method is used for adding an entry to the database:

$result = $ldap->add("uid=john,ou=People,dc=leapster,dc=org", 
                attr => [ 'cn' => 'John Smith',
                          'sn' => 'Smith',
                          'uid' => 'john',
                          'givenName' => 'John',
                          'homePhone' => '555-2020',
                          'mail' => 'john@domain.name',
                          'objectclass' => [ 'person', 'inetOrgPerson']
                        ]
           );

The above snippet of code adds a user named John Smith to our database. As you can see, the attributes are provided as a list to the attr parameter. Any attribute to which you wish to give multiple values should have them supplied as a list (in our example above, objectclass is such an attribute).

We're now at the point where we can write a simple script to add our large number of users in bulk, a script I've called ldap_addusers.

#!/usr/bin/perl
use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
while(<>) {
        chomp $_;
        ($uid,$givenName,$sn,$mail) = split(/:/,$_);
        $cn="$givenName $sn";
        $dn="uid=$uid,ou=People,dc=leapster,dc=org";
        $result = $ldap->add($dn,
                attr => [ 'uid' => $uid,
                          'cn' => $cn,
                          'sn' => $sn,
                          'mail' => $mail,
                          'givenName' => $givenName,
                          'objectclass' => [ 'person', 'inetOrgPerson']
                        ]
           );
        $result->code && warn "error: ", $result->error;
}

The above script takes a colon-separated file of users, one per line, on stdin:

tom:Tom:Jones:tom@domain.name
dick:Dick:Tracy:dick@domain.name
harry:Harry:Windsor:harry@domain.name

So, if these names are stored in a file called userlist, we can enter them into our LDAP database with the ldap_addusers script, as follows:

cat userlist | ./ldap_addusers

There's one line in the ldap_addusers script that we haven't seen before:

$result->code && warn "error: ", $result->error;

As mentioned earlier, the add() method returns an object of type Net::LDAP::Message. Here, this object is referenced by $result. $result->code is the code value returned from the LDAP server in the result message after a query (in this case, the request to add an entry). Generally, when the request is successful, a zero is returned. Hence, in our above statement, the warning is issued only when $result->code is not zero.

Some other useful methods in Net::LDAP::Message are:

$result->dn        The DN contained in the result message
$result->error     The error message in the result (only if there was an error)
$result->done      True if the request was completed
$result->is_error  True if the particular result is an error for the operation

For a full description of the other methods, see the perldoc manual for Net::LDAP::Message.

perl-ldap also provides the delete() method for deleting entries, when given their DN:

$dn="uid=paul,ou=People,dc=leapster,dc=org";
$ldap->delete($dn);

This would, for example, allow you to write a script to do a bulk delete of expired users from your system:

#!/usr/bin/perl
use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
while(<>) {
        chomp $_;
        $dn="uid=$_,ou=People,dc=leapster,dc=org";
        $ldap->delete($dn);
}

This script deletes all users whose uids are fed to it on standard input, one per line. Be careful when using this script on production servers; if you accidentally feed it the wrong file, you may find yourself with no users left in your directory.

Searching for Entries - search()

It wouldn't be much use only to be able to write to our LDAP server; we need to be able to read from it also. The perl-ldap search command is used to perform lookups on an LDAP server.

$mesg = $ldap->search(filter=>"(uid=paul)", base=>"dc=leapster,dc=org");

The base parameter specifies the base object entry from which the search will be made. In the example above, it searches the entire LDAP tree. The search could be confined to only the ou=People branch with:

$mesg = $ldap->search(  filter=>"(uid=paul)", 
                        base=>"ou=People,dc=leapster,dc=org");

A number of other optional parameters are available to the search() method:

  • scope : This can be one of the following:

    • base: Search the base object only

    • one: Search only the entries one level below the base

    • sub: Search the entire subtree below the base

    If I knew no subtrees were below ou=People--or if there were, but I didn't want results returned from them--I could use:

    $mesg = $ldap->search(  filter=>"(uid=paul)", 
                            base=>"ou=People,dc=leapster,dc=org"
                            scope=>"one");
    
  • timelimit: Set a limit in seconds on the amount of time that a request may take. The default is 0, which signals that the time is unlimited.

  • attrs: Set the attributes (as a reference to an array) that should be returned in the search. If not provided, the search returns all of the attributes. For example, the following search returns only the uid, sn and givenName attributes:

    $mesg = $ldap->search(  filter=>"(uid=paul)", 
                            base=>"ou=People,dc=leapster,dc=org",
                            attrs=> ['uid', 'sn', 'givenName'] );
    

  • filter: The filter may be a string, in standard LDAP filter format (see the ldap_search(3) man page for a description of this), or it may be a Net::LDAP::Filter object (see the Net::LDAP:Filter man page for further information).

The search() method returns Net::LDAP::Search objects. The easiest way to get at the contents of this object is to use its entries() method, which returns an array of Net::LDAP::Entry objects (see below):

@entries = $mesg->entries;

The Net::LDAP::Search object also has a number of other useful methods:

$mesg->count;           The number of entries returned in the search
$mesg->entry(n);        Returns the n'th entry (initial entry is 0)
$mesg->sorted([list])   Returns a list of entry objects sorted by attr list

Now, we can write a small script to list every entry in the directory:

#!/usr/bin/perl
use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
$mesg = $ldap->search(filter=>"(objectClass=*)", base=>"dc=leapster,dc=org");
@entries = $mesg->entries;
foreach $entry (@entries) {
        $entry->dump;
}

I've cheated a little in the above script and used the dump() method of Net::LDAP::Entry, in order to make things clearer. dump() primarily is used for debugging; it merely dumps the DN and contents of an entry straight to standard output, without allowing for any manipulation of the results.

The most commonly used methods of the Net::LDAP::Entry object are:

  • attributes: returns the list of attributes contained in this entry.

    @attrs = $entry->attributes();
    

  • dn: Returns the DN of the current entry. If given with a parameter, it sets the DN of the entry:

    $dn = "uid=pbd,ou=Users,dc=leapster,dc=org";
    $entry->dn($dn);
    

  • get_value: obtains the value or values for the attribute, given as a parameter. If this method is used to assign to a scalar variable, it returns only the first value for the attribute; if used with an array, it returns all of the attributes.

    $phone = $entry->get_value("homePhone");  # returns only one phone number
    @phone = $entry->get_value("homePhone");  # returns all phone numbers for entry
    

  • add, delete, modify: These methods allow changes to be made to the entry and are discussed further in the next section.

  • update: pushes any changes made to the entry to the LDAP server (whose object is given as a parameter):

    $entry->add(homePhone => "555 3034");
    $entry->update($ldap);
    

Now that we've examined the Net::LDAP::Entry object, we can expand the above script further. We can write out the contents of the entries ourselves:

#!/usr/bin/perl
use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
$mesg = $ldap->search(filter=>"(objectClass=*)", base=>"dc=leapster,dc=org");
@entries = $mesg->entries;
foreach $entry (@entries) {
        print "dn: " . $entry->dn() . "\n";
        @attrs = $entry->attributes();
        foreach $attr (@attrs) {
                printf("\t%s: %s\n", $attr, $entry->get_value($attr));
        }
}
______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Awesome Work

Vineet Niranjan's picture

Awesome codes and illustrations.
Would have appreciated more if the Acronyms were clear.

How to add UserAccountControl

Anonymous's picture

How to add user ACcount Control Parameter ,it not working ?

UID

Anonymous's picture

Cannot figure what the value of UID should be? I can dump all the contents with filter set as (objectclass=*) and get around 200 entries ; But, when I try to search for a particular UId, I do not get any results back. can anyone tell me what value should be put in for UID to search? I tried with filter=>"(uid=vinda)" filter=>"(uid=vinda norman)" filter=>"(uid=norman)" but no luck.

dn: CN=vinda Norman,OU=Users,OU=SysStaff,OU=SBCS,DC=ad,DC=cs,DC=sunysb,DC=edu
objectClass: top
cn: Vinda Norman
sn: Norman
givenName: Vinda
distinguishedName: CN=Vinda Norman,OU=Users,OU=SysStaff,OU=SBCS,DC=ad,DC=cs,DC=sunysb,DC=edu
instanceType: 4
whenCreated: 20080904132500.0Z
whenChanged: 20090906023318.0Z
displayName: Vinda norman
uSNCreated: 9019
memberOf: CN=System Staff Users,CN=Users,DC=ad,DC=cs,DC=sunysb,DC=edu
uSNChanged: 778762
name: Vinda Norman
objectGUID: *nÄsM¹lN_Aö
userAccountControl: 512
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 128975058670944212
lastLogoff: 0
lastLogon: 128975753602883244
pwdLastSet: 128965394154875698
primaryGroupID: 513
objectSid: ªl²p!Xù{®MZ
accountExpires: 9223372036854775807
logonCount: 267
sAMAccountName: vinda
sAMAccountType: 805306368
userPrincipalName: vinda@ad.cs.sunysb.edu

What happens if bind fails

Am's picture

$mesg = $ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");

I tried this, but even with a wrong password, this line did not give an error.

you mean it still connects

Anonymous's picture

you mean it still connects to LDAP?

This tutorial has helped me

Ldap_guy's picture

This tutorial has helped me created a whole project in one week. This was exactly what i needed! Good job

use warnings; use strict;

Anonymous's picture

use warnings;
use strict;

perl-ldap vs. PerLDAP

Anonymous's picture

Not be be confused with another project, PerLDAP, which started back when Netscape was king. I had used PerLDAP for years, before perl-ldap even existed. It's now part of the Mozilla Foundation and is available here: http://www.mozilla.org/directory/perldap.html

The one downside is that it requires the Netscape Directory SDK. But it's free and available for almost any platform.

PerLDAP came after perl-ldap

Anonymous's picture

PerLDAP was originally Net::LDAPapi by Clayton Donley. Netscape announced taking over the module and renaming it PerLDAP at the second perl conference, which was held in San Jose in August 1998.

perl-ldap (Net::LDAP) and Net::LDAPapi projects were both started in 1997 about the same time.

PerLDAP can be difficult to get working though!

Anonymous's picture

I've been trying to get Bugzilla to work with PerLDAP, and while I can successfully compile the Netscape directory code, I can't get PerLDAP itself to compile - too many miconfigurations it appears, maybe some problems with versioning b/w PerLDAP & Netscape's SDK. Anyway, I gave up, and I now use Paul's patch to bugzilla which allows bugzilla to work with Net::LDAP. Cheers Paul.

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState