NAME

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

SYNOPSIS

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' );
    $ldap->bind;
    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=*cwinters.com)';
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=*cwinters.com)';
my $iter = My::LDAP->fetch_iterator({ where => $ldap_filter });
while ( my $object = $iter->get_next ) {
    print "Name: $object->{givenname} at $object->{mail}\n";
}

DESCRIPTION

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.)

CONFIGURATION

Configuration of an SPOPS::LDAP data object is similar to that of other SPOPS objects, with a few modifications.

  • isa (\@)

    Same as a normal SPOPS field, but it must have SPOPS::LDAP in it.

  • base_dn ($)

    DN in an LDAP tree where this object is located. For instance, the common 'inetOrgPerson' type of object might be located under:

    base_dn  => 'ou=People,dc=MyCompany,dc=com'

    While 'printer' objects might be located under:

    base_dn  => 'ou=Equipment,dc=MyCompany,dc=com'

    Note that SPOPS::LDAP::MultiDatasource allows you to specify a partial DN on a per-datasource basis.

  • ldap_object_class (\@)

    When you create a new object you can specify the LDAP object class yourself when creating the object or SPOPS::LDAP can do it for you behind the scenes. If you specify one or more LDAP object class strings here they will be used whenever you create a new object and save it.

    Example:

    ldap_object_class => [ 'top', 'person', 'inetOrgPerson',
                           'organizationalPerson' ]
  • ldap_fetch_object_class ($) (optional)

    Specify an objectclass here to ensure your results are restricted properly. This is also used to do an 'empty' search and find all records of a particular class.

    NOTE: This is only used with the fetch_group() and fetch_iterator() methods.

    Example:

    ldap_fetch_object_class => 'person'
  • multivalue (\@) (optional)

    You must list the fields here that may have multiple values in the directory. Otherwise the object will have only one of the values and, on saving the object, will probably wipe out all the others.

    Example:

    multivalue  => [ 'objectclass', 'cn' ]
  • id_value_field ($) (optional)

    Returns the field used for the ID value (a string) in this object. By default this is the value stored in 'id_field', but there are cases where you may wish to use a particular fieldname for the DN of an object and the value from another field.

METHODS

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

class_initialize()

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

build_dn()

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.

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.

BUGS

Renaming of DNs not supported

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

TO DO

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.

SEE ALSO

Net::LDAP

SPOPS::Iterator::LDAP

SPOPS

COPYRIGHT

Copyright (c) 2001 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.

AUTHORS

Chris Winters <chris@cwinters.com>