2016-05-05 22:26:41 +02:00
|
|
|
package Lemonldap::NG::Portal::Lib::LDAP;
|
2016-04-20 07:09:23 +02:00
|
|
|
|
|
|
|
use strict;
|
2017-01-15 14:18:01 +01:00
|
|
|
use Mouse;
|
|
|
|
use Lemonldap::NG::Portal::Lib::Net::LDAP;
|
2020-11-08 13:14:41 +01:00
|
|
|
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
|
2020-11-08 13:14:41 +01:00
|
|
|
);
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
extends 'Lemonldap::NG::Common::Module';
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2020-12-22 15:17:23 +01:00
|
|
|
our $VERSION = '2.0.11';
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
# PROPERTIES
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
has ldap => (
|
|
|
|
is => 'rw',
|
|
|
|
lazy => 1,
|
|
|
|
builder => 'newLdap',
|
|
|
|
);
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2019-06-17 21:14:37 +02:00
|
|
|
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;
|
2016-04-20 07:09:23 +02:00
|
|
|
|
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} }
|
|
|
|
)
|
|
|
|
)
|
2016-04-20 07:09:23 +02:00
|
|
|
{
|
2018-09-04 09:39:21 +02:00
|
|
|
$self->logger->error("LDAP initialization error: $@");
|
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 );
|
2016-04-20 07:09:23 +02:00
|
|
|
}
|
2018-09-04 07:08:46 +02:00
|
|
|
elsif ( $self->{conf}->{ldapPpolicyControl} and not $ldap->loadPP() ) {
|
|
|
|
$self->logger->error("LDAP password policy error");
|
2016-04-20 07:09:23 +02:00
|
|
|
}
|
2017-01-15 14:18:01 +01:00
|
|
|
return $ldap;
|
2016-04-20 07:09:23 +02:00
|
|
|
}
|
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
has filter => (
|
|
|
|
is => 'rw',
|
|
|
|
lazy => 1,
|
|
|
|
builder => 'buildFilter',
|
|
|
|
);
|
|
|
|
|
2018-01-22 21:57:48 +01:00
|
|
|
has mailFilter => (
|
|
|
|
is => 'rw',
|
|
|
|
lazy => 1,
|
|
|
|
builder => 'buildMailFilter',
|
|
|
|
);
|
|
|
|
|
2020-12-27 23:22:26 +01:00
|
|
|
has findUserFilter => (
|
|
|
|
is => 'ro',
|
|
|
|
lazy => 1,
|
|
|
|
builder => sub {
|
|
|
|
$_[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))' );
|
|
|
|
}
|
|
|
|
|
2018-01-22 21:57:48 +01:00
|
|
|
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);
|
2018-01-22 21:57:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 =~
|
2018-07-05 22:56:16 +02:00
|
|
|
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;
|
2016-04-20 07:09:23 +02:00
|
|
|
}
|
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
# INITIALIZATION
|
2016-04-20 07:09:23 +02:00
|
|
|
|
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");
|
2018-09-04 09:39:21 +02:00
|
|
|
$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
|
2016-04-20 07:09:23 +02:00
|
|
|
|
2019-06-17 21:14:37 +02:00
|
|
|
sub getUser {
|
|
|
|
my ( $self, $req, %args ) = @_;
|
2019-10-01 19:17:31 +02:00
|
|
|
|
2019-11-20 16:52:04 +01:00
|
|
|
$self->validateLdap;
|
2020-10-01 21:49:00 +02:00
|
|
|
return PE_LDAPCONNECTFAILED unless $self->ldap;
|
2019-11-20 16:52:04 +01:00
|
|
|
|
|
|
|
$self->bind();
|
|
|
|
|
2019-06-17 21:14:37 +02:00
|
|
|
my $mesg = $self->ldap->search(
|
|
|
|
base => $self->conf->{ldapBase},
|
|
|
|
scope => 'sub',
|
|
|
|
filter => (
|
|
|
|
$args{useMail}
|
|
|
|
? $self->mailFilter->($req)
|
|
|
|
: $self->filter->($req)
|
|
|
|
),
|
2020-09-10 08:40:23 +02:00
|
|
|
deref => $self->conf->{ldapSearchDeref} || 'find',
|
2019-06-17 21:14:37 +02:00
|
|
|
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 );
|
2019-06-17 21:14:37 +02:00
|
|
|
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 ) = @_;
|
2020-12-27 23:22:26 +01:00
|
|
|
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;
|
|
|
|
|
2020-12-27 23:22:26 +01:00
|
|
|
$self->findUserFilter =~ /\bobjectClass=(\w+)\b/;
|
2021-01-05 21:55:16 +01:00
|
|
|
my $filter = "(&(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;
|
2021-01-05 21:55:16 +01:00
|
|
|
}
|
|
|
|
else {
|
2021-01-07 09:54:00 +01:00
|
|
|
$_->{value} =~ s/\Q*\E+//g;
|
2021-01-05 21:55:16 +01:00
|
|
|
}
|
|
|
|
$filter .= "($_->{key}=$_->{value})";
|
|
|
|
}
|
2020-12-27 23:22:26 +01:00
|
|
|
$filter .= "(!($_->{key}=$_->{value}))" foreach (@$excluding);
|
|
|
|
$filter .= ')';
|
|
|
|
$self->logger->debug("LDAP UserDB built filter: $filter");
|
|
|
|
|
2020-12-20 17:31:50 +01:00
|
|
|
$self->bind();
|
2020-12-27 23:22:26 +01:00
|
|
|
my $mesg = $self->ldap->search(
|
|
|
|
base => $self->conf->{ldapBase},
|
|
|
|
scope => 'sub',
|
|
|
|
filter => $filter,
|
|
|
|
deref => $self->conf->{ldapSearchDeref} || 'find',
|
|
|
|
attrs => $self->attrs,
|
|
|
|
);
|
2021-01-02 22:50:56 +01:00
|
|
|
|
2020-12-27 23:22:26 +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() ) );
|
2020-12-27 23:22:26 +01:00
|
|
|
$self->logger->debug("Demo UserDB random rank: $rank");
|
|
|
|
my $entry =
|
|
|
|
( $mesg->entry($rank)->dn() =~ /\b(?:uid|sAMAccountName)=(\w+?)\b/ )
|
|
|
|
[0];
|
|
|
|
$self->userLogger->info("FindUser: LDAP UserDB returns $entry");
|
|
|
|
$req->data->{findUser} = $entry;
|
2021-01-03 19:00:20 +01:00
|
|
|
return PE_OK;
|
2020-12-27 23:22:26 +01:00
|
|
|
}
|
2020-12-20 17:31:50 +01:00
|
|
|
|
2021-01-03 19:00:20 +01:00
|
|
|
return PE_USERNOTFOUND;
|
2019-06-17 21:14:37 +02:00
|
|
|
}
|
|
|
|
|
2019-10-01 19:17:31 +02:00
|
|
|
# Validate LDAP connection before use
|
|
|
|
sub validateLdap {
|
|
|
|
my ($self) = @_;
|
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 );
|
|
|
|
}
|
2019-10-01 19:17:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Bind
|
|
|
|
sub bind {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
$self->validateLdap;
|
2020-10-01 21:49:00 +02:00
|
|
|
return undef unless $self->ldap;
|
2020-11-08 13:14:41 +01:00
|
|
|
|
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;
|
|
|
|
}
|
2020-12-27 23:22:26 +01:00
|
|
|
|
2017-01-15 14:18:01 +01:00
|
|
|
return 1;
|
2016-04-20 07:09:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
1;
|