SPOPS::LDAP - Implement object persistence in an LDAP datastore


 use strict;
 use SPOPS::Initialize;

 # Normal SPOPS configuration

 my $config = {
    class      => 'My::LDAP',
    isa        => [ qw/ SPOPS::LDAP / ],
    field      => [ qw/ cn sn givenname displayname mail
                        telephonenumber objectclass uid ou / ],
    id_field   => 'uid',
    ldap_base_dn => 'ou=People,dc=MyCompany,dc=com',
    multivalue => [ qw/ objectclass / ],
    creation_security => {
                 u => undef,
                 g   => { 3 => 'WRITE' },
                 w   => 'READ',
    track        => { create => 0, update => 1, remove => 1 },
    display      => { url => '/Person/show/' },
    name         => 'givenname',
    object_name  => 'Person',

 # Minimal connection handling...

 sub My::LDAP::global_datasource_handle {
     my $ldap = Net::LDAP->new( 'localhost' );
     return $ldap;

 # Create the class

 SPOPS::Initialize->process({ config => $config });

 # Search for a group of objects and display information

 my $ldap_filter = '&(objectclass=inetOrgPerson)(mail=*';
 my $list = My::LDAP->fetch_group({ where => $ldap_filter });
 foreach my $object ( @{ $list } ) {
     print "Name: $object->{givenname} at $object->{mail}\n";

 # The same thing, but with an iterator

 my $ldap_filter = '&(objectclass=inetOrgPerson)(mail=*';
 my $iter = My::LDAP->fetch_iterator({ where => $ldap_filter });
 while ( my $object = $iter->get_next ) {
     print "Name: $object->{givenname} at $object->{mail}\n";


This class implements object persistence in an LDAP datastore. It is similar to SPOPS::DBI but with some important differences -- LDAP gurus can certainly find more:

  • LDAP supports multiple-valued properties.

  • Rather than tables, LDAP supports a hierarchy of data information, stored in a tree. An object can be at any level of a tree under a particular branch.

  • LDAP supports referrals, or punting a query off to another server. (SPOPS does not support referrals yet, but we fake it with SPOPS::LDAP::MultiDatasource.)


See SPOPS::Manual::Configuration for the configuration fields used and LDAP-specific issues.


Configuration Methods

See relevant discussion for each of these items under CONFIGURATION (configuration key name is the same as the method name).

base_dn (Returns: $)

ldap_objectclass (Returns: \@) (optional)

id_value_field (Returns: $) (optional)

Datasource Methdods

global_datasource_handle( [ $connect_key ] )

You need to create a method to return a datasource handle for use by the various methods of this class. You can also pass in a handle directory using the parameter 'ldap':

 # This object has a 'global_datasource_handle' method

 my $object = My::Object->fetch( 'blah' );

 # This object does not

 my $object = Your::Object->fetch( 'blah', { ldap => $ldap });

Should return: Net::LDAP (or compatible) connection object that optionally maps to $connect_key.

You can configure your objects to use multiple datasources when certain conditions are found. For instance, you can configure the fetch() operation to cycle through a list of datasources until an object is found -- see SPOPS::LDAP::MultiDatasource for an example.

Class Initialization


Just create the 'field_list' configuration parameter.

Object Information

dn( [ $new_dn ] )

Retrieves and potentially sets the DN (distinguished name) for a particular object. This is done automatically when you call fetch() or fetch_group() to retrieve objects so you can always access the DN for an object. If the DN is empty the object has not yet been serialized to the LDAP datastore. (You can also call the SPOPS method is_saved() to check this.)

Returns: DN for this object


Builds a DN from an object -- you should never need to call this and it might disappear in future versions, only to be used internally.

Object Serialization

Note that you can pass in the following parameters for any of these methods:

  • ldap: A Net::LDAP connection object.

  • connect_key: A connection key to use for a particular LDAP connection.

fetch( $id, \%params )

Retrieve an object with ID $id or matching other specified parameters.


  • filter ($)

    Use the given filter to find an object. Note that the method will die if you get more than one entry back as a result.

    (Synonym: 'where')

fetch_by_dn( $dn, \%params )

Retrieve an object by a full DN ($dn).

fetch_group( \%params )

Retrieve a group of objects

fetch_iterator( \%params )

Instead of returning an arrayref of results, return an object of class SPOPS::Iterator::LDAP.

Parameters are the same as fetch_group().

save( \%params )

Save an LDAP object to the datastore. This is quite straightforward.

remove( \%params )

Remove an LDAP object to the datastore. This is quite straightforward.


Renaming of DNs not supported

Moving an object from one DN to another is not currently supported.



("This is quite straightforward" does not cut it.)

More Usage

I have only tested this on an OpenLDAP (version 2.0.11) server. Since we are using Net::LDAP for the interface, we should (in theory) have no problems connecting to other LDAP servers such as iPlanet Directory Server, Novell NDS or Microsoft Active Directory.

It would also be good to test with a wider variety of schemas and objects.

Expand LDAP Interfaces

Currently we use Net::LDAP to interface with the LDAP directory, but Perl/C libraries may be faster and provide different features. Once this is needed, we will probably need to create implementation-specific subclasses. This should not be very difficult -- the actual calls to Net::LDAP are minimal and straightforward.






Copyright (c) 2001-2004 MSN Marketing Service Nordwest, GmbH. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


Chris Winters <>