From 53e16eca8c3745c4d4772bff54aca3aa94b69c21 Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Sat, 23 May 2020 23:02:37 +0200 Subject: [PATCH] Append unit tests (#2207) --- lemonldap-ng-portal/MANIFEST | 3 + .../NG/Portal/Plugins/ContextSwitching.pm | 10 +- .../t/02-Password-Demo-Local-noPpolicy.t | 1 - .../t/67-CheckUser-with-UnrestrictedUser.t | 229 +++++++++++++++ .../t/68-ContextSwitching-with-Logout.t | 1 - ...8-ContextSwitching-with-UnrestrictedUser.t | 263 ++++++++++++++++++ .../t/68-Impersonation-with-History.t | 18 +- .../68-Impersonation-with-UnrestrictedUser.t | 119 ++++++++ 8 files changed, 627 insertions(+), 17 deletions(-) create mode 100644 lemonldap-ng-portal/t/67-CheckUser-with-UnrestrictedUser.t create mode 100644 lemonldap-ng-portal/t/68-ContextSwitching-with-UnrestrictedUser.t create mode 100644 lemonldap-ng-portal/t/68-Impersonation-with-UnrestrictedUser.t diff --git a/lemonldap-ng-portal/MANIFEST b/lemonldap-ng-portal/MANIFEST index f4b9be8ca..16f39f0b8 100644 --- a/lemonldap-ng-portal/MANIFEST +++ b/lemonldap-ng-portal/MANIFEST @@ -651,16 +651,19 @@ t/67-CheckUser-with-Global-token.t t/67-CheckUser-with-Impersonation-and-Macros.t t/67-CheckUser-with-issuer-SAML-POST.t t/67-CheckUser-with-token.t +t/67-CheckUser-with-UnrestrictedUser.t t/67-CheckUser.t t/68-ContextSwitching-with-Impersonation.t t/68-ContextSwitching-with-Logout.t t/68-ContextSwitching-with-TOTP-and-Notification.t +t/68-ContextSwitching-with-UnrestrictedUser.t t/68-ContextSwitching.t t/68-Impersonation-with-doubleCookies.t t/68-Impersonation-with-filtered-merge.t t/68-Impersonation-with-History.t t/68-Impersonation-with-merge.t t/68-Impersonation-with-TOTP.t +t/68-Impersonation-with-UnrestrictedUser.t t/68-Impersonation.t t/70-2F-TOTP-8-with-global-storage.t t/70-2F-TOTP-and-U2F-with-TTL-and-JSON.t diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm index 003b5ccb7..a6beccba2 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm @@ -168,7 +168,7 @@ sub run { # ContextSwitching required -> Check user Id if ( $spoofId && $spoofId ne $req->{user} ) { - $self->logger->debug("Spoof Id: $spoofId"); + $self->logger->debug("Spoofed Id: $spoofId"); unless ( $spoofId =~ /$self->{conf}->{userControl}/o ) { $self->userLogger->warn('Malformed spoofed Id'); $self->logger->debug( @@ -191,7 +191,7 @@ sub run { # Main session $self->p->updateSession( $req, $req->sessionInfo ); $self->userLogger->notice( - "ContextSwitching: Update $realId session with $spoofId session data"); + "ContextSwitching: Update \"$realId\" session with \"$spoofId\" session data"); return $self->p->do( $req, [ sub { $statut } ] ); } @@ -255,7 +255,7 @@ sub _switchContext { } $self->userLogger->notice( - "Start ContextSwitching: $realId becomes $spoofId "); + "Start ContextSwitching: \"$realId\" becomes \"$spoofId\""); return $req; } @@ -272,7 +272,7 @@ sub _abortImpersonation { if ($abort) { $self->userLogger->notice( - "Abort ContextSwitching: $spoofId by $realId"); + "Abort ContextSwitching: \"$spoofId\" by \"$realId\""); if ( my $abortSession = $self->p->getApacheSession( $req->id ) ) { $abortSession->remove; } @@ -283,7 +283,7 @@ sub _abortImpersonation { } else { $self->userLogger->notice( - "Stop ContextSwitching for $realId with uid $spoofId"); + "Stop ContextSwitching for \"$realId\" with uid \"$spoofId\""); $self->p->deleteSession($req); } diff --git a/lemonldap-ng-portal/t/02-Password-Demo-Local-noPpolicy.t b/lemonldap-ng-portal/t/02-Password-Demo-Local-noPpolicy.t index 388854a33..1e2e7fdf9 100644 --- a/lemonldap-ng-portal/t/02-Password-Demo-Local-noPpolicy.t +++ b/lemonldap-ng-portal/t/02-Password-Demo-Local-noPpolicy.t @@ -1,7 +1,6 @@ use Test::More; use strict; use IO::String; -use JSON; use Lemonldap::NG::Portal::Main::Constants qw( PE_PP_PASSWORD_TOO_SHORT PE_PP_INSUFFICIENT_PASSWORD_QUALITY PE_PP_NOT_ALLOWED_CHARACTER PE_PP_NOT_ALLOWED_CHARACTERS diff --git a/lemonldap-ng-portal/t/67-CheckUser-with-UnrestrictedUser.t b/lemonldap-ng-portal/t/67-CheckUser-with-UnrestrictedUser.t new file mode 100644 index 000000000..f26975d86 --- /dev/null +++ b/lemonldap-ng-portal/t/67-CheckUser-with-UnrestrictedUser.t @@ -0,0 +1,229 @@ +use Test::More; +use strict; +use IO::String; +use JSON qw(to_json from_json); + +BEGIN { + require 't/test-lib.pm'; +} + +my $res; + +my $client = LLNG::Manager::Test->new( { + ini => { + logLevel => 'error', + authentication => 'Demo', + userDB => 'Same', + loginHistoryEnabled => 0, + brutForceProtection => 0, + checkUser => 1, + checkUserIdRule => '$uid ne "rtyler"', + checkUserUnrestrictedUsersRule => '$uid eq "msmith"', + tokenUseGlobalStorage => 0, + checkUserDisplayPersistentInfo => 0, + checkUserDisplayEmptyValues => 0, + impersonationMergeSSOgroups => 0, + } + } +); + +## Try to authenticate +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +count(1); +my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'user', 'password' ); + +$query =~ s/user=/user=dwho/; +$query =~ s/password=/password=dwho/; +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +count(1); + +my $id = expectCookie($res); +expectRedirection( $res, 'http://auth.example.com/' ); + +# CheckUser form +# ------------------------ +ok( + $res = $client->_get( + '/checkuser', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'CheckUser form', +); +count(1); +( $host, $url, $query ) = + expectForm( $res, undef, '/checkuser', 'user', 'url' ); +ok( $res->[2]->[0] =~ m%%, 'Found trspan="checkUser"' ) + or explain( $res->[2]->[0], 'trspan="checkUser"' ); +count(1); + +# Try checkUser with an allowed identity +$query =~ s/user=dwho/user=msmith/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'checkUserComputeSession', 'Computed session' ) + or print STDERR Dumper($res); +count(2); + +# Try checkUser with a forbidden identity +$query =~ s/user=msmith/user=rtyler/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'PE5', 'BADCREDENTIALS' ) + or print STDERR Dumper($res); +count(2); + +# Try to authenticate with rtyler +ok( + $res = $client->_post( + '/', + IO::String->new('user=rtyler&password=rtyler'), + length => 27 + ), + 'Auth query' +); +count(1); +expectOK($res); +my $id2 = expectCookie($res); + +# Try chckUser with a forbidden identity existing in DB +$query =~ s/user=msmith/user=rtyler/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'PE5', 'BADCREDENTIALS' ) + or print STDERR Dumper($res); +count(2); + +# Try to authenticate with msmith +ok( + $res = $client->_post( + '/', + IO::String->new('user=msmith&password=msmith'), + length => 27 + ), + 'Auth query' +); +count(1); +expectOK($res); +$id = expectCookie($res); + +# CheckUser form +# ------------------------ +ok( + $res = $client->_get( + '/checkuser', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'CheckUser form', +); +count(1); +( $host, $url, $query ) = + expectForm( $res, undef, '/checkuser', 'user', 'url' ); +ok( $res->[2]->[0] =~ m%%, 'Found trspan="checkUser"' ) + or explain( $res->[2]->[0], 'trspan="checkUser"' ); +count(1); + +# Try checkUser with an allowed identity +$query =~ s/user=msmith/user=dwho/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'checkUser', 'SSO session' ) + or print STDERR Dumper($res); +count(2); + +# Try checkUser with a forbidden identity existing in DB +$query =~ s/user=dwho/user=rtyler/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'checkUser', 'SSO session' ) + or print STDERR Dumper($res); +count(2); + +$client->logout($id2); + +# Try checkUser with a forbidden identity +$query =~ s/user=dwho/user=rtyler/; +ok( + $res = $client->_post( + '/checkuser', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + ), + 'POST checkuser' +); +count(1); + +ok( $res = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $res->{MSG} eq 'checkUserComputeSession', 'Computed session' ) + or print STDERR Dumper($res); +count(2); + +$client->logout($id); +clean_sessions(); +done_testing( count() ); diff --git a/lemonldap-ng-portal/t/68-ContextSwitching-with-Logout.t b/lemonldap-ng-portal/t/68-ContextSwitching-with-Logout.t index 433e3f2bd..904995c29 100644 --- a/lemonldap-ng-portal/t/68-ContextSwitching-with-Logout.t +++ b/lemonldap-ng-portal/t/68-ContextSwitching-with-Logout.t @@ -217,5 +217,4 @@ ok( $res->[2]->[0] =~ /trmsg="47"/, 'Found logout message' ); count(2); clean_sessions(); - done_testing( count() ); diff --git a/lemonldap-ng-portal/t/68-ContextSwitching-with-UnrestrictedUser.t b/lemonldap-ng-portal/t/68-ContextSwitching-with-UnrestrictedUser.t new file mode 100644 index 000000000..767496593 --- /dev/null +++ b/lemonldap-ng-portal/t/68-ContextSwitching-with-UnrestrictedUser.t @@ -0,0 +1,263 @@ +use Test::More; +use strict; +use IO::String; +use JSON qw(to_json from_json); + +BEGIN { + require 't/test-lib.pm'; +} + +my $res; + +my $client = LLNG::Manager::Test->new( { + ini => { + logLevel => 'error', + authentication => 'Demo', + userDB => 'Same', + https => 0, + loginHistoryEnabled => 0, + brutForceProtection => 0, + portalMainLogo => 'common/logos/logo_llng_old.png', + requireToken => 0, + checkUser => 0, + securedCookie => 0, + checkUserDisplayPersistentInfo => 0, + checkUserDisplayEmptyValues => 0, + contextSwitchingRule => 1, + contextSwitchingStopWithLogout => 0, + contextSwitchingIdRule => '$uid ne "msmith"', + contextSwitchingUnrestrictedUsersRule => '$uid eq "dwho"', + } + } +); + +## Try to authenticate +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +count(1); +my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'user', 'password' ); + +$query =~ s/user=/user=rtyler/; +$query =~ s/password=/password=rtyler/; +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +count(1); +my $id = expectCookie($res); +expectRedirection( $res, 'http://auth.example.com/' ); + +# Get Menu +# ------------------------ +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'Get Menu', +); +count(1); +expectOK($res); +ok( + $res->[2]->[0] =~ m%Connected as rtyler%, + 'Connected as rtyler' +) or print STDERR Dumper( $res->[2]->[0] ); +expectAuthenticatedAs( $res, 'rtyler' ); +ok( + $res->[2]->[0] =~ + m%contextSwitching_ON%, + 'Connected as rtyler' +) or print STDERR Dumper( $res->[2]->[0] ); +count(2); + +# ContextSwitching form +# ------------------------ +ok( + $res = $client->_get( + '/switchcontext', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'ContextSwitching form', +); + +( $host, $url, $query ) = + expectForm( $res, undef, '/switchcontext', 'spoofId' ); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_ON"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' ); +count(2); + +## POST form +$query =~ s/spoofId=/spoofId=dwho/; +ok( + $res = $client->_post( + '/switchcontext', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + accept => 'text/html', + ), + 'POST switchcontext' +); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_OFF"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' ); +my $id2 = expectCookie($res); + +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id2", + accept => 'text/html' + ), + 'Get Menu', +); +expectAuthenticatedAs( $res, 'dwho' ); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_OFF"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' ); +count(4); + +# Stop ContextSwitching +# ------------------------ +ok( + $res = $client->_get( + '/switchcontext', + cookie => "lemonldap=$id2", + accept => 'text/html' + ), + 'Stop context switching', +); +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id2", + accept => 'text/html' + ), + 'Get Menu', +); +ok( $res->[2]->[0] =~ m%%, 'SESSIONEXPIRED' ) + or explain( $res->[2]->[0], 'SESSIONEXPIRED' ); +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'Get Menu', +); +expectAuthenticatedAs( $res, 'rtyler' ); +count(4); + +# ContextSwitching form +# ------------------------ +ok( + $res = $client->_get( + '/switchcontext', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'ContextSwitching form', +); + +( $host, $url, $query ) = + expectForm( $res, undef, '/switchcontext', 'spoofId' ); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_ON"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' ); +count(2); + +## POST form +$query =~ s/spoofId=/spoofId=msmith/; +ok( + $res = $client->_post( + '/switchcontext', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + accept => 'text/html', + ), + 'POST switchcontext' +); +ok( $res->[2]->[0] =~ m%%, 'MALFORMEDUSER' ) + or explain( $res->[2]->[0], 'MALFORMEDUSER' ); +count(2); + +## Try to authenticate with an unresticted user +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +( $host, $url, $query ) = expectForm( $res, '#', undef, 'user', 'password' ); + +$query =~ s/user=/user=dwho/; +$query =~ s/password=/password=dwho/; +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +count(2); +$id = expectCookie($res); +expectRedirection( $res, 'http://auth.example.com/' ); + +# ContextSwitching form +# ------------------------ +ok( + $res = $client->_get( + '/switchcontext', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'ContextSwitching form', +); + +( $host, $url, $query ) = + expectForm( $res, undef, '/switchcontext', 'spoofId' ); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_ON"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' ); +count(2); + +## POST form with a forbidden identity +$query =~ s/spoofId=/spoofId=msmith/; +ok( + $res = $client->_post( + '/switchcontext', + IO::String->new($query), + cookie => "lemonldap=$id", + length => length($query), + accept => 'text/html', + ), + 'POST switchcontext' +); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_OFF"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' ); +$id2 = expectCookie($res); +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id2", + accept => 'text/html' + ), + 'Get Menu', +); +expectAuthenticatedAs( $res, 'msmith' ); +ok( $res->[2]->[0] =~ m%%, + 'Found trspan="contextSwitching_OFF"' ) + or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' ); +count(4); + +$client->logout($id); +$client->logout($id2); + +clean_sessions(); +done_testing( count() ); diff --git a/lemonldap-ng-portal/t/68-Impersonation-with-History.t b/lemonldap-ng-portal/t/68-Impersonation-with-History.t index 740a4e415..a93e5cb96 100644 --- a/lemonldap-ng-portal/t/68-Impersonation-with-History.t +++ b/lemonldap-ng-portal/t/68-Impersonation-with-History.t @@ -10,16 +10,14 @@ my $res; my $client = LLNG::Manager::Test->new( { ini => { - logLevel => 'error', - authentication => 'Demo', - userDB => 'Same', - loginHistoryEnabled => 1, - brutForceProtection => 0, - portalMainLogo => 'common/logos/logo_llng_old.png', - requireToken => 0, - impersonationRule => 1, - checkUserDisplayPersistentInfo => 0, - checkUserDisplayEmptyValues => 0, + logLevel => 'error', + authentication => 'Demo', + userDB => 'Same', + loginHistoryEnabled => 1, + brutForceProtection => 0, + portalMainLogo => 'common/logos/logo_llng_old.png', + requireToken => 0, + impersonationRule => 1, } } ); diff --git a/lemonldap-ng-portal/t/68-Impersonation-with-UnrestrictedUser.t b/lemonldap-ng-portal/t/68-Impersonation-with-UnrestrictedUser.t new file mode 100644 index 000000000..ba03d2ba6 --- /dev/null +++ b/lemonldap-ng-portal/t/68-Impersonation-with-UnrestrictedUser.t @@ -0,0 +1,119 @@ +use Test::More; +use strict; +use IO::String; + +BEGIN { + require 't/test-lib.pm'; +} + +my $res; + +my $client = LLNG::Manager::Test->new( { + ini => { + logLevel => 'error', + authentication => 'Demo', + userDB => 'Same', + loginHistoryEnabled => 0, + brutForceProtection => 0, + portalMainLogo => 'common/logos/logo_llng_old.png', + requireToken => 0, + impersonationRule => 1, + impersonationIdRule => '$uid ne "msmith"', + impersonationUnrestrictedUsersRule => '$uid eq "dwho"', + } + } +); + +## Try to Impersonate an allowed identity +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +count(1); +my ( $host, $url, $query ) = + expectForm( $res, '#', undef, 'user', 'password', 'spoofId' ); + +$query =~ s/user=/user=rtyler/; +$query =~ s/password=/password=rtyler/; +$query =~ s/spoofId=/spoofId=dwho/; + +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +my $id = expectCookie($res); +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'Get Menu', +); +expectAuthenticatedAs( $res, 'dwho' ); +count(2); +$client->logout($id); + +## Try to Impersonate a forbidden identity +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +count(1); +my ( $host, $url, $query ) = + expectForm( $res, '#', undef, 'user', 'password', 'spoofId' ); + +$query =~ s/user=/user=rtyler/; +$query =~ s/password=/password=rtyler/; +$query =~ s/spoofId=/spoofId=msmith/; + +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +ok( + $res->[2]->[0] =~ +m%
%, + ' PE5 found' +) or explain( $res->[2]->[0], "PE5 - Forbidden identity" ); +count(2); + +## Try to Impersonate a forbidden identity with an Unrestricted user +ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); +count(1); +( $host, $url, $query ) = + expectForm( $res, '#', undef, 'user', 'password', 'spoofId' ); + +$query =~ s/user=/user=dwho/; +$query =~ s/password=/password=dwho/; +$query =~ s/spoofId=/spoofId=msmith/; + +ok( + $res = $client->_post( + '/', + IO::String->new($query), + length => length($query), + accept => 'text/html', + ), + 'Auth query' +); +$id = expectCookie($res); +ok( + $res = $client->_get( + '/', + cookie => "lemonldap=$id", + accept => 'text/html' + ), + 'Get Menu', +); +expectAuthenticatedAs( $res, 'msmith' ); +count(2); +$client->logout($id); + +clean_sessions(); + +done_testing( count() );