SPOPS::Iterator::LDAP - Implementation of SPOPS::Iterator for SPOPS::LDAP


SPOPS documentation Contained in the SPOPS distribution.

Index


Code Index:

NAME

Top

SPOPS::Iterator::LDAP - Implementation of SPOPS::Iterator for SPOPS::LDAP

SYNOPSIS

Top

  my $iter = My::SPOPS->fetch_iterator({ 
                             skip_security => 1,
                             filter => '&(objectclass=person)(mail=chris*)' });
  while ( my $person = $iter->get_next ) {
      print "Item ", $iter->position, ": $person->{first_name}: $person->{mail}",
            " (", $iter->is_first, ") (", $iter->is_last, ")\n";
  }

DESCRIPTION

Top

This is an implementation of the SPOPS::Iterator interface -- for usage guidelines please see the documentation for that module. The methods listed here are for SPOPS developers (versus SPOPS users).

METHODS

Top

initialize()

Store the Net::LDAP::Message object so we can peel off one record at a time, along with the various other pieces of information.

fetch_object()

Peel off a record, see if it fits in our min/max requirements and if this user can see it. If so return, otherwise try again.

finish()

Just clear out the Net::LDAP::Message object.

SEE ALSO

Top

SPOPS::Iterator

SPOPS::LDAP

Net::LDAP

COPYRIGHT

Top

AUTHORS

Top

Chris Winters <chris@cwinters.com>


SPOPS documentation Contained in the SPOPS distribution.

package SPOPS::Iterator::LDAP;

# $Id: LDAP.pm,v 3.3 2004/06/02 00:48:23 lachoy Exp $

use strict;
use base  qw( SPOPS::Iterator );
use Log::Log4perl qw( get_logger );

use SPOPS;
use SPOPS::Iterator qw( ITER_IS_DONE ITER_FINISHED );
use SPOPS::Secure   qw( :level );

my $log = get_logger();

$SPOPS::Iterator::LDAP::VERSION = sprintf("%d.%02d", q$Revision: 3.3 $ =~ /(\d+)\.(\d+)/);

# Keys with _LDAP at the beginning are specific to this implementation;
# keys without _LDAP at the begining are used in all iterators.

sub initialize {
    my ( $self, $p ) = @_;
    $self->{_LDAP_MSG}       = $p->{ldap_msg};

    $self->{_LDAP_OFFSET}    = $p->{offset};
    $self->{_LDAP_MAX}       = $p->{max};

    $self->{_LDAP_CONN}      = $p->{ldap};
    $self->{_LDAP_ID_LIST}   = $p->{id_list};

    $self->{_LDAP_COUNT}     = 1;
    $self->{_LDAP_RAW_COUNT} = 0;
}


# TODO [fetch_object]: Put the pre_fetch_action in here for when we
# don't have _LDAP_ID_LIST

sub fetch_object {
    my ( $self ) = @_;

    # First either grab the row with the ID if we've already got it or
    # kick the DBI statement handle for the next row

    my ( $object );
    my $object_class = $self->{_CLASS};
    if ( $self->{_LDAP_ID_LIST} ) {
        my $id = $self->{_LDAP_ID_LIST}->[ $self->{_LDAP_RAW_COUNT} ];
        $log->is_info &&
            $log->info( "Trying to retrieve idx ($self->{_LDAP_RAW_COUNT}) with ",
                          "ID ($id) from class ($self->{_CLASS}" );
        $object = eval { $object_class->fetch( $id,
                                            { skip_security => $self->{_SKIP_SECURITY} } ) };

        # If the object doesn't exist then it's likely a security issue,
        # in which case we bump up our internal count (but not the
        # position!) and try again.

        if ( $@ and $@->isa( 'SPOPS::Exception::Security' ) ) {
            $log->is_info &&
                $log->info( "Skip to next item, caught security exception: $@" );
            $self->{_LDAP_RAW_COUNT}++;
            return $self->fetch_object;
        }

        unless( $object ) {
            $log->is_info &&
                $log->info( "Iterator is depleted (no object fetched), notify parent" );
            return ITER_IS_DONE;
        }
    }
    else {
        my $entry = $self->{_LDAP_MSG}->shift_entry;
        unless ( $entry ) {
            $log->is_info &&
                $log->info( "Iterator is depleted (no entry returned), notify parent" );
            return ITER_IS_DONE;
        }

        # It's ok to create the object now

        $object = $object_class->new({ skip_default_values => 1 });
        $object->_fetch_assign_row( undef, $entry );

        # Check security on the row unless overridden. If the security
        # check fails that's ok, just skip the row and move on -- DO
        # increase our internal index but DON'T increase the position.

        $object->{tmp_security_level} = SEC_LEVEL_WRITE;
        unless ( $self->{_SKIP_SECURITY} ) {
            $object->{tmp_security_level} =
                    eval { $object->check_action_security({
                                        required => SEC_LEVEL_READ }) };
            if ( $@ ) {
                $log->is_info &&
                    $log->info( "Security check for ($self->{_CLASS}) failed: $@" );
                $self->{_LDAP_RAW_COUNT}++;
                return $self->fetch_object;
            }
        }

        # Now call the post_fetch callback; if it fails, fetch another row

        my $post_ok = $object->_fetch_post_process(
                                   {}, $object->{tmp_security_level} );
        unless ( $post_ok ) {
            $log->is_info &&
                $log->info( "Post process for object failed; get next" );
            $self->{_LDAP_RAW_COUNT}++;
            return $self->fetch_object;
        }
    }

    # Not to the offset yet, so call ourselves again to go to the next
    # row but first bump up the count.

    # Note: this *does* need to be beneath the fetch and security stuff
    # above b/c the count refers to the records this user *could*
    # see. So if 25 records matched the criteria but the user couldn't
    # see 5 of them, we only want to the user to know and iterate
    # through 1-20, not know that we actually picked the records
    # 1-10,12,14,16-22,24-25 and discarded the others due to security.

    if ( $self->{_LDAP_OFFSET} and 
         ( $self->{_LDAP_COUNT} < $self->{_LDAP_OFFSET} ) ) {
        $log->is_info &&
            $log->info( "Not reached the offset yet, get next object" );
        $self->{_LDAP_COUNT}++;
        $self->{_LDAP_RAW_COUNT}++;
        return $self->fetch_object;
    }

    # Oops, we've gone past the max. Finish up.

    if ( $self->{_LDAP_MAX} and
         ( $self->{_LDAP_COUNT} > $self->{_LDAP_MAX} ) ) {
        $log->is_info &&
            $log->info( "Fetched past the MAX number of objects, done" );
        return ITER_IS_DONE;
    }

    # Ok, we've navigated everything we need to, which means we can
    # actually return this record. So bump up both counts. Note that
    # we also use this count for the ID list.

    $self->{_LDAP_COUNT}++;
    $self->{_LDAP_RAW_COUNT}++;

    return ( $object, $self->{_LDAP_COUNT} );
}


# Might need to wrap this in an eval

sub finish {
    my ( $self ) = @_;
    undef $self->{_LDAP_MSG};
    return $self->{ ITER_FINISHED() } = 1;
}

1;

__END__