PE_OK
PE_LDAPERROR
PE_USERNOTFOUND
PE_BADCREDENTIALS
PE_LDAPCONNECTFAILED
)
;
our
$VERSION
=
'2.19.0'
;
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} }
];
}
);
sub
newLdap {
my
$self
=
$_
[0];
my
$ldap
;
$self
->logger->debug(
"Try to build new LDAP connection with: $self->{conf}->{ldapServer}"
);
return
undef
unless
(
$ldap
= Lemonldap::NG::Portal::Lib::Net::LDAP->new(
{
p
=>
$self
->{p},
conf
=>
$self
->{conf} }
)
);
my
$msg
=
$ldap
->
bind
;
$self
->logger->error(
'LDAP test has failed: '
.
$msg
->error )
if
$msg
->code;
return
$ldap
;
}
has
filter
=> (
is
=>
'rw'
,
lazy
=> 1,
builder
=>
'buildFilter'
,
);
has
mailFilter
=> (
is
=>
'rw'
,
lazy
=> 1,
builder
=>
'buildMailFilter'
,
);
has
findUserFilter
=> (
is
=>
'ro'
,
lazy
=> 1,
builder
=>
sub
{
$_
[0]->conf->{AuthLDAPFilter}
||
$_
[0]->conf->{LDAPFilter}
||
'(&(uid=$user)(objectClass=inetOrgPerson))'
;
}
);
sub
buildFilter {
return
$_
[0]->_buildFilter(
$_
[0]->conf->{AuthLDAPFilter}
||
$_
[0]->conf->{LDAPFilter}
||
'(&(uid=$user)(objectClass=inetOrgPerson))'
);
}
sub
buildMailFilter {
my
$f
=
$_
[0]->conf->{mailLDAPFilter}
||
'(&(mail=$user)(objectClass=inetOrgPerson))'
;
$f
=~ s/\
$mail
\b/\
$user
/g;
return
$_
[0]->_buildFilter(
$f
);
}
sub
_buildFilter {
my
(
$self
,
$filter
) =
@_
;
my
$conf
=
$self
->{conf};
$self
->{p}->logger->debug(
"LDAP Search base: $_[0]->{conf}->{ldapBase}"
);
$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;
$self
->{p}->logger->debug(
"LDAP transformed filter: $filter"
);
$filter
=
"sub{my(\$req)=\$_[0];return \"$filter\";}"
;
my
$res
=
eval
$filter
;
$self
->error(
"Unable to build fiter: $@"
)
if
($@);
return
$res
;
}
sub
init {
my
(
$self
) =
@_
;
$self
->filter;
}
sub
getUser {
my
(
$self
,
$req
,
%args
) =
@_
;
$self
->validateLdap;
unless
(
$self
->ldap ) {
eval
{
$self
->p->_authentication->setSecurity(
$req
) };
return
PE_LDAPCONNECTFAILED;
}
unless
(
$self
->
bind
() ) {
eval
{
$self
->p->_authentication->setSecurity(
$req
) };
return
PE_LDAPERROR;
}
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 ) {
$self
->logger->error(
'LDAP Search error '
.
$mesg
->code .
': '
.
$mesg
->error );
eval
{
$self
->p->_authentication->setSecurity(
$req
) };
return
PE_LDAPERROR;
}
if
(
$mesg
->count() > 1 ) {
$self
->logger->error(
'More than one entry returned by LDAP directory'
);
eval
{
$self
->p->_authentication->setSecurity(
$req
) };
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
;
$self
->validateLdap;
return
PE_LDAPCONNECTFAILED
unless
$self
->ldap;
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
) {
$_
->{value} =~ s/\Q
$wildcard
\E+/*/g;
}
else
{
$_
->{value} =~ s/\Q*\E+//g;
}
$filter
.=
"($_->{key}=$_->{value})"
;
}
$filter
.=
"(!($_->{key}=$_->{value}))"
foreach
(
@$excluding
);
$filter
.=
')'
;
$self
->logger->debug(
"LDAP UserDB built filter: $filter"
);
$self
->
bind
();
my
$mesg
=
$self
->ldap->search(
base
=>
$self
->conf->{ldapBase},
scope
=>
'sub'
,
filter
=>
$filter
,
deref
=>
$self
->conf->{ldapSearchDeref} ||
'find'
,
attrs
=>
$self
->attrs,
sizelimit
=> 50
);
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() ) {
my
@results
=
map
{ (
$_
->dn() =~ /\b(?:uid|sAMAccountName)\x3d(.+?),/ )[0] || () }
$mesg
->entries;
if
(
@results
) {
$req
->data->{findUserAll} = \
@results
;
my
$rank
=
int
(
rand
(
@results
) );
$self
->logger->debug(
"LDAP UserDB random rank: $rank"
);
my
$entry
=
$results
[
$rank
];
$self
->userLogger->info(
"FindUser: LDAP UserDB returns $entry"
)
if
$entry
;
$req
->data->{findUser} =
$entry
;
}
return
PE_OK;
}
return
PE_USERNOTFOUND;
}
sub
validateLdap {
my
(
$self
) =
@_
;
local
$SIG
{
'PIPE'
} =
sub
{
$self
->logger->info(
'Reconnecting to LDAP server due to broken socket'
);
};
unless
(
$self
->ldap
and
$self
->ldap->root_dse(
attrs
=> [
'supportedLDAPVersion'
] ) )
{
$self
->ldap->DESTROY
if
$self
->ldap;
$self
->ldap(
$self
->newLdap );
}
}
sub
bind
{
my
$self
=
shift
;
$self
->validateLdap;
return
undef
unless
$self
->ldap;
my
$msg
=
$self
->ldap->
bind
(
@_
);
if
(
$msg
->code ) {
$self
->logger->error(
$msg
->error );
return
undef
;
}
return
1;
}
1;