FindUser with LDAP & AD & Append unit test (#1976)

This commit is contained in:
Christophe Maudoux 2020-12-27 23:22:26 +01:00
parent 072f68004a
commit 597455dfcf
14 changed files with 339 additions and 35 deletions

View File

@ -681,6 +681,7 @@ t/68-FindUser-with-Choice-and-token.t
t/68-FindUser-with-DBI.t
t/68-FindUser-with-Demo-and-token.t
t/68-FindUser-with-Demo.t
t/68-FindUser-with-LDAP.t
t/68-FindUser-without-attribute.t
t/68-FindUser-without-Impersonation.t
t/68-Impersonation-with-2F.t

View File

@ -69,6 +69,16 @@ has mailFilter => (
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}
@ -157,11 +167,47 @@ sub getUser {
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;
$self->findUserFilter =~ /\bobjectClass=(\w+)\b/;
my $filter = "(&(objectClass=$1)";
$filter .= "($_->{key}=$_->{value})" foreach (@$searching);
$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,
);
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 $rank = rand( $mesg->count() );
$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;
}
return PE_OK;
}
@ -189,7 +235,7 @@ sub bind {
$self->logger->error( $msg->error );
return undef;
}
return 1;
}

View File

@ -71,7 +71,7 @@ sub provideUser {
eval { $self->p->_authentication->setSecurity($req) };
return $self->p->do( $req, [ sub { $error } ] );
}
return $self->sendJSONresponse(
$req,
{
@ -87,10 +87,11 @@ sub retreiveFindUserParams {
my ( $searching, $excluding ) = ( [], [] );
$self->logger->debug("FindUser: reading parameters...");
foreach ( sort keys %{ $self->conf->{findUserSearchingAttributes} } ) {
if ( $req->params($_) ) {
my $param = $req->params($_) // '';
if ( $param =~ /.+/ ) {
$self->logger->debug(
"Pushing searching parameter: $_ => " . $req->params($_) );
push @$searching, { key => $_, value => $req->params($_) };
"Pushing searching parameter: $_ => $param" );
push @$searching, { key => $_, value => $param };
}
}

View File

@ -22,4 +22,14 @@ has filter => (
}
);
has findUserFilter => (
is => 'ro',
lazy => 1,
builder => sub {
$_[0]->conf->{AuthLDAPFilter}
|| $_[0]->conf->{LDAPFilter}
|| '(&(sAMAccountName=$user)(objectClass=person))';
}
);
1;

View File

@ -60,21 +60,17 @@ sub findUser {
my @args;
my $sth;
foreach (@$searching) {
if ( $_->{value} ) {
$request .= "$_->{key} = ? AND ";
push @args, $_->{value};
}
$request .= "$_->{key} = ? AND ";
push @args, $_->{value};
}
foreach (@$excluding) {
if ( $_->{value} ) {
$request .= "$_->{key} != ? AND ";
push @args, $_->{value};
}
$request .= "$_->{key} != ? AND ";
push @args, $_->{value};
}
$request =~ s/AND\s$//;
$self->logger->debug("DBI UserDB built condition: $request");
$self->logger->debug( "DBI UserDB built args: " . join '|', @args );
eval {
$sth = $self->dbh->prepare($request);
$sth->execute(@args);

View File

@ -66,7 +66,7 @@ sub getUser {
}
eval { $self->p->_authentication->setSecurity($req) };
PE_BADCREDENTIALS;
}
@ -82,15 +82,11 @@ sub findUser {
return PE_OK unless scalar @$searching;
my $cond = '';
foreach (@$searching) {
$cond .= '$' . $_->{key} . " eq '$_->{value}' && " if $_->{value};
}
foreach (@$excluding) {
$cond .= '$' . $_->{key} . " ne '$_->{value}' && " if $_->{value};
}
$cond .= '$' . $_->{key} . " eq '$_->{value}' && " foreach (@$searching);
$cond .= '$' . $_->{key} . " ne '$_->{value}' && " foreach (@$excluding);
$cond =~ s/&&\s$//;
$self->logger->debug("Demo UserDB built condition: $cond");
my @results = map {
my $uid = $demoAccounts{$_}->{uid};
my $cn = $demoAccounts{$_}->{cn};

View File

@ -33,10 +33,10 @@ SKIP: {
}
}
);
my $postString = 'user='
. ( $ENV{LDAPACCOUNT} || 'dwho' )
. '&password='
. ( $ENV{LDAPPWD} || 'dwho' );
# my $postString = 'user='
# . ( $ENV{LDAPACCOUNT} || 'dwho' )
# . '&password='
# . ( $ENV{LDAPPWD} || 'dwho' );
# Try yo authenticate
# -------------------

View File

@ -58,7 +58,7 @@ m%<input id="spoofIdfield" name="spoofId" type="text" class="form-control" value
'value=""'
) or explain( $res->[2]->[0], 'value=""' );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'guy', 'cn', 'token' );
expectForm( $res, '#', undef, 'uid', 'guy', 'cn', 'token' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
$query =~ s/2_ssl/1_demo/;

View File

@ -60,7 +60,7 @@ count(3);
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Portal', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'guy', 'cn', 'token' );
expectForm( $res, '#', undef, 'uid', 'guy', 'cn', 'token' );
Time::Fake->offset("+150s");
$query =~ s/uid=/uid=dwho/;
ok(

View File

@ -76,9 +76,9 @@ SKIP: {
'Post empty FindFuser request'
);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'uid', 'password', 'spoofId' );
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'guy', 'cn' );
expectForm( $res, '#', undef, 'uid', 'guy', 'cn' );
ok(
$res->[2]->[0] =~
m%<input id="spoofIdfield" name="spoofId" type="text" class="form-control" value="" autocomplete="off"%,
@ -133,7 +133,7 @@ m%<input id="findUser_cn" name="cn" type="text" autocomplete="off" class="form-c
ok( $json->{user} eq 'dwho', ' Good user' )
or explain( $json, 'user => dwho' );
$request = 'user=ohwd';
$request = 'uid=ohwd';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),

View File

@ -49,7 +49,7 @@ count(3);
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Portal', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'guy', 'cn', 'token' );
expectForm( $res, '#', undef, 'uid', 'guy', 'cn', 'token' );
Time::Fake->offset("+150s");
$query =~ s/uid=/uid=dwho/;
ok(
@ -113,6 +113,7 @@ ok(
'/finduser',
IO::String->new($query),
accept => 'application/json',
length => length($query),
cookie => "lemonldap=$id"
),
'Get findUser'

View File

@ -42,8 +42,8 @@ ok(
'Post empty FindFuser request'
);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'uid', 'password', 'spoofId' );
( $host, $url, $query ) = expectForm( $res, '#', undef, 'user', 'guy', 'cn' );
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
( $host, $url, $query ) = expectForm( $res, '#', undef, 'uid', 'guy', 'cn' );
ok(
$res->[2]->[0] =~
m%<input id="spoofIdfield" name="spoofId" type="text" class="form-control" value="" autocomplete="off"%,
@ -98,7 +98,7 @@ ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
ok( $json->{user} eq 'dwho', ' Good user' )
or explain( $json, 'user => dwho' );
$request = 'user=ohwd';
$request = 'uid=ohwd';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),

View File

@ -0,0 +1,241 @@
use Test::More;
use strict;
use JSON;
use IO::String;
require 't/test-lib.pm';
my $res;
my $json;
my $request;
my $maintests = 41;
SKIP: {
skip 'LLNGTESTLDAP is not set', $maintests unless ( $ENV{LLNGTESTLDAP} );
require 't/test-ldap.pm';
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'LDAP',
portal => 'http://auth.example.com/',
userDB => 'Same',
ldapServer => 'ldap://127.0.0.1:19389/',
ldapBase => 'ou=users,dc=example,dc=com',
managerDn => 'cn=admin,dc=example,dc=com',
managerPassword => 'admin',
ldapExportedVars => {
uid => 'uid',
cn => 'cn',
sn => 'sn',
displayName => 'displayName',
roomNumber => 'roomNumber',
mail => 'mail'
},
requireToken => 0,
findUser => 1,
impersonationRule => 1,
findUserSearchingAttributes =>
{ uid => 'Login', roomNumber => 'Room', cn => 'Name' },
findUserExcludingAttributes =>
{ mail => 'french@badwolf.org', uid => 'russian' },
}
}
);
## Simple access
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Portal', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
$request = '';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'text/html',
length => length($request)
),
'Post empty FindFuser request'
);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'uid', 'roomNumber', 'cn' );
ok(
$res->[2]->[0] =~
m%<input id="spoofIdfield" name="spoofId" type="text" class="form-control" value="" autocomplete="off"%,
'value=""'
) or explain( $res->[2]->[0], 'value=""' );
$request = 'uid=dwho';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'text/html',
length => length($request)
),
'Post FindFuser request'
);
ok( $res->[2]->[0] =~ m%value="dwho"%, 'value="dwho"' )
or explain( $res->[2]->[0], 'value="dwho"' );
ok( $res->[2]->[0] =~ m%autocomplete="off"%, 'autocomplete="off"' )
or explain( $res->[2]->[0], 'autocomplete="off"' );
ok(
$res->[2]->[0] =~
m%<span trspan="searchAccount">Search for an account</span>%,
'Search an account'
) or explain( $res->[2]->[0], 'Search for an account' );
ok(
$res->[2]->[0] =~
m%<input id="findUser_roomNumber" name="roomNumber" type="text" autocomplete="off" class="form-control" placeholder="Room" />%,
'id="findUser_roomNumber"'
) or explain( $res->[2]->[0], 'id="findUser_roomNumber"' );
ok(
$res->[2]->[0] =~
m%<input id="findUser_uid" name="uid" type="text" autocomplete="off" class="form-control" placeholder="Login" />%,
'id="findUser_uid"'
) or explain( $res->[2]->[0], 'id="findUser_uid"' );
ok(
$res->[2]->[0] =~
m%<input id="findUser_cn" name="cn" type="text" autocomplete="off" class="form-control" placeholder="Name" />%,
'id="findUser_cn"'
) or explain( $res->[2]->[0], 'id="findUser_cn"' );
$request = 'uid=dwho';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{user} eq 'dwho', ' Good user' )
or explain( $json, 'user => dwho' );
$request = 'uid=ohwd';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with bad user'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{user} eq '', ' No user' )
or explain( $json, "user => ''" );
$request = 'cn=Rose Tyler';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request one result'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{user} eq 'rtyler', ' Good user' )
or explain( $json, "user => 'rtyler'" );
$request = 'roomNumber=0';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request multi results'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{user} =~ /^(dwho|french|reset)$/, " Good user ($1)" )
or explain( $json, "user => $1" );
$request = 'arg=1';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with bad arg'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{result} == 1, ' Good result' )
or explain( $json, 'result => 1' );
ok( $json->{user} eq '', ' No user' )
or explain( $json, 'user => ?' );
$request = 'roomNumber=0&uid=dwho';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with two args'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{user} eq 'dwho', ' Good user' )
or explain( $json, 'user => dwho' );
$request = 'roomNumber=0&uid=rtyler';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with wrong args'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{result} == 1, ' Good result' )
or explain( $json, 'result => 1' );
ok( $json->{user} eq '', ' No user' )
or explain( $json, 'user => ?' );
$request = 'uid=russian';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with excluding result'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{result} == 1, ' Good result' )
or explain( $json, 'result => 1' );
ok( $json->{user} eq '', ' No user' )
or explain( $json, 'user => ?' );
$request = 'uid=french';
ok(
$res = $client->_post(
'/finduser', IO::String->new($request),
accept => 'application/json',
length => length($request)
),
'Post FindFuser request with excluding result'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{result} == 1, ' Good result' )
or explain( $json, 'result => 1' );
ok( $json->{user} eq '', ' No user' )
or explain( $json, 'user => ?' );
clean_sessions();
}
count($maintests);
stopLdapServer() if $ENV{LLNGTESTLDAP};
done_testing( count() );

View File

@ -29,6 +29,16 @@ cn: Dr Who
sn: Who
mail: dwho@badwolf.org
userPassword: dwho
roomNumber: 0
dn: uid=rtyler,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
uid: rtyler
cn: Rose Tyler
sn: Rtyler
mail: rtyler@badwolf.org
userPassword: rtyler
roomNumber: 1
dn: uid=french,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
@ -38,6 +48,7 @@ sn: Accents
mail: french@badwolf.org
userPassword: french
displayName:: RnLDqWTDqXJpYyBBY2NlbnRz
roomNumber: 0
dn: uid=russian,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
@ -57,6 +68,7 @@ mail: reset@badwolf.org
userPassword: reset
pwdPolicySubentry: cn=passwordreset,ou=ppolicies,dc=example,dc=com
pwdReset: TRUE
roomNumber: 0
dn: uid=expire,ou=users,dc=example,dc=com
objectClass: inetOrgPerson