lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/LDAP.pm

262 lines
6.5 KiB
Perl
Raw Normal View History

2016-05-05 22:26:41 +02:00
package Lemonldap::NG::Portal::Lib::LDAP;
use strict;
2017-01-15 14:18:01 +01:00
use Mouse;
use Lemonldap::NG::Portal::Lib::Net::LDAP;
use Lemonldap::NG::Portal::Main::Constants qw(
2021-01-03 19:00:20 +01:00
PE_OK
PE_LDAPERROR
PE_USERNOTFOUND
PE_BADCREDENTIALS
PE_LDAPCONNECTFAILED
);
2017-01-15 14:18:01 +01:00
extends 'Lemonldap::NG::Common::Module';
our $VERSION = '2.0.14';
2017-01-15 14:18:01 +01:00
# PROPERTIES
2017-01-15 14:18:01 +01:00
has ldap => (
is => 'rw',
lazy => 1,
builder => 'newLdap',
);
has attrs => (
is => 'rw',
lazy => 1,
builder => sub {
return [
values %{ $_[0]->{conf}->{exportedVars} },
values %{ $_[0]->{conf}->{ldapExportedVars} }
];
}
);
2017-01-15 14:18:01 +01:00
sub newLdap {
my $self = $_[0];
my $ldap;
2017-01-15 14:18:01 +01:00
# Build object and test LDAP connexion
2018-09-04 07:08:46 +02:00
unless (
2017-01-15 14:18:01 +01:00
$ldap = Lemonldap::NG::Portal::Lib::Net::LDAP->new(
{ p => $self->{p}, conf => $self->{conf} }
)
)
{
2018-09-04 07:08:46 +02:00
return undef;
}
# Test connection
my $msg = $ldap->bind;
if ( $msg->code ) {
$self->logger->error( 'LDAP test has failed: ' . $msg->error );
}
2017-01-15 14:18:01 +01:00
return $ldap;
}
2017-01-15 14:18:01 +01:00
has filter => (
is => 'rw',
lazy => 1,
builder => 'buildFilter',
);
has mailFilter => (
is => 'rw',
lazy => 1,
builder => 'buildMailFilter',
);
has findUserFilter => (
is => 'ro',
lazy => 1,
builder => sub {
2022-02-16 17:43:29 +01:00
$_[0]->conf->{AuthLDAPFilter}
|| $_[0]->conf->{LDAPFilter}
|| '(&(uid=$user)(objectClass=inetOrgPerson))';
}
);
2018-01-24 22:32:08 +01:00
sub buildFilter {
return $_[0]->_buildFilter( $_[0]->conf->{AuthLDAPFilter}
|| $_[0]->conf->{LDAPFilter}
|| '(&(uid=$user)(objectClass=inetOrgPerson))' );
}
sub buildMailFilter {
2018-01-24 22:32:08 +01:00
my $f = $_[0]->conf->{mailLDAPFilter}
|| '(&(mail=$user)(objectClass=inetOrgPerson))';
$f =~ s/\$mail\b/\$user/g;
return $_[0]->_buildFilter($f);
}
sub _buildFilter {
2018-09-04 07:08:46 +02:00
my ( $self, $filter ) = @_;
my $conf = $self->{conf};
$self->{p}->logger->debug("LDAP Search base: $_[0]->{conf}->{ldapBase}");
2017-01-15 14:18:01 +01:00
$filter =~ s/"/\\"/g;
$filter =~ s/\$(\w+)/".\$req->{sessionInfo}->{$1}."/g;
$filter =~ s/\$req->\{sessionInfo\}->\{user\}/\$req->{user}/g;
$filter =~
s/\$req->\{sessionInfo\}->\{(_?password|mail)\}/\$req->{data}->{$1}/g;
2018-09-04 07:08:46 +02:00
$self->{p}->logger->debug("LDAP transformed filter: $filter");
2017-01-15 14:18:01 +01:00
$filter = "sub{my(\$req)=\$_[0];return \"$filter\";}";
2018-09-04 07:08:46 +02:00
my $res = eval $filter;
if ($@) {
$self->error("Unable to build fiter: $@");
}
return $res;
}
2017-01-15 14:18:01 +01:00
# INITIALIZATION
2017-01-15 14:18:01 +01:00
sub init {
my ($self) = @_;
2018-09-04 09:50:48 +02:00
$self->ldap
or $self->logger->error(
"LDAP initialization has failed, but let's continue");
$self->filter;
2017-01-15 14:18:01 +01:00
}
2017-02-15 07:41:50 +01:00
2017-01-15 14:18:01 +01:00
# RUNNING METHODS
sub getUser {
my ( $self, $req, %args ) = @_;
$self->validateLdap;
2020-10-01 21:49:00 +02:00
return PE_LDAPCONNECTFAILED unless $self->ldap;
return PE_LDAPERROR unless $self->bind();
my $mesg = $self->ldap->search(
base => $self->conf->{ldapBase},
scope => 'sub',
filter => (
$args{useMail}
? $self->mailFilter->($req)
: $self->filter->($req)
),
deref => $self->conf->{ldapSearchDeref} || 'find',
attrs => $self->attrs,
);
if ( $mesg->code() != 0 ) {
2019-09-30 17:19:57 +02:00
$self->logger->error(
'LDAP Search error ' . $mesg->code . ": " . $mesg->error );
return PE_LDAPERROR;
}
if ( $mesg->count() > 1 ) {
$self->logger->error('More than one entry returned by LDAP directory');
eval { $self->p->_authentication->setSecurity($req) };
2020-12-20 17:31:50 +01:00
return PE_BADCREDENTIALS;
}
unless ( $req->data->{ldapentry} = $mesg->entry(0) ) {
$self->userLogger->warn(
"$req->{user} was not found in LDAP directory ("
. $req->address
. ")" );
eval { $self->p->_authentication->setSecurity($req) };
return PE_BADCREDENTIALS;
}
$req->data->{dn} = $req->data->{ldapentry}->dn();
return PE_OK;
}
sub findUser {
my ( $self, $req, %args ) = @_;
my $plugin =
$self->p->loadedModules->{"Lemonldap::NG::Portal::Plugins::FindUser"};
my ( $searching, $excluding ) = $plugin->retreiveFindUserParams($req);
eval { $self->p->_authentication->setSecurity($req) };
return PE_OK unless scalar @$searching;
2020-12-20 17:31:50 +01:00
$self->validateLdap;
return PE_LDAPCONNECTFAILED unless $self->ldap;
2021-08-12 21:04:30 +02:00
my $filter =
$self->findUserFilter =~ /\bobjectClass=(\w+)\b/
? "(&(objectClass=$1)"
: '(&';
my $wildcard = $self->conf->{findUserWildcard};
$self->logger->info("LDAP UserDB with wildcard ($wildcard)") if $wildcard;
foreach (@$searching) {
if ($wildcard) {
2021-01-07 09:54:00 +01:00
$_->{value} =~ s/\Q$wildcard\E+/*/g;
}
else {
2021-01-07 09:54:00 +01:00
$_->{value} =~ s/\Q*\E+//g;
}
$filter .= "($_->{key}=$_->{value})";
}
$filter .= "(!($_->{key}=$_->{value}))" foreach (@$excluding);
$filter .= ')';
$self->logger->debug("LDAP UserDB built filter: $filter");
2020-12-20 17:31:50 +01:00
$self->bind();
my $mesg = $self->ldap->search(
2021-08-12 21:04:30 +02:00
base => $self->conf->{ldapBase},
scope => 'sub',
filter => $filter,
deref => $self->conf->{ldapSearchDeref} || 'find',
attrs => $self->attrs,
sizelimit => 50
);
2021-01-02 22:50:56 +01:00
if ( $mesg->code() != 0 ) {
$self->logger->error(
'LDAP Search error ' . $mesg->code . ": " . $mesg->error );
return PE_LDAPERROR;
}
$self->logger->debug(
'LDAP UserDB number of result(s): ' . $mesg->count() );
if ( $mesg->count() ) {
2021-01-02 22:50:56 +01:00
my $rank = int( rand( $mesg->count() ) );
$self->logger->debug("Demo UserDB random rank: $rank");
my $entry =
2021-08-12 21:04:30 +02:00
( $mesg->entry($rank)->dn() =~ /\b(?:uid|sAMAccountName)\x3d(.+?),/ )
[0] || '';
$self->userLogger->info("FindUser: LDAP UserDB returns $entry")
if $entry;
$req->data->{findUser} = $entry;
2021-01-03 19:00:20 +01:00
return PE_OK;
}
2020-12-20 17:31:50 +01:00
2021-01-03 19:00:20 +01:00
return PE_USERNOTFOUND;
}
# Validate LDAP connection before use
sub validateLdap {
my ($self) = @_;
local $SIG{'PIPE'} = sub {
$self->logger->info("Reconnecting to LDAP server due to broken socket");
};
2017-01-15 14:18:01 +01:00
unless ($self->ldap
and $self->ldap->root_dse( attrs => ['supportedLDAPVersion'] ) )
{
2018-03-13 14:43:12 +01:00
$self->ldap->DESTROY if ( $self->ldap );
2017-01-15 14:18:01 +01:00
$self->ldap( $self->newLdap );
}
}
# Bind
sub bind {
my $self = shift;
$self->validateLdap;
2020-10-01 21:49:00 +02:00
return undef unless $self->ldap;
2017-01-15 14:18:01 +01:00
my $msg = $self->ldap->bind(@_);
if ( $msg->code ) {
2017-02-15 07:41:50 +01:00
$self->logger->error( $msg->error );
2017-01-15 14:18:01 +01:00
return undef;
}
2017-01-15 14:18:01 +01:00
return 1;
}
1;