Footprintless-Plugin-Ldap

 view release on metacpan or  search on metacpan

lib/Footprintless/Plugin/Ldap/Ldap.pm  view on Meta::CPAN


use parent qw(Footprintless::MixableBase);

use overload q{""} => 'to_string', fallback => 1;

use Carp;
use Footprintless::Mixins qw(
    _entity
);
use Log::Any;
use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_NO_SUCH_OBJECT);
use Net::LDAP::LDIF;
use Net::LDAP::Util;
use Time::HiRes qw(gettimeofday tv_interval);

my $logger = Log::Any->get_logger();

# https://tools.ietf.org/html/rfc2251#section-4.5.1
# If the client does not want any attributes returned, it can specify
# a list containing only the attribute with OID "1.1"
use constant NO_ATTRIBUTES => ['1.1'];

sub add {
    my ( $self, $entry ) = @_;
    croak('not connected') unless ( $self->{connection} );

    $logger->tracef( 'adding %s', $entry->dn() );
    my $message = $self->{connection}->add($entry);
    $message->code() && croak( $message->error() );

    return $self;
}

sub add_or_update {
    my ( $self, $entry ) = @_;

    eval { $self->add($entry); };
    if ($@) {
        $self->update($entry);
    }

    return $self;
}

sub base_dn {
    my ( $self, @rdns ) = @_;
    my $dn = $self->{default_base};
    foreach my $rdn (@rdns) {
        $dn = "$rdn,$dn";
    }
    return $dn;
}

sub bind {
    my ( $self, $dn, %options ) = @_;
    croak('not connected') unless ( $self->{connection} );

    if ( !$dn ) {

        # binding with instance credentials
        $dn      = $self->{bind_dn};
        %options = %{ $self->{bind_options} };
    }

    if ($dn) {
        my $result = $self->{connection}->bind( $dn, %options );
        $result->code() && croak( "unable to authenticate to $self:\n\t" . $result->error() );
    }
    else {
        my $result = $self->{connection}->bind();
        $result->code() && croak( "unable to bind anonymously:\n\t" . $result->error() );
    }

    return $self;
}

sub connect {
    my ( $self, %connect_options ) = @_;

    return if ( $self->{connection} );

    my ( $hostname, $port );
    if ( $self->{tunnel_hostname} ) {
        $self->{tunnel} = $self->{factory}->tunnel(
            $self->{coordinate},
            destination_hostname => $self->{tunnel_destination_hostname} || $self->{hostname},
            destination_port => $self->{port}
        );
        $self->{tunnel}->open();
        $hostname = $self->{tunnel}->get_local_hostname() || 'localhost';
        $port = $self->{tunnel}->get_local_port();
    }
    else {
        $hostname = $self->{hostname};
        $port     = $self->{port};
    }

    $logger->debugf( 'connecting to [%s:%s]', $hostname, $port );
    $self->{connection} = (
        $self->{secure}
        ? Net::LDAPS->new( $hostname, port => $port, %connect_options )
        : Net::LDAP->new( $hostname, port => $port, %connect_options )
        )
        || croak("unable to connect");

    if ( $self->{tunnel_hostname} ) {

        # Since tunnels give the illusion of a successful connection
        # we will attempt a bind to see if we are really connected.
        my $result = $self->{connection}->bind();
        if ( $result->code() == 1 ) {
            $self->disconnect();
            croak( "unable to connect through tunnel:\n\t" . $result->error() );
        }

        # We do not unbind since some ldap servers do not allow subsequent
        # bind operations (https://metacpan.org/pod/Net::LDAP#unbind)
    }

    return $self;



( run in 0.542 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )