Merge branch 'v2.0'

This commit is contained in:
Xavier Guimard 2019-03-07 22:29:34 +01:00
commit 77301e70ca
74 changed files with 1500 additions and 786 deletions

View File

@ -142,8 +142,9 @@
},
"locationRules": {
"auth.example.com" : {
"(?#checkUser)/checkuser" : "$uid eq \"dwho\"",
"default" : "deny"
"(?#checkUser)^/checkuser": "$uid eq \"dwho\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
"manager.example.com": {
"(?#Configuration)^/(manager\\.html|conf/)": "$uid eq \"dwho\"",

View File

@ -1,4 +1,4 @@
.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
@ -54,16 +54,20 @@
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.if !\nF .nr F 0
.if \nF>0 \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
@ -129,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "llng-fastcgi-server 1"
.TH llng-fastcgi-server 1 "2018-08-03" "perl v5.26.2" "User Contributed Perl Documentation"
.TH llng-fastcgi-server 1 "2019-03-05" "perl v5.28.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l

View File

@ -64,30 +64,34 @@ sub defaultValues {
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/'
},
'gpgDb' => '',
'groups' => {},
'handlerInternalCache' => 15,
'hiddenAttributes' => '_password',
'httpOnly' => 1,
'https' => -1,
'infoFormMethod' => 'get',
'issuerDBCASPath' => '^/cas/',
'issuerDBCASRule' => 1,
'issuerDBGetParameters' => {},
'issuerDBGetPath' => '^/get/',
'issuerDBGetRule' => 1,
'issuerDBOpenIDConnectPath' => '^/oauth2/',
'issuerDBOpenIDConnectRule' => 1,
'issuerDBOpenIDPath' => '^/openidserver/',
'issuerDBOpenIDRule' => 1,
'issuerDBSAMLPath' => '^/saml/',
'issuerDBSAMLRule' => 1,
'jsRedirect' => 0,
'krbAuthnLevel' => 3,
'krbRemoveDomain' => 1,
'ldapAuthnLevel' => 2,
'ldapBase' => 'dc=example,dc=com',
'ldapExportedVars' => {
'gpgDb' => '',
'groups' => {},
'handlerInternalCache' => 15,
'hiddenAttributes' => '_password',
'httpOnly' => 1,
'https' => -1,
'impersonationHiddenAttributes' => '_2fDevices _loginHistory',
'impersonationPrefix' => 'real_',
'impersonationRule' => 0,
'impersonationSkipEmptyValues' => 1,
'infoFormMethod' => 'get',
'issuerDBCASPath' => '^/cas/',
'issuerDBCASRule' => 1,
'issuerDBGetParameters' => {},
'issuerDBGetPath' => '^/get/',
'issuerDBGetRule' => 1,
'issuerDBOpenIDConnectPath' => '^/oauth2/',
'issuerDBOpenIDConnectRule' => 1,
'issuerDBOpenIDPath' => '^/openidserver/',
'issuerDBOpenIDRule' => 1,
'issuerDBSAMLPath' => '^/saml/',
'issuerDBSAMLRule' => 1,
'jsRedirect' => 0,
'krbAuthnLevel' => 3,
'krbRemoveDomain' => 1,
'ldapAuthnLevel' => 2,
'ldapBase' => 'dc=example,dc=com',
'ldapExportedVars' => {
'cn' => 'cn',
'mail' => 'mail',
'uid' => 'uid'

View File

@ -41,12 +41,12 @@ sub serviceToXML {
samlOrganizationURL
);
if ($type and $type eq 'idp') {
$template->param( 'hideSPMetadata', 1);
if ( $type and $type eq 'idp' ) {
$template->param( 'hideSPMetadata', 1 );
}
if ($type and $type eq 'sp') {
$template->param( 'hideIDPMetadata', 1);
if ( $type and $type eq 'sp' ) {
$template->param( 'hideIDPMetadata', 1 );
}
foreach (@param_auto) {

View File

@ -31,8 +31,8 @@ sub new {
$self->env->{PATH_INFO} =~ s|^$tmp|/|;
}
$self->env->{PATH_INFO} ||= '/';
$self->{uri} = uri_unescape( $self->env->{REQUEST_URI} );
$self->{uri} =~ s|^//+|/|g;
$self->env->{REQUEST_URI} =~ s|^//+|/|g;
$self->{uri} = uri_unescape( $self->env->{REQUEST_URI} );
$self->{data} = {};
$self->{error} = 0;
$self->{respHeaders} = [];

View File

@ -44,20 +44,20 @@ sub getStatus {
if ( $ENV{LLNGSTATUSHOST} ) {
require IO::Socket::INET;
foreach ( 64322 .. 64331 ) {
if ( $statusOut
= IO::Socket::INET->new( Proto => 'udp', LocalPort => $_ ) )
if ( $statusOut =
IO::Socket::INET->new( Proto => 'udp', LocalPort => $_ ) )
{
$args = ' host='
. ( $ENV{LLNGSTATUSCLIENT} || 'localhost' ) . ":$_";
$args =
' host=' . ( $ENV{LLNGSTATUSCLIENT} || 'localhost' ) . ":$_";
last;
}
}
return $class->abort( $req,
"$class: status page can not be displayed, unable to open socket"
) unless ($statusOut);
"$class: status page can not be displayed, unable to open socket" )
unless ($statusOut);
}
return $class->abort( $req, "$class: status page can not be displayed" )
unless ( $statusPipe and $statusOut );
unless ( $statusPipe and $statusOut );
my $q = $req->{env}->{QUERY_STRING} || '';
if ( $q =~ /\s/ ) {
$class->logger->error("Bad characters in query");
@ -84,12 +84,12 @@ sub checkType {
if ( time() - $class->lastCheck > $class->checkTime ) {
die("$class: No configuration found")
unless ( $class->checkConf );
unless ( $class->checkConf );
}
my $vhost = $class->resolveAlias($req);
return ( defined $class->tsv->{type}->{$vhost} )
? $class->tsv->{type}->{$vhost}
: 'Main';
? $class->tsv->{type}->{$vhost}
: 'Main';
}
## @rmethod int run
@ -125,7 +125,7 @@ sub run {
my ($cond);
( $cond, $protection ) = $class->conditionSub($rule) if ($rule);
$protection = $class->isUnprotected( $req, $uri ) || 0
unless ( defined $protection );
unless ( defined $protection );
if ( $protection == $class->SKIP ) {
$class->logger->debug("Access control skipped");
@ -150,7 +150,7 @@ sub run {
# AUTHORIZATION
return ( $class->forbidden( $req, $session ), $session )
unless ( $class->grant( $req, $session, $uri, $cond ) );
unless ( $class->grant( $req, $session, $uri, $cond ) );
$class->updateStatus( $req, 'OK',
$session->{ $class->tsv->{whatToTrace} } );
@ -168,8 +168,8 @@ sub run {
# Log access granted
$class->logger->debug( "User "
. $session->{ $class->tsv->{whatToTrace} }
. " was granted to access to $uri" );
. $session->{ $class->tsv->{whatToTrace} }
. " was granted to access to $uri" );
# Catch POST rules
$class->postOutputFilter( $req, $session, $uri );
@ -192,7 +192,7 @@ sub run {
# Redirect user to the portal
$class->logger->info("No cookie found")
unless ($id);
unless ($id);
# if the cookie was fetched, a log is sent by retrieveSession()
$class->updateStatus( $req, $id ? 'EXPIRED' : 'REDIRECT' );
@ -243,10 +243,10 @@ sub lmLog {
sub checkMaintenanceMode {
my ( $class, $req ) = @_;
my $vhost = $class->resolveAlias($req);
my $_maintenance
= ( defined $class->tsv->{maintenance}->{$vhost} )
? $class->tsv->{maintenance}->{$vhost}
: $class->tsv->{maintenance}->{_};
my $_maintenance =
( defined $class->tsv->{maintenance}->{$vhost} )
? $class->tsv->{maintenance}->{$vhost}
: $class->tsv->{maintenance}->{_};
if ($_maintenance) {
$class->logger->debug("Maintenance mode enabled");
@ -272,17 +272,17 @@ sub grant {
}
}
for (
my $i = 0;
$i < ( $class->tsv->{locationCount}->{$vhost} || 0 );
my $i = 0 ;
$i < ( $class->tsv->{locationCount}->{$vhost} || 0 ) ;
$i++
)
)
{
if ( $uri =~ $class->tsv->{locationRegexp}->{$vhost}->[$i] ) {
$class->logger->debug( 'Regexp "'
. $class->tsv->{locationConditionText}->{$vhost}->[$i]
. '" match' );
. $class->tsv->{locationConditionText}->{$vhost}->[$i]
. '" match' );
return $class->tsv->{locationCondition}->{$vhost}->[$i]
->( $req, $session );
->( $req, $session );
}
}
unless ( $class->tsv->{defaultCondition}->{$vhost} ) {
@ -319,8 +319,8 @@ sub forbidden {
# Log forbidding
$class->userLogger->notice( "User "
. $session->{ $class->tsv->{whatToTrace} }
. " was forbidden to access to $vhost$uri" );
. $session->{ $class->tsv->{whatToTrace} }
. " was forbidden to access to $vhost$uri" );
$class->updateStatus( $req, 'REJECT',
$session->{ $class->tsv->{whatToTrace} } );
@ -377,9 +377,9 @@ sub goToPortal {
$class->logger->debug(
"Redirect $req->{env}->{REMOTE_ADDR} to portal (url was $url)");
$class->set_header_out( $req,
'Location' => $class->tsv->{portal}->()
. "$path?url=$urlc_init"
. ( $arg ? "&$arg" : "" ) );
'Location' => $class->tsv->{portal}->()
. "$path?url=$urlc_init"
. ( $arg ? "&$arg" : "" ) );
return $class->REDIRECT;
}
@ -389,9 +389,9 @@ sub goToError {
$class->logger->debug(
"Redirect $req->{env}->{REMOTE_ADDR} to lmError (url was $url)");
$class->set_header_out( $req,
'Location' => $class->tsv->{portal}->()
. "/lmerror/$code"
. "?url=$urlc_init" );
'Location' => $class->tsv->{portal}->()
. "/lmerror/$code"
. "?url=$urlc_init" );
return $class->REDIRECT;
}
@ -403,12 +403,12 @@ sub fetchId {
my $t = $req->{env}->{HTTP_COOKIE} or return 0;
my $vhost = $class->resolveAlias($req);
my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/
and not $class->_isHttps( $req, $vhost ) );
and not $class->_isHttps( $req, $vhost ) );
my $cn = $class->tsv->{cookieName};
my $value
= $lookForHttpCookie
? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 )
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
my $value =
$lookForHttpCookie
? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 )
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
if ( $value && $lookForHttpCookie && $class->tsv->{securedCookie} == 3 ) {
$value = $class->tsv->{cipher}->decryptHex( $value, "http" );
@ -446,8 +446,8 @@ sub retrieveSession {
# 2. Get the session from cache or backend
my $session = $req->data->{session} = (
Lemonldap::NG::Common::Session->new(
{ storageModule => $class->tsv->{sessionStorageModule},
Lemonldap::NG::Common::Session->new( {
storageModule => $class->tsv->{sessionStorageModule},
storageModuleOptions => $class->tsv->{sessionStorageOptions},
cacheModule => $class->tsv->{sessionCacheModule},
cacheModuleOptions => $class->tsv->{sessionCacheOptions},
@ -464,36 +464,36 @@ sub retrieveSession {
# Verify that session is valid
$class->logger->error(
"_utime is not defined. This should not happen. Check if it is well transmitted to handler"
"_utime is not defined. This should not happen. Check if it is well transmitted to handler"
) unless $session->data->{_utime};
$class->logger->debug("Check session validity from Handler");
$class->logger->debug(
"Session timeout -> " . $class->tsv->{timeout} );
$class->logger->debug( "Session timeout -> " . $class->tsv->{timeout} );
$class->logger->debug( "Session timeoutActivity -> "
. $class->tsv->{timeoutActivity}
. "s" )
if ( $class->tsv->{timeoutActivity} );
. $class->tsv->{timeoutActivity}
. "s" )
if ( $class->tsv->{timeoutActivity} );
$class->logger->debug(
"Session _utime -> " . $session->data->{_utime} );
$class->logger->debug( "now -> " . $now );
$class->logger->debug( "_lastSeen -> " . $session->data->{_lastSeen} )
if ( $session->data->{_lastSeen} );
if ( $session->data->{_lastSeen} );
my $delta = $now - $session->data->{_lastSeen}
if ( $session->data->{_lastSeen} );
if ( $session->data->{_lastSeen} );
$class->logger->debug( "now - _lastSeen = " . $delta )
if ( $session->data->{_lastSeen} );
if ( $session->data->{_lastSeen} );
$class->logger->debug( "Session timeoutActivityInterval -> "
. $class->tsv->{timeoutActivityInterval} )
if ( $class->tsv->{timeoutActivityInterval} );
. $class->tsv->{timeoutActivityInterval} )
if ( $class->tsv->{timeoutActivityInterval} );
my $ttl = $class->tsv->{timeout} - $now + $session->data->{_utime};
$class->logger->debug( "Session TTL = " . $ttl );
if ($now - $session->data->{_utime} > $class->tsv->{timeout}
if (
$now - $session->data->{_utime} > $class->tsv->{timeout}
or ( $class->tsv->{timeoutActivity}
and $session->data->{_lastSeen}
and $delta > $class->tsv->{timeoutActivity} )
)
)
{
$class->logger->info("Session $id expired");
@ -503,10 +503,11 @@ sub retrieveSession {
}
# Update the session to notify activity, if necessary
if ($class->tsv->{timeoutActivity}
and ( $now - $session->data->{_lastSeen}
> $class->tsv->{timeoutActivityInterval} )
)
if (
$class->tsv->{timeoutActivity}
and ( $now - $session->data->{_lastSeen} >
$class->tsv->{timeoutActivityInterval} )
)
{
$req->data->{session}->update( { '_lastSeen' => $now } );
$class->data( $session->data );
@ -593,9 +594,9 @@ sub _buildUrl {
my $_https = $class->_isHttps( $req, $vhost );
my $portString = $class->_getPort( $req, $vhost );
$portString = (
( $realvhost =~ /:\d+/ )
or ( $_https && $portString == 443 )
or ( !$_https && $portString == 80 )
( $realvhost =~ /:\d+/ )
or ( $_https && $portString == 443 )
or ( !$_https && $portString == 80 )
) ? '' : ":$portString";
my $url = "http" . ( $_https ? "s" : "" ) . "://$realvhost$portString$s";
$class->logger->debug("Build URL $url");
@ -611,10 +612,10 @@ sub isUnprotected {
my ( $class, $req, $uri ) = @_;
my $vhost = $class->resolveAlias($req);
for (
my $i = 0;
$i < ( $class->tsv->{locationCount}->{$vhost} || 0 );
my $i = 0 ;
$i < ( $class->tsv->{locationCount}->{$vhost} || 0 ) ;
$i++
)
)
{
if ( $uri =~ $class->tsv->{locationRegexp}->{$vhost}->[$i] ) {
return $class->tsv->{locationProtection}->{$vhost}->[$i];
@ -631,8 +632,8 @@ sub sendHeaders {
if ( defined $class->tsv->{forgeHeaders}->{$vhost} ) {
# Log headers in debug mode
my %headers
= $class->tsv->{forgeHeaders}->{$vhost}->( $req, $session );
my %headers =
$class->tsv->{forgeHeaders}->{$vhost}->( $req, $session );
foreach my $h ( sort keys %headers ) {
if ( defined( my $v = $headers{$h} ) ) {
$class->logger->debug("Send header $h with value $v");
@ -655,12 +656,12 @@ sub checkHeaders {
if ( defined $class->tsv->{forgeHeaders}->{$vhost} ) {
# Create array of hashes with headers
my %headers
= $class->tsv->{forgeHeaders}->{$vhost}->( $req, $session );
my %headers =
$class->tsv->{forgeHeaders}->{$vhost}->( $req, $session );
foreach my $h ( sort keys %headers ) {
defined $headers{$h}
? push @$array_headers, { key => $h, value => $headers{$h} }
: push @$array_headers, { key => $h, value => '' };
? push @$array_headers, { key => $h, value => $headers{$h} }
: push @$array_headers, { key => $h, value => '' };
}
}
return $array_headers;
@ -685,7 +686,7 @@ sub resolveAlias {
$vhost =~ s/:\d+//;
return $class->tsv->{vhostAlias}->{$vhost}
if ( $class->tsv->{vhostAlias}->{$vhost} );
if ( $class->tsv->{vhostAlias}->{$vhost} );
return $vhost if ( $class->tsv->{defaultCondition}->{$vhost} );
my $v = $vhost;
while ( $v =~ s/[\w\-]+/\*/ ) {
@ -758,8 +759,8 @@ sub postOutputFilter {
$class->logger->debug("Filling a html form with fake data");
$class->unset_header_in( $req, "Accept-Encoding" );
my %postdata = $class->tsv->{outputPostData}->{$vhost}->{$uri}
->( $req, $session );
my %postdata =
$class->tsv->{outputPostData}->{$vhost}->{$uri}->( $req, $session );
my $formParams = $class->tsv->{postFormParams}->{$vhost}->{$uri};
my $js = $class->postJavascript( $req, \%postdata, $formParams );
$class->addToHtmlHead( $req, $js );
@ -776,8 +777,8 @@ sub postInputFilter {
if ( defined( $class->tsv->{inputPostData}->{$vhost}->{$uri} ) ) {
$class->logger->debug("Replacing fake data with real form data");
my %data = $class->tsv->{inputPostData}->{$vhost}->{$uri}
->( $req, $session );
my %data =
$class->tsv->{inputPostData}->{$vhost}->{$uri}->( $req, $session );
foreach ( keys %data ) {
$data{$_} = uri_escape( $data{$_} );
}
@ -797,33 +798,32 @@ sub postJavascript {
foreach my $name ( keys %$data ) {
use bytes;
my $value = "x" x bytes::length( $data->{$name} );
$filler
.= "form.find('input[name=\"$name\"], select[name=\"$name\"], textarea[name=\"$name\"]').val('$value')\n";
$filler .=
"form.find('input[name=\"$name\"], select[name=\"$name\"], textarea[name=\"$name\"]').val('$value')\n";
}
my $submitter
= $formParams->{buttonSelector} eq "none" ? ""
: $formParams->{buttonSelector}
? "form.find('$formParams->{buttonSelector}').click();\n"
: "form.submit();\n";
my $submitter =
$formParams->{buttonSelector} eq "none" ? ""
: $formParams->{buttonSelector}
? "form.find('$formParams->{buttonSelector}').click();\n"
: "form.submit();\n";
my $jqueryUrl = $formParams->{jqueryUrl} || "";
$jqueryUrl
= &{ $class->tsv->{portal} } . "skins/common/js/jquery-1.10.2.js"
if ( $jqueryUrl eq "default" );
$jqueryUrl = &{ $class->tsv->{portal} } . "skins/common/js/jquery-1.10.2.js"
if ( $jqueryUrl eq "default" );
$jqueryUrl = "<script type='text/javascript' src='$jqueryUrl'></script>\n"
if ($jqueryUrl);
if ($jqueryUrl);
return
$jqueryUrl
. "<script type='text/javascript'>\n"
. "/* script added by Lemonldap::NG */\n"
. "jQuery(window).on('load', function() {\n"
. "var form = jQuery('$form');\n"
. "form.attr('autocomplete', 'off');\n"
. $filler
. $submitter . "})\n"
. "</script>\n";
$jqueryUrl
. "<script type='text/javascript'>\n"
. "/* script added by Lemonldap::NG */\n"
. "jQuery(window).on('load', function() {\n"
. "var form = jQuery('$form');\n"
. "form.attr('autocomplete', 'off');\n"
. $filler
. $submitter . "})\n"
. "</script>\n";
}
1;

View File

@ -51,7 +51,6 @@ sub defaultUnauthRoute {
sub _run {
my $self = shift;
$self->rule(1);
return sub {
my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
@ -61,6 +60,7 @@ sub _run {
$req->userData( $self->api->data );
}
elsif ( $res->[0] != 403 ) {
# Unset headers (handler adds a Location header)
$self->logger->debug(
"User not authenticated, Try in use, cancel redirection");

View File

@ -1195,6 +1195,26 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => -1,
'type' => 'trool'
},
'impersonationHiddenAttributes' => {
'default' => '_2fDevices _loginHistory',
'type' => 'text'
},
'impersonationMergeSSOgroups' => {
'default' => 0,
'type' => 'bool'
},
'impersonationPrefix' => {
'default' => 'real_',
'type' => 'text'
},
'impersonationRule' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'impersonationSkipEmptyValues' => {
'default' => 1,
'type' => 'bool'
},
'infoFormMethod' => {
'default' => 'get',
'select' => [ {

View File

@ -416,6 +416,59 @@ sub attributes {
type => 'text',
documentation => 'Secret token for CheckState plugin',
},
checkUser => {
default => 0,
type => 'bool',
documentation => 'Enable check user',
flags => 'p',
},
checkUserHiddenAttributes => {
type => 'text',
default => '_2fDevices _loginHistory hGroups',
documentation => 'Attributes to hide in CheckUser plugin',
flags => 'p',
},
checkUserDisplayPersistentInfo => {
default => 0,
type => 'bool',
documentation => 'Display persistent session info',
flags => 'p',
},
checkUserDisplayEmptyValues => {
default => 0,
type => 'bool',
documentation => 'Display session empty values',
flags => 'p',
},
impersonationMergeSSOgroups => {
default => 0,
type => 'bool',
documentation => 'Merge spoofed and real SSO groups',
flags => 'p',
},
impersonationPrefix => {
type => 'text',
default => 'real_',
documentation => 'Prefix to rename real session attributes',
flags => 'p',
},
impersonationRule => {
type => 'boolOrExpr',
default => 0,
documentation => 'Impersonation activation rule',
},
impersonationHiddenAttributes => {
type => 'text',
default => '_2fDevices _loginHistory',
documentation => 'Attributes to skip',
flags => 'p',
},
impersonationSkipEmptyValues => {
default => 1,
type => 'bool',
documentation => 'Skip session empty values',
flags => 'p',
},
skipRenewConfirmation => {
type => 'bool',
default => 0,
@ -578,30 +631,6 @@ sub attributes {
documentation => 'Enable Cross Domain Authentication',
flags => 'hp',
},
checkUser => {
default => 0,
type => 'bool',
documentation => 'Enable check user',
flags => 'p',
},
checkUserHiddenAttributes => {
type => 'text',
default => '_2fDevices _loginHistory hGroups',
documentation => 'Attributes to hide in CheckUser plugin',
flags => 'p',
},
checkUserDisplayPersistentInfo => {
default => 0,
type => 'bool',
documentation => 'Display persistent session info',
flags => 'p',
},
checkUserDisplayEmptyValues => {
default => 0,
type => 'bool',
documentation => 'Display session empty values',
flags => 'p',
},
checkXSS => {
default => 1,
type => 'bool',

View File

@ -22,7 +22,8 @@ sub cTrees {
'locationRules',
'exportedHeaders',
'post',
{ title => 'vhostOptions',
{
title => 'vhostOptions',
help => 'configvhost.html#options',
form => 'simpleInputContainer',
nodes => [
@ -36,7 +37,8 @@ sub cTrees {
'samlIDPMetaDataXML',
'samlIDPMetaDataExportedAttributes',
{ title => "samlIDPMetaDataOptionsSession",
{
title => "samlIDPMetaDataOptionsSession",
form => 'simpleInputContainer',
nodes => [
"samlIDPMetaDataOptionsAdaptSessionUtime",
@ -45,7 +47,8 @@ sub cTrees {
"samlIDPMetaDataOptionsUserAttribute"
]
},
{ title => "samlIDPMetaDataOptionsSignature",
{
title => "samlIDPMetaDataOptionsSignature",
form => 'simpleInputContainer',
nodes => [
"samlIDPMetaDataOptionsSignSSOMessage",
@ -54,14 +57,16 @@ sub cTrees {
"samlIDPMetaDataOptionsCheckSLOMessageSignature"
]
},
{ title => "samlIDPMetaDataOptionsBinding",
{
title => "samlIDPMetaDataOptionsBinding",
form => 'simpleInputContainer',
nodes => [
"samlIDPMetaDataOptionsSSOBinding",
"samlIDPMetaDataOptionsSLOBinding"
]
},
{ title => "samlIDPMetaDataOptionsSecurity",
{
title => "samlIDPMetaDataOptionsSecurity",
form => 'simpleInputContainer',
nodes => [
"samlIDPMetaDataOptionsEncryptionMode",
@ -69,7 +74,8 @@ sub cTrees {
"samlIDPMetaDataOptionsCheckAudience"
]
},
{ title => 'samlIDPMetaDataOptions',
{
title => 'samlIDPMetaDataOptions',
help => 'authsaml.html#options',
form => 'simpleInputContainer',
nodes => [
@ -83,7 +89,8 @@ sub cTrees {
'samlIDPMetaDataOptionsRelayStateURL',
],
},
{ title => "samlIDPMetaDataOptionsDisplay",
{
title => "samlIDPMetaDataOptionsDisplay",
form => 'simpleInputContainer',
nodes => [
"samlIDPMetaDataOptionsDisplayName",
@ -94,10 +101,11 @@ sub cTrees {
samlSPMetaDataNode => [
"samlSPMetaDataXML",
"samlSPMetaDataExportedAttributes",
{ title => "samlSPMetaDataOptions",
{
title => "samlSPMetaDataOptions",
help => 'idpsaml.html#options',
nodes => [
{ title => "samlSPMetaDataOptionsAuthnResponse",
nodes => [ {
title => "samlSPMetaDataOptionsAuthnResponse",
form => 'simpleInputContainer',
nodes => [
"samlSPMetaDataOptionsNameIDFormat",
@ -108,7 +116,8 @@ sub cTrees {
"samlSPMetaDataOptionsForceUTF8"
]
},
{ title => "samlSPMetaDataOptionsSignature",
{
title => "samlSPMetaDataOptionsSignature",
form => 'simpleInputContainer',
nodes => [
"samlSPMetaDataOptionsSignSSOMessage",
@ -117,7 +126,8 @@ sub cTrees {
"samlSPMetaDataOptionsCheckSLOMessageSignature"
]
},
{ title => "samlSPMetaDataOptionsSecurity",
{
title => "samlSPMetaDataOptionsSecurity",
form => 'simpleInputContainer',
nodes => [
"samlSPMetaDataOptionsEncryptionMode",
@ -132,9 +142,10 @@ sub cTrees {
'oidcOPMetaDataJSON',
'oidcOPMetaDataJWKS',
'oidcOPMetaDataExportedVars',
{ title => 'oidcOPMetaDataOptions',
nodes => [
{ title => 'oidcOPMetaDataOptionsConfiguration',
{
title => 'oidcOPMetaDataOptions',
nodes => [ {
title => 'oidcOPMetaDataOptionsConfiguration',
form => 'simpleInputContainer',
nodes => [
'oidcOPMetaDataOptionsConfigurationURI',
@ -144,7 +155,8 @@ sub cTrees {
'oidcOPMetaDataOptionsStoreIDToken'
]
},
{ title => 'oidcOPMetaDataOptionsProtocol',
{
title => 'oidcOPMetaDataOptionsProtocol',
form => 'simpleInputContainer',
nodes => [
'oidcOPMetaDataOptionsScope',
@ -161,7 +173,8 @@ sub cTrees {
},
]
},
{ title => 'oidcOPMetaDataOptionsDisplayParams',
{
title => 'oidcOPMetaDataOptionsDisplayParams',
form => 'simpleInputContainer',
nodes => [
'oidcOPMetaDataOptionsDisplayName',
@ -172,9 +185,10 @@ sub cTrees {
oidcRPMetaDataNode => [
'oidcRPMetaDataExportedVars',
'oidcRPMetaDataOptionsExtraClaims',
{ title => 'oidcRPMetaDataOptions',
nodes => [
{ title => 'oidcRPMetaDataOptionsAuthentication',
{
title => 'oidcRPMetaDataOptions',
nodes => [ {
title => 'oidcRPMetaDataOptionsAuthentication',
form => 'simpleInputContainer',
nodes => [
'oidcRPMetaDataOptionsClientID',
@ -187,7 +201,8 @@ sub cTrees {
'oidcRPMetaDataOptionsAccessTokenExpiration',
'oidcRPMetaDataOptionsRedirectUris',
'oidcRPMetaDataOptionsBypassConsent',
{ title => 'logout',
{
title => 'logout',
form => 'simpleInputContainer',
nodes => [
'oidcRPMetaDataOptionsPostLogoutRedirectUris',
@ -199,7 +214,8 @@ sub cTrees {
'oidcRPMetaDataOptionsRule',
]
},
{ title => 'oidcRPMetaDataOptionsDisplay',
{
title => 'oidcRPMetaDataOptionsDisplay',
form => 'simpleInputContainer',
nodes => [
'oidcRPMetaDataOptionsDisplayName',
@ -210,7 +226,8 @@ sub cTrees {
casSrvMetaDataNode => [
'casSrvMetaDataExportedVars',
'casSrvMetaDataOptionsProxiedServices',
{ title => 'casSrvMetaDataOptions',
{
title => 'casSrvMetaDataOptions',
form => 'simpleInputContainer',
nodes => [
'casSrvMetaDataOptionsUrl',
@ -218,7 +235,8 @@ sub cTrees {
'casSrvMetaDataOptionsGateway',
]
},
{ title => 'casSrvMetaDataOptionsDisplay',
{
title => 'casSrvMetaDataOptionsDisplay',
form => 'simpleInputContainer',
nodes => [
'casSrvMetaDataOptionsDisplayName',
@ -226,8 +244,8 @@ sub cTrees {
]
},
],
casAppMetaDataNode => [
{ title => 'casAppMetaDataOptions',
casAppMetaDataNode => [ {
title => 'casAppMetaDataOptions',
form => 'simpleInputContainer',
nodes => [
'casAppMetaDataOptionsService',

View File

@ -498,8 +498,7 @@ sub tree {
title => 'logParams',
help => 'logs.html',
form => 'simpleInputContainer',
nodes =>
[ 'whatToTrace', 'hiddenAttributes' ]
nodes => [ 'whatToTrace', 'hiddenAttributes' ]
},
{
title => 'cookieParams',
@ -648,6 +647,18 @@ sub tree {
'checkUserDisplayEmptyValues',
]
},
{
title => 'impersonation',
help => 'impersonation.html',
form => 'simpleInputContainer',
nodes => [
'impersonationRule',
'impersonationPrefix',
'impersonationHiddenAttributes',
'impersonationSkipEmptyValues',
'impersonationMergeSSOgroups',
]
},
]
},
{

View File

@ -31,7 +31,8 @@ sub tests {
portalIsInDomain => sub {
return (
1,
( index( $conf->{portal}, $conf->{domain} ) > 0
(
index( $conf->{portal}, $conf->{domain} ) > 0
? ''
: "Portal seems not to be in the domain $conf->{domain}"
)
@ -43,7 +44,7 @@ sub tests {
# Checking for ending slash
$conf->{portal} .= '/'
unless ( $conf->{portal} =~ qr#/$# );
unless ( $conf->{portal} =~ qr#/$# );
# Deleting trailing ending slash
my $regex = qr#/+$#;
@ -61,10 +62,11 @@ sub tests {
}
return (
1,
( @pb
(
@pb
? 'Virtual hosts '
. join( ', ', @pb )
. " are not in $conf->{domain} and cross-domain-authentication is not set"
. join( ', ', @pb )
. " are not in $conf->{domain} and cross-domain-authentication is not set"
: undef
)
);
@ -78,9 +80,9 @@ sub tests {
}
if (@pb) {
return ( 0,
'Virtual hosts '
. join( ', ', @pb )
. " contain a port, this is not allowed" );
'Virtual hosts '
. join( ', ', @pb )
. " contain a port, this is not allowed" );
}
else { return 1; }
},
@ -93,9 +95,9 @@ sub tests {
}
if (@pb) {
return ( 0,
'Virtual hosts '
. join( ', ', @pb )
. " must be in lower case" );
'Virtual hosts '
. join( ', ', @pb )
. " must be in lower case" );
}
else { return 1; }
},
@ -103,12 +105,12 @@ sub tests {
# Check if "userDB" and "authentication" are consistent
authAndUserDBConsistency => sub {
foreach
my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID))
my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID))
{
return ( 0,
"\"$type\" can not be used as user database without using \"$type\" for authentication"
)
if ($conf->{userDB} =~ /$type/
"\"$type\" can not be used as user database without using \"$type\" for authentication"
)
if ( $conf->{userDB} =~ /$type/
and $conf->{authentication} !~ /$type/ );
}
return 1;
@ -118,29 +120,30 @@ sub tests {
checkAttrAndMacros => sub {
my @tmp;
foreach my $k ( keys %$conf ) {
if ( $k
=~ /^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/
)
if ( $k =~
/^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/
)
{
my $v = $conf->{$k};
$v =~ s/^$//;
next if ( $v =~ /^_/ );
push @tmp,
$k
unless (
$k
unless (
defined(
$conf->{exportedVars}->{$v}
or defined( $conf->{macros}->{$v} )
or defined( $conf->{macros}->{$v} )
)
);
);
}
}
return (
1,
( @tmp
(
@tmp
? 'Values of parameter(s) "'
. join( ', ', @tmp )
. '" are not defined in exported attributes or macros'
. join( ', ', @tmp )
. '" are not defined in exported attributes or macros'
: ''
)
);
@ -152,18 +155,18 @@ sub tests {
if ( $conf->{userDB} =~ /^Google$/ ) {
foreach my $k ( keys %{ $conf->{exportedVars} } ) {
my $v = $conf->{exportedVars}->{$k};
if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() )
{
if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() ) {
push @tmp, $v;
}
}
}
return (
1,
( @tmp
(
@tmp
? 'Values of parameter(s) "'
. join( ', ', @tmp )
. '" are not exported by Google'
. join( ', ', @tmp )
. '" are not exported by Google'
: ''
)
);
@ -175,8 +178,7 @@ sub tests {
if ( $conf->{userDB} =~ /^OpenID$/ ) {
foreach my $k ( keys %{ $conf->{exportedVars} } ) {
my $v = $conf->{exportedVars}->{$k};
if ( $v
!~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
if ( $v !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
{
push @tmp, $v;
}
@ -184,10 +186,11 @@ sub tests {
}
return (
1,
( @tmp
(
@tmp
? 'Values of parameter(s) "'
. join( ', ', @tmp )
. '" are not exported by OpenID SREG'
. join( ', ', @tmp )
. '" are not exported by OpenID SREG'
: ''
)
);
@ -196,40 +199,39 @@ sub tests {
# Try to use Apache::Session module
testApacheSession => sub {
my ( $id, %h );
my $gc
= Lemonldap::NG::Handler::Main->tsv->{sessionStorageModule};
my $gc = Lemonldap::NG::Handler::Main->tsv->{sessionStorageModule};
return 1
if ( ( $gc and $gc eq $conf->{globalStorage} )
or $conf->{globalStorage}
=~ /^Lemonldap::NG::Common::Apache::Session::/ );
if ( ( $gc and $gc eq $conf->{globalStorage} )
or $conf->{globalStorage} =~
/^Lemonldap::NG::Common::Apache::Session::/ );
eval "use $conf->{globalStorage}";
return ( -1, "Unknown package $conf->{globalStorage}" ) if ($@);
eval {
tie %h, 'Lemonldap::NG::Common::Apache::Session', undef,
{
{
%{ $conf->{globalStorageOptions} },
backend => $conf->{globalStorage}
};
};
};
return ( -1, "Unable to create a session ($@)" )
if ( $@ or not tied(%h) );
if ( $@ or not tied(%h) );
eval {
$h{a} = 1;
$id = $h{_session_id} or return ( -1, 'No _session_id' );
untie(%h);
tie %h, 'Lemonldap::NG::Common::Apache::Session', $id,
{
{
%{ $conf->{globalStorageOptions} },
backend => $conf->{globalStorage}
};
};
};
return ( -1, "Unable to insert data ($@)" ) if ($@);
return ( -1, "Unable to recover data stored" )
unless ( $h{a} == 1 );
unless ( $h{a} == 1 );
eval { tied(%h)->delete; };
return ( -1, "Unable to delete session ($@)" ) if ($@);
return ( -1,
'All sessions may be lost and you must restart all your Apache servers'
'All sessions may be lost and you must restart all your Apache servers'
) if ( $gc and $conf->{globalStorage} ne $gc );
return 1;
},
@ -239,8 +241,9 @@ sub tests {
my $cn = Lemonldap::NG::Handler::Main->tsv->{cookieName};
return (
1,
( $cn
and $cn ne $conf->{cookieName}
(
$cn
and $cn ne $conf->{cookieName}
? 'Cookie name has changed, you must restart all your web servers'
: ()
)
@ -251,10 +254,10 @@ sub tests {
cookieTTL => sub {
return 1 unless ( defined $conf->{cookieExpiration} );
return ( 0, "Cookie TTL must be higher than one minute" )
unless ( $conf->{cookieExpiration} == 0
unless ( $conf->{cookieExpiration} == 0
|| $conf->{cookieExpiration} > 60 );
return ( 1, "Cookie TTL should be higher or equal than one hour" )
unless ( $conf->{cookieExpiration} >= 3600
unless ( $conf->{cookieExpiration} >= 3600
|| $conf->{cookieExpiration} == 0 );
# Return
@ -265,7 +268,7 @@ sub tests {
sessionTimeout => sub {
return 1 unless ( defined $conf->{timeout} );
return ( -1, "Session timeout should be higher than ten minutes" )
unless ( $conf->{timeout} > 600
unless ( $conf->{timeout} > 600
|| $conf->{timeout} == 0 );
# Return
@ -276,9 +279,9 @@ sub tests {
sessionTimeoutActivity => sub {
return 1 unless ( defined $conf->{timeoutActivity} );
return ( 0,
"Session activity timeout must be higher or equal than one minute"
)
unless ( $conf->{timeoutActivity} > 59
"Session activity timeout must be higher or equal than one minute"
)
unless ( $conf->{timeoutActivity} > 59
|| $conf->{timeoutActivity} == 0 );
# Return
@ -289,11 +292,11 @@ sub tests {
timeoutActivityInterval => sub {
return 1 unless ( defined $conf->{timeoutActivityInterval} );
return ( 0,
"Activity timeout interval must be lower than session activity timeout"
)
if ($conf->{timeoutActivity}
and $conf->{timeoutActivity}
<= $conf->{timeoutActivityInterval} );
"Activity timeout interval must be lower than session activity timeout"
)
if ( $conf->{timeoutActivity}
and $conf->{timeoutActivity} <=
$conf->{timeoutActivityInterval} );
# Return
return 1;
@ -303,7 +306,8 @@ sub tests {
managerProtection => sub {
return (
1,
( $conf->{cfgAuthor} eq 'anonymous'
(
$conf->{cfgAuthor} eq 'anonymous'
? 'Your manager seems to be unprotected'
: ''
)
@ -319,7 +323,7 @@ sub tests {
# Use SMTP
eval "use Net::SMTP";
return ( 1, "Net::SMTP module is required to use SMTP server" )
if ($@);
if ($@);
# Create SMTP object
my $smtp = Net::SMTP->new(
@ -329,15 +333,15 @@ sub tests {
);
return ( 1,
"SMTP connection to " . $conf->{SMTPServer} . " failed" )
unless ($smtp);
unless ($smtp);
# Skip other tests if no authentication
return 1
unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
# Try authentication
return ( 1, "SMTP authentication failed" )
unless $smtp->auth( $conf->{SMTPAuthUser},
unless $smtp->auth( $conf->{SMTPAuthUser},
$conf->{SMTPAuthPass} );
# Return
@ -347,14 +351,15 @@ sub tests {
# SAML entity ID must be uniq
samlIDPEntityIdUniqueness => sub {
return 1
unless ( $conf->{samlIDPMetaDataXML}
unless ( $conf->{samlIDPMetaDataXML}
and %{ $conf->{samlIDPMetaDataXML} } );
my @msg;
my $res = 1;
my %entityIds;
foreach my $idpId ( keys %{ $conf->{samlIDPMetaDataXML} } ) {
unless ( $conf->{samlIDPMetaDataXML}->{$idpId}
->{samlIDPMetaDataXML} =~ /entityID=(['"])(.+?)\1/si )
unless (
$conf->{samlIDPMetaDataXML}->{$idpId}->{samlIDPMetaDataXML}
=~ /entityID=(['"])(.+?)\1/si )
{
push @msg, "$idpId SAML metadata has no EntityID";
$res = 0;
@ -363,7 +368,7 @@ sub tests {
my $eid = $2;
if ( defined $entityIds{$eid} ) {
push @msg,
"$idpId and $entityIds{$eid} have the same SAML EntityID";
"$idpId and $entityIds{$eid} have the same SAML EntityID";
$res = 0;
next;
}
@ -373,15 +378,15 @@ sub tests {
},
samlSPEntityIdUniqueness => sub {
return 1
unless ( $conf->{samlSPMetaDataXML}
unless ( $conf->{samlSPMetaDataXML}
and %{ $conf->{samlSPMetaDataXML} } );
my @msg;
my $res = 1;
my %entityIds;
foreach my $spId ( keys %{ $conf->{samlSPMetaDataXML} } ) {
unless (
$conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML}
=~ /entityID=(['"])(.+?)\1/si )
$conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML} =~
/entityID=(['"])(.+?)\1/si )
{
push @msg, "$spId SAML metadata has no EntityID";
$res = 0;
@ -390,7 +395,7 @@ sub tests {
my $eid = $2;
if ( defined $entityIds{$eid} ) {
push @msg,
"$spId and $entityIds{$eid} have the same SAML EntityID";
"$spId and $entityIds{$eid} have the same SAML EntityID";
$res = 0;
next;
}
@ -404,7 +409,7 @@ sub tests {
return 1 unless ( $conf->{authentication} eq 'Combination' );
require Lemonldap::NG::Common::Combination::Parser;
return ( 0, 'No module declared for combination' )
unless ( $conf->{combModules} and %{ $conf->{combModules} } );
unless ( $conf->{combModules} and %{ $conf->{combModules} } );
my $moduleList;
foreach my $md ( keys %{ $conf->{combModules} } ) {
my $entry = $conf->{combModules}->{$md};
@ -415,8 +420,8 @@ sub tests {
);
}
eval {
Lemonldap::NG::Common::Combination::Parser->parse(
$moduleList, $conf->{combination} );
Lemonldap::NG::Common::Combination::Parser->parse( $moduleList,
$conf->{combination} );
};
return ( 0, $@ ) if ($@);
@ -428,9 +433,9 @@ sub tests {
combinationParameters => sub {
return 1 unless ( $conf->{authentication} eq "Combination" );
return ( 0, "Combination rule must be defined" )
unless ( $conf->{combination} );
unless ( $conf->{combination} );
return ( 0, 'userDB must be set to "Same" to enable Combination' )
unless ( $conf->{userDB} eq "Same" );
unless ( $conf->{userDB} eq "Same" );
# Return
return 1;
@ -453,7 +458,7 @@ sub tests {
eval "use Convert::Base32";
return ( 1,
"Convert::Base32 module is required to enable TOTP" )
if ($@);
if ($@);
}
# Use U2F
@ -462,7 +467,7 @@ sub tests {
{
eval "use Crypt::U2F::Server::Simple";
return ( 1,
"Crypt::U2F::Server::Simple module is required to enable U2F"
"Crypt::U2F::Server::Simple module is required to enable U2F"
) if ($@);
}
@ -470,7 +475,7 @@ sub tests {
if ( $conf->{yubikey2fActivation} ) {
eval "use Auth::Yubikey_WebClient";
return ( 1,
"Auth::Yubikey_WebClient module is required to enable Yubikey"
"Auth::Yubikey_WebClient module is required to enable Yubikey"
) if ($@);
}
@ -484,7 +489,7 @@ sub tests {
my $w = "";
foreach ( 'totp', 'u' ) {
$w .= uc($_) . "2F is activated twice \n"
if ( $conf->{ $_ . '2fActivation' } eq '1' );
if ( $conf->{ $_ . '2fActivation' } eq '1' );
}
return ( 1, ( $w ? $w : () ) );
},
@ -495,9 +500,9 @@ sub tests {
return 1 unless ( defined $conf->{totp2fDigits} );
return (
1,
(
( $conf->{totp2fDigits} == 6
or $conf->{totp2fDigits} == 8
( (
$conf->{totp2fDigits} == 6
or $conf->{totp2fDigits} == 8
)
? ''
: 'TOTP should be 6 or 8 digits long'
@ -509,9 +514,9 @@ sub tests {
totp2fParams => sub {
return 1 unless ( $conf->{totp2fActivation} );
return ( 0, 'TOTP range must be defined' )
unless ( $conf->{totp2fRange} );
unless ( $conf->{totp2fRange} );
return ( 1, "TOTP interval should be higher than 10s" )
unless ( $conf->{totp2fInterval} > 10 );
unless ( $conf->{totp2fInterval} > 10 );
# Return
return 1;
@ -522,11 +527,12 @@ sub tests {
yubikey2fParams => sub {
return 1 unless ( $conf->{yubikey2fActivation} );
return ( 0, "Yubikey client ID and secret key must be set" )
unless ( defined $conf->{yubikey2fSecretKey}
unless ( defined $conf->{yubikey2fSecretKey}
&& defined $conf->{yubikey2fClientID} );
return (
1,
( ( $conf->{yubikey2fPublicIDSize} == 12 )
(
( $conf->{yubikey2fPublicIDSize} == 12 )
? ''
: 'Yubikey public ID size should be 12 digits long'
)
@ -537,7 +543,7 @@ sub tests {
rest2fVerifyUrl => sub {
return 1 unless ( $conf->{rest2fActivation} );
return ( 0, "REST 2F Verify URL must be set" )
unless ( defined $conf->{rest2fVerifyUrl} );
unless ( defined $conf->{rest2fVerifyUrl} );
# Return
return 1;
@ -551,16 +557,15 @@ sub tests {
my $ok = 0;
foreach (qw(u totp yubikey)) {
$ok ||= $conf->{ $_ . '2fActivation' }
&& $conf->{ $_ . '2fSelfRegistration' };
&& $conf->{ $_ . '2fSelfRegistration' };
last if ($ok);
}
$ok ||= $conf->{'utotp2fActivation'}
&& ( $conf->{'u2fSelfRegistration'}
&& ( $conf->{'u2fSelfRegistration'}
|| $conf->{'totp2fSelfRegistration'} );
$msg
= "A self registrable module should be enabled to require 2FA"
unless ($ok);
$msg = "A self registrable module should be enabled to require 2FA"
unless ($ok);
return ( 1, $msg );
},
@ -569,10 +574,10 @@ sub tests {
ext2fCommands => sub {
return 1 unless ( $conf->{ext2fActivation} );
return ( 0, "External 2F Send command must be set" )
unless ( defined $conf->{ext2FSendCommand} );
unless ( defined $conf->{ext2FSendCommand} );
unless ( defined $conf->{ext2fCodeActivation} ) {
return ( 0, "External 2F Validate command must be set" )
unless ( defined $conf->{ext2FValidateCommand} );
unless ( defined $conf->{ext2FValidateCommand} );
}
# Return
@ -583,9 +588,9 @@ sub tests {
formTimeout => sub {
return 1 unless ( defined $conf->{formTimeout} );
return ( 0, "XSRF form token TTL must be higher than 30s" )
unless ( $conf->{formTimeout} > 30 );
unless ( $conf->{formTimeout} > 30 );
return ( 1, "XSRF form token TTL should not be higher than 2mn" )
if ( $conf->{formTimeout} > 120 );
if ( $conf->{formTimeout} > 120 );
# Return
return 1;
@ -594,9 +599,8 @@ sub tests {
# Warn if number of password reset retries is null
passwordResetRetries => sub {
return 1 unless ( $conf->{portalDisplayResetPassword} );
return ( 1,
"Number of reset password retries should not be null" )
unless ( $conf->{passwordResetAllowedRetries} );
return ( 1, "Number of reset password retries should not be null" )
unless ( $conf->{passwordResetAllowedRetries} );
# Return
return 1;
@ -606,10 +610,10 @@ sub tests {
bruteForceProtection => sub {
return 1 unless ( $conf->{bruteForceProtection} );
return ( 1,
'"History" plugin is required to enable "BruteForceProtection" plugin'
'"History" plugin is required to enable "BruteForceProtection" plugin'
) unless ( $conf->{loginHistoryEnabled} );
return ( 1,
'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin'
'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin'
) unless ( $conf->{failedLoginNumber} > 2 );
# Return
@ -620,15 +624,25 @@ sub tests {
checkMailResetSecurity => sub {
return 1 unless ( $conf->{portalDisplayResetPassword} );
return ( -1,
'"passwordMailReset" plugin is enabled without CSRF Token neither Captcha required !!!'
)
unless ( $conf->{requireToken}
'"passwordMailReset" plugin is enabled without CSRF Token neither Captcha required !!!'
)
unless ( $conf->{requireToken}
or $conf->{captcha_mail_enabled} );
# Return
return 1;
},
## Warn if IdSpoofing plugin is enabled
# checkIdSpoofing => sub {
# return ( -1,
# '"IdSpoofing" plugin is enabled!!!'
# )
# if ( $conf->{idSpoofingRule} );
# # Return
# return 1;
# },
};
}

View File

@ -151,7 +151,7 @@
"clickHereToForce":"انقر هنا لإجبار",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
@ -286,6 +286,12 @@
"hideTree":"إخفاء الشجرة",
"httpOnly":"الحماية بواسطة جافا سكريبت",
"https":"إتش تي تي بي س",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"الحقول المطلوبة مفقودة",
"index":"فهرس",
"infoFormMethod":"طريقة للحصول على معلومات الإستمارة",

View File

@ -151,7 +151,7 @@
"clickHereToForce":"Click here to force",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"choiceParams":"Choice parameters",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
@ -286,6 +286,12 @@
"hideTree":"Hide tree",
"httpOnly":"Javascript protection",
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"Required fields are missing",
"index":"Index",
"infoFormMethod":"Method for info form",

View File

@ -151,7 +151,7 @@
"clickHereToForce":"Click here to force",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
@ -286,6 +286,12 @@
"hideTree":"Hide tree",
"httpOnly":"Javascript protection",
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"Required fields are missing",
"index":"Index",
"infoFormMethod":"Method for info form",

View File

@ -151,7 +151,7 @@
"clickHereToForce":"Cliquer ici pour forcer",
"checkState":"Activation",
"checkStateSecret":"Secret partagé",
"checkUsers":"Vérification de session",
"checkUsers":"Vérification des profils SSO",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Attributs masqués",
"checkUserDisplayPersistentInfo":"Afficher les données de session persistante",
@ -286,6 +286,12 @@
"hideTree":"Masquer l'arbre",
"httpOnly":"Protection contre javascript",
"https":"HTTPS",
"impersonation":"Usurpation d'identité",
"impersonationRule":"Règle d'utilisation",
"impersonationHiddenAttributes":"Attributs masqués",
"impersonationMergeSSOgroups":"Fusionner les groupes SSO réels et usurpés",
"impersonationPrefix":"Préfix des vrais attributs",
"impersonationSkipEmptyValues":"Ignorer les valeurs nulles",
"incompleteForm":"Des champs requis manquent",
"index":"Index",
"infoFormMethod":"Méthode du formulaire d'information",

View File

@ -151,7 +151,7 @@
"clickHereToForce":"Clicca qui per forzare",
"checkState":"Attivazione",
"checkStateSecret":"Segreto condiviso",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
@ -286,6 +286,12 @@
"hideTree":"Nascondi l'albero",
"httpOnly":"Protezione Javascript",
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"Mancano campi obbligatori",
"index":"Indice",
"infoFormMethod":"Metodo per il modulo informazioni",

View File

@ -286,6 +286,12 @@
"hideTree":"Ẩn cây",
"httpOnly":"Bảo vệ Javascript",
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"Các trường bắt buộc bị thiếu",
"index":"Chỉ mục",
"infoFormMethod":"Phương pháp cho mẫu thông tin",

View File

@ -151,7 +151,7 @@
"clickHereToForce":"Click here to force",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
@ -286,6 +286,12 @@
"hideTree":"Hide tree",
"httpOnly":"Javascript protection",
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
"impersonationSkipEmptyValues":"Skip empty values",
"incompleteForm":"Required fields are missing",
"index":"Index",
"infoFormMethod":"Method for info form",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" type="image/vnd.microsoft.icon" sizes="16x16 32x32 48x48 64x64 128x128" href="<TMPL_VAR NAME="STATIC_PREFIX">logos/favicon.ico" />
<link rel="icon" type="image/vnd.microsoft.icon" sizes="16x16 32x32 48x48 64x64 128x128" href="<TMPL_VAR NAME="STATIC_PREFIX">logos/favicon.ico" />

View File

@ -104,6 +104,7 @@ lib/Lemonldap/NG/Portal/Plugins/CheckUser.pm
lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm
lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm
lib/Lemonldap/NG/Portal/Plugins/History.pm
lib/Lemonldap/NG/Portal/Plugins/Impersonation.pm
lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm
lib/Lemonldap/NG/Portal/Plugins/Notifications.pm
lib/Lemonldap/NG/Portal/Plugins/PublicPages.pm
@ -338,6 +339,7 @@ site/templates/bootstrap/footer.tpl
site/templates/bootstrap/gpgform.tpl
site/templates/bootstrap/header.tpl
site/templates/bootstrap/idpchoice.tpl
site/templates/bootstrap/impersonation.tpl
site/templates/bootstrap/info.tpl
site/templates/bootstrap/ldapPpGrace.tpl
site/templates/bootstrap/login.json
@ -503,6 +505,8 @@ t/66-CDA-with-SOAP.t
t/66-CDA.t
t/67-CheckUser-with-token.t
t/67-CheckUser.t
t/68-Impersonation-with-merge.t
t/68-Impersonation.t
t/70-2F-TOTP-with-History.t
t/70-2F-TOTP.t
t/70-2F-TOTP_8.t

View File

@ -4,11 +4,11 @@ use strict;
use Mouse;
use String::Random;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_ERROR
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
PE_BADCREDENTIALS
PE_ERROR
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
);
our $VERSION = '2.1.0';
@ -30,7 +30,7 @@ sub init {
}
}
$self->logo( $self->conf->{ext2fLogo} )
if ( $self->conf->{ext2fLogo} );
if ( $self->conf->{ext2fLogo} );
return $self->SUPER::init();
}
if ( $self->conf->{ext2fCodeActivation} ) {
@ -40,7 +40,7 @@ sub init {
}
$self->random( String::Random->new );
$self->logo( $self->conf->{ext2fLogo} )
if ( $self->conf->{ext2fLogo} );
if ( $self->conf->{ext2fLogo} );
return $self->SUPER::init();
}
return 0;
@ -57,22 +57,22 @@ sub run {
# Generate Code to send
my $code;
if ( $self->conf->{ext2fCodeActivation} ) {
$code
= $self->random->randregex( $self->conf->{ext2fCodeActivation} );
$code = $self->random->randregex( $self->conf->{ext2fCodeActivation} );
$self->logger->debug("Generated ext2f code : $code");
$self->ott->updateToken( $token, __ext2fcode => $code );
}
# Prepare command and launch it
$self->logger->debug( 'Launching "Send" external 2F command -> '
. $self->conf->{ext2FSendCommand} );
if (my $c = $self->launch(
. $self->conf->{ext2FSendCommand} );
if (
my $c = $self->launch(
$req->sessionInfo, $self->conf->{ext2FSendCommand}, $code
)
)
)
{
$self->logger->error("External send command failed (code $c)");
return $self->p->do( $req, [ sub {PE_ERROR} ] );
return $self->p->do( $req, [ sub { PE_ERROR } ] );
}
# Prepare form
@ -104,15 +104,16 @@ sub verify {
# Prepare command and launch it
$self->logger->debug( 'Launching "Validate" external 2F command -> '
. $self->conf->{ext2FValidateCommand} );
. $self->conf->{ext2FValidateCommand} );
$self->logger->debug(" code -> $usercode");
if (my $c = $self->launch(
if (
my $c = $self->launch(
$session, $self->conf->{ext2FValidateCommand}, $usercode
)
)
)
{
$self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } );
. $session->{ $self->conf->{whatToTrace} } );
$self->logger->error("External verify command failed (code $c)");
return PE_BADCREDENTIALS;
}
@ -130,7 +131,7 @@ sub verify {
return PE_OK if ( $usercode eq $savedcode );
$self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } );
. $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS;
}

View File

@ -4,18 +4,18 @@ use strict;
use Mouse;
use String::Random;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_ERROR
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
PE_MUSTHAVEMAIL
PE_BADCREDENTIALS
PE_ERROR
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
PE_MUSTHAVEMAIL
);
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::SecondFactor',
'Lemonldap::NG::Portal::Lib::SMTP';
'Lemonldap::NG::Portal::Lib::SMTP';
# INITIALIZATION
@ -31,10 +31,10 @@ has ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott = $_[0]->{p}
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{mail2fTimeout}
|| $_[0]->{conf}->{formTimeout} );
|| $_[0]->{conf}->{formTimeout} );
return $ott;
}
);
@ -47,7 +47,7 @@ sub init {
return 0;
}
$self->logo( $self->conf->{mail2fLogo} )
if ( $self->conf->{mail2fLogo} );
if ( $self->conf->{mail2fLogo} );
return $self->SUPER::init();
}
@ -63,7 +63,7 @@ sub run {
my $dest = $req->{sessionInfo}->{ $self->conf->{mailSessionKey} };
unless ($dest) {
$self->logger->error( "Could not find mail attribute for login "
. $req->{sessionInfo}->{_user} );
. $req->{sessionInfo}->{_user} );
return PE_MUSTHAVEMAIL;
}
@ -141,7 +141,7 @@ sub verify {
return PE_OK if ( $usercode eq $savedcode );
$self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } );
. $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS;
}

View File

@ -49,7 +49,7 @@ sub authenticate {
unless ( $req->data->{password} ) {
$self->p->{user} = $req->userData->{_dn} = $req->data->{dn};
unless($self->p->{_passwordDB}) {
unless ( $self->p->{_passwordDB} ) {
$self->logger->error('No password database configured, aborting');
return PE_ERROR;
}

View File

@ -16,6 +16,7 @@ use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_PASSWORDFORMEMPTY
PE_TOKENEXPIRED
PE_MALFORMEDUSER
);
our $VERSION = '2.1.0';
@ -53,6 +54,13 @@ sub init {
sub extractFormInfo {
my ( $self, $req ) = @_;
if ( $req->param('user') ) {
unless ( $req->param('user') =~ /$self->{conf}->{userControl}/o ) {
$self->setSecurity($req);
return PE_MALFORMEDUSER;
}
}
# Detect first access and empty forms
my $defUser = defined $req->param('user');
my $defPassword = defined $req->param('password');

View File

@ -4,7 +4,8 @@ use strict;
use Mouse;
use URI::Escape;
use Lemonldap::NG::Common::FormEncode;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_BADURL PE_GET_SERVICE_NOT_ALLOWED);
use Lemonldap::NG::Portal::Main::Constants
qw(PE_OK PE_BADURL PE_GET_SERVICE_NOT_ALLOWED);
our $VERSION = '2.1.0';
@ -19,11 +20,9 @@ sub init {
# Parse activation rule
my $hd = $self->p->HANDLER;
$self->logger->debug(
"GET rule -> " . $self->conf->{issuerDBGetRule} );
$self->logger->debug( "GET rule -> " . $self->conf->{issuerDBGetRule} );
my $rule =
$hd->buildSub(
$hd->substitute( $self->conf->{issuerDBGetRule} ) );
$hd->buildSub( $hd->substitute( $self->conf->{issuerDBGetRule} ) );
unless ($rule) {
$self->error( "Bad GET rule -> " . $hd->tsv->{jail}->error );
return 0;

View File

@ -62,8 +62,7 @@ sub init {
$self->logger->debug(
"OpenID rule -> " . $self->conf->{issuerDBOpenIDRule} );
my $rule =
$hd->buildSub(
$hd->substitute( $self->conf->{issuerDBOpenIDRule} ) );
$hd->buildSub( $hd->substitute( $self->conf->{issuerDBOpenIDRule} ) );
unless ($rule) {
$self->error( "Bad OpenID rule -> " . $hd->tsv->{jail}->error );
return 0;

View File

@ -145,11 +145,13 @@ sub init {
return 0 unless ( $self->lassoServer( $self->loadService ) );
$self->addUnauthRoute(
( $self->{path} || 'saml' ) => { 'metadata' => { ':type' => 'metadata' }},
( $self->{path} || 'saml' ) =>
{ 'metadata' => { ':type' => 'metadata' } },
['GET']
);
$self->addAuthRoute(
( $self->{path} || 'saml' ) => { 'metadata' => { ':type' => 'metadata' }},
( $self->{path} || 'saml' ) =>
{ 'metadata' => { ':type' => 'metadata' } },
['GET']
);
return 1;
@ -3075,7 +3077,7 @@ sub metadata {
my $type = $req->param('type');
require Lemonldap::NG::Common::Conf::SAML::Metadata;
if ( my $metadata = Lemonldap::NG::Common::Conf::SAML::Metadata->new() ) {
my $s = $metadata->serviceToXML( $self->conf, $type);
my $s = $metadata->serviceToXML( $self->conf, $type );
return [
200,
[

View File

@ -10,95 +10,95 @@ use constant {
# Portal errors
# Developers warning, do not use PE_INFO, it's reserved to autoRedirect.
PE_IDPCHOICE => -5,
PE_SENDRESPONSE => -4,
PE_INFO => -3,
PE_REDIRECT => -2,
PE_DONE => -1,
PE_OK => 0,
PE_SESSIONEXPIRED => 1,
PE_FORMEMPTY => 2,
PE_WRONGMANAGERACCOUNT => 3,
PE_USERNOTFOUND => 4,
PE_BADCREDENTIALS => 5,
PE_LDAPCONNECTFAILED => 6,
PE_LDAPERROR => 7,
PE_APACHESESSIONERROR => 8,
PE_FIRSTACCESS => 9,
PE_BADCERTIFICATE => 10,
PE_PP_ACCOUNT_LOCKED => 21,
PE_PP_PASSWORD_EXPIRED => 22,
PE_CERTIFICATEREQUIRED => 23,
PE_ERROR => 24,
PE_PP_CHANGE_AFTER_RESET => 25,
PE_PP_PASSWORD_MOD_NOT_ALLOWED => 26,
PE_PP_MUST_SUPPLY_OLD_PASSWORD => 27,
PE_PP_INSUFFICIENT_PASSWORD_QUALITY => 28,
PE_PP_PASSWORD_TOO_SHORT => 29,
PE_PP_PASSWORD_TOO_YOUNG => 30,
PE_PP_PASSWORD_IN_HISTORY => 31,
PE_PP_GRACE => 32,
PE_PP_EXP_WARNING => 33,
PE_PASSWORD_MISMATCH => 34,
PE_PASSWORD_OK => 35,
PE_NOTIFICATION => 36,
PE_BADURL => 37,
PE_NOSCHEME => 38,
PE_BADOLDPASSWORD => 39,
PE_MALFORMEDUSER => 40,
PE_SESSIONNOTGRANTED => 41,
PE_CONFIRM => 42,
PE_MAILFORMEMPTY => 43,
PE_BADMAILTOKEN => 44,
PE_MAILERROR => 45,
PE_MAILOK => 46,
PE_LOGOUT_OK => 47,
PE_SAML_ERROR => 48,
PE_SAML_LOAD_SERVICE_ERROR => 49,
PE_SAML_LOAD_IDP_ERROR => 50,
PE_SAML_SSO_ERROR => 51,
PE_SAML_UNKNOWN_ENTITY => 52,
PE_SAML_DESTINATION_ERROR => 53,
PE_SAML_CONDITIONS_ERROR => 54,
PE_SAML_IDPSSOINITIATED_NOTALLOWED => 55,
PE_SAML_SLO_ERROR => 56,
PE_SAML_SIGNATURE_ERROR => 57,
PE_SAML_ART_ERROR => 58,
PE_SAML_SESSION_ERROR => 59,
PE_SAML_LOAD_SP_ERROR => 60,
PE_SAML_ATTR_ERROR => 61,
PE_OPENID_EMPTY => 62,
PE_OPENID_BADID => 63,
PE_MISSINGREQATTR => 64,
PE_BADPARTNER => 65,
PE_MAILCONFIRMATION_ALREADY_SENT => 66,
PE_PASSWORDFORMEMPTY => 67,
PE_CAS_SERVICE_NOT_ALLOWED => 68,
PE_MAILFIRSTACCESS => 69,
PE_MAILNOTFOUND => 70,
PE_PASSWORDFIRSTACCESS => 71,
PE_MAILCONFIRMOK => 72,
PE_RADIUSCONNECTFAILED => 73,
PE_MUST_SUPPLY_OLD_PASSWORD => 74,
PE_FORBIDDENIP => 75,
PE_CAPTCHAERROR => 76,
PE_CAPTCHAEMPTY => 77,
PE_REGISTERFIRSTACCESS => 78,
PE_REGISTERFORMEMPTY => 79,
PE_REGISTERALREADYEXISTS => 80,
PE_NOTOKEN => 81,
PE_TOKENEXPIRED => 82,
PE_U2FFAILED => 83,
PE_UNAUTHORIZEDPARTNER => 84,
PE_RENEWSESSION => 85,
PE_WAIT => 86,
PE_MUSTAUTHN => 87,
PE_MUSTHAVEMAIL => 88,
PE_SAML_SERVICE_NOT_ALLOWED => 89,
PE_OIDC_SERVICE_NOT_ALLOWED => 90,
PE_OID_SERVICE_NOT_ALLOWED => 91,
PE_GET_SERVICE_NOT_ALLOWED => 92,
PE_IDPCHOICE => -5,
PE_SENDRESPONSE => -4,
PE_INFO => -3,
PE_REDIRECT => -2,
PE_DONE => -1,
PE_OK => 0,
PE_SESSIONEXPIRED => 1,
PE_FORMEMPTY => 2,
PE_WRONGMANAGERACCOUNT => 3,
PE_USERNOTFOUND => 4,
PE_BADCREDENTIALS => 5,
PE_LDAPCONNECTFAILED => 6,
PE_LDAPERROR => 7,
PE_APACHESESSIONERROR => 8,
PE_FIRSTACCESS => 9,
PE_BADCERTIFICATE => 10,
PE_PP_ACCOUNT_LOCKED => 21,
PE_PP_PASSWORD_EXPIRED => 22,
PE_CERTIFICATEREQUIRED => 23,
PE_ERROR => 24,
PE_PP_CHANGE_AFTER_RESET => 25,
PE_PP_PASSWORD_MOD_NOT_ALLOWED => 26,
PE_PP_MUST_SUPPLY_OLD_PASSWORD => 27,
PE_PP_INSUFFICIENT_PASSWORD_QUALITY => 28,
PE_PP_PASSWORD_TOO_SHORT => 29,
PE_PP_PASSWORD_TOO_YOUNG => 30,
PE_PP_PASSWORD_IN_HISTORY => 31,
PE_PP_GRACE => 32,
PE_PP_EXP_WARNING => 33,
PE_PASSWORD_MISMATCH => 34,
PE_PASSWORD_OK => 35,
PE_NOTIFICATION => 36,
PE_BADURL => 37,
PE_NOSCHEME => 38,
PE_BADOLDPASSWORD => 39,
PE_MALFORMEDUSER => 40,
PE_SESSIONNOTGRANTED => 41,
PE_CONFIRM => 42,
PE_MAILFORMEMPTY => 43,
PE_BADMAILTOKEN => 44,
PE_MAILERROR => 45,
PE_MAILOK => 46,
PE_LOGOUT_OK => 47,
PE_SAML_ERROR => 48,
PE_SAML_LOAD_SERVICE_ERROR => 49,
PE_SAML_LOAD_IDP_ERROR => 50,
PE_SAML_SSO_ERROR => 51,
PE_SAML_UNKNOWN_ENTITY => 52,
PE_SAML_DESTINATION_ERROR => 53,
PE_SAML_CONDITIONS_ERROR => 54,
PE_SAML_IDPSSOINITIATED_NOTALLOWED => 55,
PE_SAML_SLO_ERROR => 56,
PE_SAML_SIGNATURE_ERROR => 57,
PE_SAML_ART_ERROR => 58,
PE_SAML_SESSION_ERROR => 59,
PE_SAML_LOAD_SP_ERROR => 60,
PE_SAML_ATTR_ERROR => 61,
PE_OPENID_EMPTY => 62,
PE_OPENID_BADID => 63,
PE_MISSINGREQATTR => 64,
PE_BADPARTNER => 65,
PE_MAILCONFIRMATION_ALREADY_SENT => 66,
PE_PASSWORDFORMEMPTY => 67,
PE_CAS_SERVICE_NOT_ALLOWED => 68,
PE_MAILFIRSTACCESS => 69,
PE_MAILNOTFOUND => 70,
PE_PASSWORDFIRSTACCESS => 71,
PE_MAILCONFIRMOK => 72,
PE_RADIUSCONNECTFAILED => 73,
PE_MUST_SUPPLY_OLD_PASSWORD => 74,
PE_FORBIDDENIP => 75,
PE_CAPTCHAERROR => 76,
PE_CAPTCHAEMPTY => 77,
PE_REGISTERFIRSTACCESS => 78,
PE_REGISTERFORMEMPTY => 79,
PE_REGISTERALREADYEXISTS => 80,
PE_NOTOKEN => 81,
PE_TOKENEXPIRED => 82,
PE_U2FFAILED => 83,
PE_UNAUTHORIZEDPARTNER => 84,
PE_RENEWSESSION => 85,
PE_WAIT => 86,
PE_MUSTAUTHN => 87,
PE_MUSTHAVEMAIL => 88,
PE_SAML_SERVICE_NOT_ALLOWED => 89,
PE_OIDC_SERVICE_NOT_ALLOWED => 90,
PE_OID_SERVICE_NOT_ALLOWED => 91,
PE_GET_SERVICE_NOT_ALLOWED => 92,
PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93,
};
# EXPORTER PARAMETERS
@ -126,7 +126,7 @@ our @EXPORT_OK = qw( PE_SENDRESPONSE PE_INFO PE_REDIRECT PE_DONE PE_OK
PE_REGISTERALREADYEXISTS PE_NOTOKEN PE_TOKENEXPIRED HANDLER PE_U2FFAILED
PE_UNAUTHORIZEDPARTNER PE_RENEWSESSION PE_IDPCHOICE PE_WAIT PE_MUSTAUTHN
PE_MUSTHAVEMAIL PE_SAML_SERVICE_NOT_ALLOWED PE_OIDC_SERVICE_NOT_ALLOWED
PE_OID_SERVICE_NOT_ALLOWED PE_GET_SERVICE_NOT_ALLOWED
PE_OID_SERVICE_NOT_ALLOWED PE_GET_SERVICE_NOT_ALLOWED PE_IMPERSONATION_SERVICE_NOT_ALLOWED
);
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK, 'import' ], );

View File

@ -291,6 +291,7 @@ sub display {
REGISTER_URL => $self->conf->{registerUrl},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
STAYCONNECTED => $self->conf->{stayConnected},
SPOOFID => $self->conf->{impersonationRule},
(
$req->data->{customScript}
? ( CUSTOM_SCRIPT => $req->data->{customScript} )

View File

@ -88,9 +88,9 @@ has csp => ( is => 'rw' );
sub init {
my ( $self, $args ) = @_;
$args ||= {};
$self->localConfig(
{ %{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
->getLocalConf('portal')
$self->localConfig( {
%{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
->getLocalConf('portal')
},
%$args
}
@ -101,8 +101,7 @@ sub init {
and -r $self->{localConfig}->{translations} )
{
open my $tr_file, '<', $self->{localConfig}->{translations}
or die "Can't open"
. $self->{localConfig}->{translations} . " : $!";
or die "Can't open" . $self->{localConfig}->{translations} . " : $!";
while (<$tr_file>) {
chomp;
$_ =~ /^([\w_]+)\s+=\s+(.+)$/;
@ -141,33 +140,33 @@ sub init {
# Handle requests (other path may be declared in enabled plugins)
$self
# "/" or undeclared paths
->addUnauthRoute( '*' => 'login', ['GET'] )
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
# "/" or undeclared paths
->addUnauthRoute( '*' => 'login', ['GET'] )
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
# Logout
->addAuthRoute( logout => 'logout', ['GET'] );
# Logout
->addAuthRoute( logout => 'logout', ['GET'] );
# Default routes must point to routines declared above
$self->defaultAuthRoute('');
@ -201,8 +200,8 @@ sub reloadConf {
$self->csp($csp);
# Initialize templateDir
$self->{templateDir}
= $self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
$self->{templateDir} =
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
unless ( -d $self->{templateDir} ) {
$self->error("Template dir $self->{templateDir} doesn't exist");
return $self->fail;
@ -222,8 +221,8 @@ sub reloadConf {
# Initialize persistent session DB
unless ( $self->conf->{persistentStorage} ) {
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
$self->conf->{persistentStorageOptions}
= $self->conf->{globalStorageOptions};
$self->conf->{persistentStorageOptions} =
$self->conf->{globalStorageOptions};
}
# Initialize cookie domain
@ -247,19 +246,19 @@ sub reloadConf {
return $self->fail;
}
$mod = $self->conf->{$type}
unless ( $self->conf->{$type} eq 'Same' );
unless ( $self->conf->{$type} eq 'Same' );
my $module = '::' . ucfirst($type) . '::' . $mod;
$module =~ s/Authentication/Auth/;
# Launch and initialize module
return $self->fail
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
}
# Load second-factor engine
return $self->fail
unless $self->{_sfEngine}
= $self->loadPlugin( $self->conf->{'sfEngine'} );
unless $self->{_sfEngine} =
$self->loadPlugin( $self->conf->{'sfEngine'} );
# Initialize trusted domain regexp
if ( $self->conf->{trustedDomains}
@ -282,8 +281,8 @@ sub reloadConf {
# - $domainlabel.$td
# $domainlabel is build looking RFC2396
# (see Regexp::Common::URI::RFC2396)
$_
=~ s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
$_ =~
s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
$re->add("$_");
}
}
@ -294,8 +293,8 @@ sub reloadConf {
$self->logger->debug("Vhost $vhost added in trusted domains");
$re->add( quotemeta($vhost) );
$self->conf->{vhostOptions} ||= {};
if ( my $tmp
= $self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
if ( my $tmp =
$self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
{
foreach my $alias ( split /\s+/, $tmp ) {
$self->logger->debug(
@ -313,22 +312,22 @@ sub reloadConf {
$self->{"_$type"} = {};
if ( $self->conf->{$type} ) {
for my $name ( sort keys %{ $self->conf->{$type} } ) {
my $sub = HANDLER->buildSub(
my $sub =
HANDLER->buildSub(
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
if ($sub) {
$self->{"_$type"}->{$name} = $sub;
}
else {
$self->logger->error( "$type $name returns an error: "
. HANDLER->tsv->{jail}->error );
. HANDLER->tsv->{jail}->error );
}
}
}
}
$self->{_jsRedirect}
= HANDLER->buildSub(
HANDLER->substitute( $self->conf->{jsRedirect} ) )
or $self->logger->error(
$self->{_jsRedirect} =
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
or $self->logger->error(
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
# Load plugins
@ -351,6 +350,9 @@ sub reloadConf {
return PE_OK;
}
};
my $portal = $self->conf->{portal};
$portal =~ s#^https?://(.*?)(?:/|$)/#$1#;
HANDLER->tsv->{defaultCondition}->{$portal} ||= sub { 1 };
1;
}
@ -364,7 +366,7 @@ sub loadPlugin {
}
my $obj;
return 0
unless ( $obj = $self->loadModule("$plugin") );
unless ( $obj = $self->loadModule("$plugin") );
return $self->findEP( $plugin, $obj );
}
@ -436,7 +438,7 @@ sub findEP {
if ( $obj->can('spRules') ) {
foreach my $k ( keys %{ $obj->{spRules} } ) {
$self->logger->info(
"$k is defined more than one time, it can have some bad effect on Menu display"
"$k is defined more than one time, it can have some bad effect on Menu display"
) if ( $self->spRules->{$k} );
$self->spRules->{$k} = $obj->{spRules}->{$k};
}

View File

@ -39,13 +39,13 @@ sub _addRoute {
return sub {
shift;
return $sub->( $self, @_ );
}
}
}
else {
return sub {
shift;
return $self->$sub(@_);
}
}
}
};
$self->p->$type( $word, $subName, $methods, $transform );
@ -57,21 +57,6 @@ sub loadTemplate {
return $self->p->loadTemplate(@_);
}
sub accessCtrl {
my ( $self, $req, $uri ) = @_;
my $url = $self->conf->{portal} . $uri;
$self->logger->debug("Plugin calls accessCtrl for URL: $url");
# Check access rule
my ( $vhost, $appuri ) = $url =~ m#^https?://([^/]*)(.*)#;
$vhost =~ s/:\d+$//;
$appuri ||= '/';
$self->logger->debug(
"grant function call with VH: $vhost and URI: $appuri");
return $self->p->HANDLER->grant( $req, $req->{userData}, $appuri,
undef, $vhost );
}
1;
__END__

View File

@ -26,6 +26,7 @@ our @pList = (
checkState => '::Plugins::CheckState',
portalForceAuthn => '::Plugins::ForceAuthn',
checkUser => '::Plugins::CheckUser',
impersonationRule => '::Plugins::Impersonation',
);
##@method list enabledPlugins

View File

@ -3,9 +3,10 @@ package Lemonldap::NG::Portal::Plugins::CheckUser;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_TOKENEXPIRED
PE_NOTOKEN
PE_BADCREDENTIALS
PE_TOKENEXPIRED
PE_NOTOKEN
PE_MALFORMEDUSER
);
our $VERSION = '2.0.3';
@ -18,8 +19,8 @@ has ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott = $_[0]->{p}
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{formTimeout} );
return $ott;
}
@ -27,7 +28,7 @@ has ott => (
sub hAttr {
$_[0]->{conf}->{checkUserHiddenAttributes} . ' '
. $_[0]->{conf}->{hiddenAttributes};
. $_[0]->{conf}->{hiddenAttributes};
}
sub init {
@ -44,28 +45,43 @@ sub check {
my ( $attrs, $array_attrs, $array_hdrs ) = ( {}, [], [] );
my $msg = my $auth = '';
# Check access rule
unless ( $self->accessCtrl( $req, 'checkuser' ) ) {
$self->userLogger->error(
"user $req->{user} not allowed to access /checkuser");
return $self->p->lmError( $req, 403 );
}
$self->userLogger->notice(
"user $req->{user} is allowed to access /checkuser");
# Check token
if ( $self->conf->{requireToken} ) {
my $token = $req->param('token');
unless ($token) {
$self->userLogger->warn('CheckUser try without token');
$msg = PE_NOTOKEN;
$token = $self->ott->createToken( $req->sessionInfo );
$token = $self->ott->createToken( $req->userData );
}
unless ( $self->ott->getToken($token) ) {
$self->userLogger->warn('Checkuser try with expired/bad token');
$msg = PE_TOKENEXPIRED;
$token = $self->ott->createToken( $req->sessionInfo );
$token = $self->ott->createToken( $req->userData );
}
my $params = {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => "PE$msg",
ALERTE => 'alert-warning',
TOKEN => $token,
};
return $self->p->sendJSONresponse( $req, $params )
if ( $req->wantJSON );
return $self->p->sendHtml( $req, 'checkuser', params => $params, )
if $msg;
}
## Check user session datas
# Use submitted attribute if exists
my $url = $req->param('url') || '';
my $user = $req->param('user') || '';
if ( $user and $user !~ /$self->{conf}->{userControl}/o ) {
$user = '';
$attrs = {};
return $self->p->sendError( $req, 'Malformed user', 400 )
if ( $req->wantJSON );
return $self->p->sendHtml(
$req,
'checkuser',
@ -73,22 +89,36 @@ sub check {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => "PE$msg",
MSG => 'PE' . PE_MALFORMEDUSER,
ALERTE => 'alert-warning',
TOKEN => $token,
LOGIN => $req->{user},
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->userData )
: ''
)
}
) if $msg;
);
}
if ( $user eq $req->{user} or !$user ) {
$self->userLogger->notice("Retrieve session from Sessions database");
$self->userLogger->warn("Using spoofed SSO groups if exist!!!")
if ( $self->conf->{impersonationRule} );
$attrs = $req->userData;
}
else {
$self->logger->debug("Check requested for $req->{user}");
$req->{user} = $user;
$self->userLogger->notice(
"Retrieve session from userDB and compute Groups & Macros");
$attrs = $self->_userDatas($req);
}
## Check user session datas
# Use submitted attribute if exists
my $url = $req->param('url') || '';
$req->{user} = $req->param('user') if ( $req->param('user') );
$self->logger->debug("Check requested for $req->{user}");
$attrs = $self->_userDatas($req);
if ( $req->error ) {
$msg = 'PE' . $req->{error};
$msg = 'PE' . $req->{error};
$array_attrs = [ [], [], [] ];
$attrs = {};
}
else {
$msg = 'checkUser';
@ -100,7 +130,7 @@ sub check {
# Ignore hidden attributes
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ );
unless ( $self->hAttr =~ /\b$k\b/ );
}
}
else {
@ -108,7 +138,7 @@ sub check {
# Ignore hidden attributes and empty values
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ or !$attrs->{$k} );
unless ( $self->hAttr =~ /\b$k\b/ or !$attrs->{$k} );
}
}
@ -120,87 +150,86 @@ sub check {
if ( $url and %$attrs ) {
# User is allowed ?
$url = 'http://' . $url unless ( $url =~ m#^https?://[^/]*.*# );
$auth = $self->_authorization( $req, $url );
$self->logger->debug(
"checkUser requested for user: $req->{user} and URL: $url");
$auth = $auth ? "allowed" : "forbidden";
$self->userLogger->notice( "checkUser -> $req->{user} is "
. uc($auth)
. " to access: $url" );
if ( $auth >= 0 ) {
# Return VirtualHost headers
$array_hdrs = $self->_headers( $req, $url );
$auth = $auth ? "allowed" : "forbidden";
$self->userLogger->notice( "checkUser -> $req->{user} is "
. uc($auth)
. " to access: $url" );
# Return VirtualHost headers
$array_hdrs = $self->_headers( $req, $url );
}
else {
$auth = 'VHnotFound';
$self->userLogger->notice(
"checkUser -> URL: $url has no configuration");
}
}
my $alert_auth = 'alert-warning';
if ( $auth eq 'allowed' ) { $alert_auth = 'alert-success' }
elsif ( $auth eq 'forbidden' ) { $alert_auth = 'alert-danger' }
# TODO:
my $params = {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => $msg,
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} ) ? ""
: $req->{user}
),
URL => (
$self->p->checkXSSAttack( 'URL', $url ) ? ""
: $url
),
ALLOWED => $auth,
ALERTE_AUTH => $alert_auth,
HEADERS => $array_hdrs,
ATTRIBUTES => $array_attrs->[2],
MACROS => $array_attrs->[1],
GROUPS => $array_attrs->[0],
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->userData )
: ''
)
};
return $self->p->sendJSONresponse( $req, $params ) if ( $req->wantJSON );
# Display form
return $self->p->sendHtml(
$req,
'checkuser',
params => {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => $msg,
ALERTE =>
( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} ) ? ""
: $req->{user}
),
URL => (
$self->p->checkXSSAttack( 'URL', $url ) ? ""
: $url
),
ALLOWED => $auth,
ALERTE_AUTH =>
( $auth eq 'allowed' ? 'alert-success' : 'alert-danger' ),
HEADERS => $array_hdrs,
ATTRIBUTES => $array_attrs->[2],
MACROS => $array_attrs->[1],
GROUPS => $array_attrs->[0],
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->sessionInfo )
: ''
)
}
);
return $self->p->sendHtml( $req, 'checkuser', params => $params, );
}
sub display {
my ( $self, $req ) = @_;
# Check access rule
unless ( $self->accessCtrl( $req, 'checkuser' ) ) {
$self->userLogger->error(
"user $req->{user} not allowed to access /checkuser");
return $self->p->lmError( $req, 403 );
}
$self->userLogger->notice(
"user $req->{user} is allowed to access /checkuser");
# Display form
return $self->p->sendHtml(
$req,
'checkuser',
params => {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => 'checkUser',
ALERTE => 'alert-info',
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} )
? ""
: $req->{user}
),
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->sessionInfo )
: ''
)
}
);
my $params = {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => 'checkUser',
ALERTE => 'alert-info',
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} ) ? ""
: $req->{user}
),
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->userData )
: ''
)
};
return $self->sendJSONresponse( $req, $params ) if ( $req->wantJSON );
return $self->p->sendHtml( $req, 'checkuser', params => $params, );
}
sub _userDatas {
@ -209,14 +238,13 @@ sub _userDatas {
# Search user in database
my $steps = [ 'getUser', 'setSessionInfo', 'setMacros', 'setGroups' ];
$self->conf->{checkUserDisplayPersistentInfo}
? push @$steps, 'setPersistentSessionInfo', 'setLocalGroups'
: push @$steps, 'setLocalGroups';
? push @$steps, 'setPersistentSessionInfo', 'setLocalGroups'
: push @$steps, 'setLocalGroups';
$req->steps($steps);
if ( my $error = $self->p->process($req) ) {
if ( $error == PE_BADCREDENTIALS ) {
$self->userLogger->warn( 'Check requested for an unvalid user ('
. $req->{user}
. ")" );
$self->userLogger->warn(
'Check requested for an unvalid user (' . $req->{user} . ")" );
}
$self->logger->debug("Process returned error: $error");
return $req->error($error);
@ -227,10 +255,21 @@ sub _userDatas {
sub _authorization {
my ( $self, $req, $uri ) = @_;
my ( $vhost, $appuri ) = $uri =~ m#^https?://([^/]*)(.*)#;
my $exist = 0;
$vhost =~ s/:\d+$//;
$appuri ||= '/';
return $self->p->HANDLER->grant( $req, $req->{sessionInfo}, $appuri,
undef, $vhost );
foreach my $vh ( keys %{ $self->conf->{locationRules} } ) {
if ( $vh eq $vhost ) {
$exist = 1;
$self->logger->debug("VirtualHost: $vh found in Conf");
last;
}
}
return $exist
? $self->p->HANDLER->grant( $req, $req->{userData}, $appuri,
undef, $vhost )
: -1;
}
sub _headers {
@ -239,7 +278,7 @@ sub _headers {
$vhost =~ s/:\d+$//;
$req->{env}->{HTTP_HOST} = $vhost;
$self->p->HANDLER->headersInit( $self->{conf} );
return $self->p->HANDLER->checkHeaders( $req, $req->{sessionInfo} );
return $self->p->HANDLER->checkHeaders( $req, $req->{userData} );
}
sub _splitAttributes {
@ -249,7 +288,9 @@ sub _splitAttributes {
$self->logger->debug("Dispatching attributes...");
while (@$attrs) {
my $element = shift @$attrs;
my $ok = 0;
$self->logger->debug(
'Processing element: ' . Data::Dumper::Dumper($element) );
my $ok = 0;
if ( $element->{key} eq 'groups' ) {
$self->logger->debug('Key "groups" found');
my $separator = $self->{conf}->{multiValuesSeparator};
@ -257,11 +298,14 @@ sub _splitAttributes {
$grps = [ map { { value => $_ } } sort @tmp ];
next;
}
foreach my $key ( sort keys %$macros ) {
if ( $element->{key} eq $key ) {
push @$mcrs, $element;
$ok = 1;
last;
if (%$macros) {
foreach my $key ( sort keys %$macros ) {
if ( $element->{key} eq $key ) {
$self->logger->debug('Macro found');
push @$mcrs, $element;
$ok = 1;
last;
}
}
}
push @$others, $element unless $ok;

View File

@ -0,0 +1,142 @@
package Lemonldap::NG::Portal::Plugins::Impersonation;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants
qw( PE_OK PE_BADCREDENTIALS PE_IMPERSONATION_SERVICE_NOT_ALLOWED PE_MALFORMEDUSER );
our $VERSION = '2.0.3';
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
use constant endAuth => 'run';
has rule => ( is => 'rw', default => sub { 1 } );
sub hAttr {
$_[0]->{conf}->{impersonationHiddenAttributes} . ' '
. $_[0]->{conf}->{hiddenAttributes};
}
sub init {
my ($self) = @_;
# Parse activation rule
my $hd = $self->p->HANDLER;
$self->logger->debug(
"impersonation rule -> " . $self->conf->{impersonationRule} );
my $rule =
$hd->buildSub( $hd->substitute( $self->conf->{impersonationRule} ) );
unless ($rule) {
$self->error( "Bad impersonation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->{rule} = $rule;
return 1;
}
# RUNNING METHOD
sub run {
my ( $self, $req ) = @_;
my $spoofId = $req->param('spoofId') || '';
if ( $spoofId
and $req->param('spoofId') !~ /$self->{conf}->{userControl}/o )
{
return PE_MALFORMEDUSER;
}
# Skip if no submitted SpoofId
return PE_OK unless $spoofId;
# Check activation rule
unless ( $self->rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->error('Impersonation service not authorized');
return PE_IMPERSONATION_SERVICE_NOT_ALLOWED;
}
# Fill spoof session
my ( $realSession, $spoofSession ) = ( {}, {} );
$self->logger->debug("Spoofing Id: $spoofId...");
my $spk = '';
foreach my $k ( keys %{ $req->{sessionInfo} } ) {
if ( $self->{conf}->{impersonationSkipEmptyValues} ) {
next unless defined $req->{sessionInfo}->{$k};
}
$spk = "$self->{conf}->{impersonationPrefix}$k";
unless ( $self->hAttr =~ /\b$k\b/ ) {
$realSession->{$spk} = $req->{sessionInfo}->{$k};
$self->logger->debug("-> Store $k in realSession key: $spk");
}
$self->logger->debug("Delete $k");
delete $req->{sessionInfo}->{$k};
}
$req->{user} = $spoofId;
$spoofSession = $self->_userDatas($req);
$spoofSession->{groups} ||= '';
# Merging SSO groups and hGroups & Dedup
if ( $self->{conf}->{impersonationMergeSSOgroups} ) {
$self->userLogger->warn("MERGING SSO groups and hGroups...");
my $spg = "$self->{conf}->{impersonationPrefix}groups";
my $sphg = "$self->{conf}->{impersonationPrefix}hGroups";
my $separator = $self->{conf}->{multiValuesSeparator};
$realSession->{$spg} ||= '';
$self->logger->debug("Processing groups...");
my @spoofGrps = my @realGrps = ();
@spoofGrps = split /\Q$separator/, $spoofSession->{groups};
@realGrps = split /\Q$separator/, $realSession->{$spg};
@spoofGrps = ( @spoofGrps, @realGrps );
my %hash = map { $_, 1 } @spoofGrps;
$spoofSession->{groups} = join $separator, sort keys %hash;
$self->logger->debug("Processing hGroups...");
$spoofSession->{hGroups} ||= {};
$realSession->{$sphg} ||= {};
$spoofSession->{hGroups} =
{ %{ $spoofSession->{hGroups} }, %{ $realSession->{$sphg} } };
}
# Create spoofed session
foreach (qw (_auth _userDB)) {
$self->logger->debug("Processing $_...");
$spk = "$self->{conf}->{impersonationPrefix}$_";
$spoofSession->{$_} = $realSession->{$spk};
}
$spoofSession = { %$spoofSession, %$realSession };
# Main session
$self->p->updateSession( $req, $spoofSession );
return PE_OK;
}
sub _userDatas {
my ( $self, $req ) = @_;
$req->{sessionInfo} = {};
# Search user in database
$req->steps( [
'getUser', 'setSessionInfo',
'setMacros', 'setGroups',
'setLocalGroups'
]
);
if ( my $error = $self->p->process($req) ) {
if ( $error == PE_BADCREDENTIALS ) {
$self->userLogger->warn(
'Impersonation requested for an unvalid user ('
. $req->{user}
. ")" );
}
$self->logger->debug("Process returned error: $error");
return $req->error($error);
}
$self->logger->debug("Populating spoofed session...");
return $req->{sessionInfo};
}
1;

View File

@ -154,4 +154,8 @@ div.oidc_consent_message > ul {
.nodecor:hover, .nodecor:active.nodecor:focus {
text-decoration: none;
}
.fa.icon-blue {
color: blue;
}

View File

@ -1 +1 @@
html,body{height:100%;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}#wrap{min-height:100%;height:auto;margin:0 auto -80px;padding:20px 0 80px}#footer{height:80px;background-color:#fff;background-color:rgba(255,255,255,0.9);text-align:center;padding-top:10px;overflow:hidden}#header img{background-color:#fff;background-color:rgba(255,255,255,0.8);margin-bottom:20px}.card,.navbar-light{background-color:#fff;background-color:rgba(255,255,255,0.9);background-image:none}.login,.password{text-align:center;padding:20px}div.form{margin:0 auto;max-width:330px}div.actions{margin:10px 0 0 0}div.actions a{margin-top:10px}.buttons{text-align:center;margin:10px 0 0 0;cursor:pointer}.btn{white-space:normal}.btn span.fa{padding-right:8px}li.ui-state-active{background-color:#fafafa;background-color:rgba(250,250,250,0.9)}#appslist,#password,#loginHistory,#logout,#oidcConsents{margin-top:20px}div.category{margin:10px 0;cursor:grab}div.application{margin:5px 0;overflow:hidden}div.application a,div.application a:hover{text-decoration:none}p.notifCheck label{margin-left:5px;margin-top:3px;display:inline-block}img.langicon{cursor:pointer}button.idploop{max-width:300px}button.idploop img{max-height:30px}div.oidc_consent_message>ul{text-align:left;list-style:circle}@media(min-width:768px){div.application{height:80px}div.application h4.appname{margin:0}#wrap{margin:0 auto -60px}#footer{height:60px}}.hiddenFrame{border:0;display:hidden;margin:0}.noborder{border:0}.max{width:100%}.link{cursor:pointer}.nodecor:hover,.nodecor:active.nodecor:focus{text-decoration:none}
html,body{height:100%;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}#wrap{min-height:100%;height:auto;margin:0 auto -80px;padding:20px 0 80px}#footer{height:80px;background-color:#fff;background-color:rgba(255,255,255,0.9);text-align:center;padding-top:10px;overflow:hidden}#header img{background-color:#fff;background-color:rgba(255,255,255,0.8);margin-bottom:20px}.card,.navbar-light{background-color:#fff;background-color:rgba(255,255,255,0.9);background-image:none}.login,.password{text-align:center;padding:20px}div.form{margin:0 auto;max-width:330px}div.actions{margin:10px 0 0 0}div.actions a{margin-top:10px}.buttons{text-align:center;margin:10px 0 0 0;cursor:pointer}.btn{white-space:normal}.btn span.fa{padding-right:8px}li.ui-state-active{background-color:#fafafa;background-color:rgba(250,250,250,0.9)}#appslist,#password,#loginHistory,#logout,#oidcConsents{margin-top:20px}div.category{margin:10px 0;cursor:grab}div.application{margin:5px 0;overflow:hidden}div.application a,div.application a:hover{text-decoration:none}p.notifCheck label{margin-left:5px;margin-top:3px;display:inline-block}img.langicon{cursor:pointer}button.idploop{max-width:300px}button.idploop img{max-height:30px}div.oidc_consent_message>ul{text-align:left;list-style:circle}@media(min-width:768px){div.application{height:80px}div.application h4.appname{margin:0}#wrap{margin:0 auto -60px}#footer{height:60px}}.hiddenFrame{border:0;display:hidden;margin:0}.noborder{border:0}.max{width:100%}.link{cursor:pointer}.nodecor:hover,.nodecor:active.nodecor:focus{text-decoration:none}.fa.icon-blue{color:blue}

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"قبول",
"accessDenied":"ليس لديك إذن بالدخول لهذا التطبيق",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"غير كلمة المرور الخاصة بك",
"checkLastLogins":"تحقق من آخر تسجيلات دخول الخاصة بي",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"اختر أحد التطبيقات المسموح لك بالدخول إليها",
"clickHere":"الرجاء الضغط هنا",
@ -211,6 +212,7 @@
"resetPwd":"إعادة تعيين كلمة المرور الخاصة بي",
"rightsReloadNeedsLogout":" إعادة تحميل الحقوق تحتاج إلى تسجيل الخروج وتسجيل الدخول مرة أخرى",
"scope":"Scope",
"search":"Search",
"selectIdP":"اختر موفر الهوية الخاص بك",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"الخدمة المقدمة من قبل",
"sessionsDeleted":"الجلسات التالية تم غلقها",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"جلسة الدخول الموحد غير نشطة",
"stayConnected":"ابق على اتصال على هذا الجهاز",
"submit":"قدم",
@ -237,6 +240,7 @@
"useYubikey":"استخدم اليوبي كي الخاص بك",
"value":"Value",
"verify":"التحقق",
"VHnotFound":"Virtual Host not found",
"wait":"انتظر",
"warning":"تحذير",
"welcomeOnPortal":"مرحبا بك على بوابة إثبات الهوية الآمنة.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"Dieser Dienst benötigt Zwei-Faktor-Authentifizierung. Bitte legen Sie ein Gerät an und gehen dann zum Portal zurück.",
"accept":"Akzeptieren",
"accessDenied":"Sie haben keine Zugriffsberechtigung für diese Anwendung",
@ -116,7 +117,7 @@
"changeKey":"Neuen Schlüssel erzeugen",
"changePwd":"Ändere dein Passwort",
"checkLastLogins":"Überprüfe meine letzten Logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Wählen deinen Ihren zweiten Faktor",
"chooseApp":"Wählen Sie eine Anwendung aus, auf die du zugreifen darfst",
"clickHere":"Bitte hier klicken",
@ -211,6 +212,7 @@
"resetPwd":"Mein Passwort zurücksetzen",
"rightsReloadNeedsLogout":"Zum Neuladen der Rechte musst du dich ab- und wieder anmelden",
"scope":"Scope",
"search":"Search",
"selectIdP":"Wähle deinen Identitätsanbieter aus",
"service":"Dienst",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Dienst angeboten von",
"sessionsDeleted":"Die folgenden Sitzungen wurden geschlossen",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO Sitzung inaktiv",
"stayConnected":"Auf diesem Gerät verbunden bleiben",
"submit":"Absenden",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Warten",
"warning":"Warnung",
"welcomeOnPortal":"Willkommen in Ihrem gesicherten Authentifizierungsportal.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -116,7 +117,7 @@
"changeKey": "Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -210,7 +211,8 @@
"resentConfirm":"Do you want the confirmation mail to be resent?",
"resetPwd":"Reset my password",
"rightsReloadNeedsLogout": "Rights reloads need to logout and login again",
"scope": "Scope",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected": "Stay connected on this device",
"submit":"Submit",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify": "Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -211,6 +212,7 @@
"resetPwd":"Reset my password",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected":"Stay connected on this device",
"submit":"Submit",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",

View File

@ -92,6 +92,7 @@
"PE90":"Accès non autorisé au service OIDC",
"PE91":"Accès non autorisé au service OID",
"PE92":"Accès non autorisé au service GET",
"PE93":"Accès non autorisé au service d'Usurpation d'Identité",
"2fRegRequired":"Ce service requiert une authentification à deux facteurs. Enregistrez un équipement ici et retournez au portail.",
"accept":"Accepter",
"accessDenied":"Vous n'avez pas les droits d'accès à cette application",
@ -116,7 +117,7 @@
"changeKey": "Générer une nouvelle clef",
"changePwd":"Changez votre mot de passe",
"checkLastLogins":"Voir mes dernières connexions",
"checkUser":"Vérifier la session d'un utilisateur",
"checkUser":"Vérifier le profil SSO d'un utilisateur",
"choose2f":"Choisissez votre second facteur",
"chooseApp":"Choisissez une application à laquelle vous êtes autorisé à accéder",
"clickHere":"Cliquez ici",
@ -211,6 +212,7 @@
"resetPwd":"Réinitialiser mon mot de passe",
"rightsReloadNeedsLogout": "Le rechargement des droits nécessite une déconnexion",
"scope": "Informations",
"search":"Chercher",
"selectIdP":"Choisissez votre fournisseur d'identité",
"service":"Service",
"sendPwd":"Envoyez-moi un lien",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Ce service est fourni par",
"sessionsDeleted":"Les sessions suivantes ont été fermées",
"sfaManager":"Gestionnaire 2ndFA",
"spoofId":"Identifiant usurpé",
"SSOSessionInactive":"Session SSO inactive",
"stayConnected": "Rester connecté sur cet appareil",
"submit":"Envoyer",
@ -237,6 +240,7 @@
"useYubikey":"Utilisez votre Yubikey",
"value":"Valeur",
"verify": "Vérifier",
"VHnotFound":"Hôte virtuel erroné ou inexistant",
"wait":"Attendre",
"warning":"Attention",
"welcomeOnPortal":"Bienvenue sur votre portail d'authentification sécurisée.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"Questo servizio richiede un'autenticazione a doppio fattore. Registrare un dispositivo ora, quindi tornare al portale.",
"accept":"Accetta",
"accessDenied":"Non hai un'autorizzazione di accesso per questa applicazione",
@ -116,7 +117,7 @@
"changeKey":"Genera nuova chiave",
"changePwd":"Cambia la tua password",
"checkLastLogins":"Controllare i miei ultimi accessi",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Scegli il tuo secondo fattore",
"chooseApp":"Scegli un'applicazione alla quale ti è consentito l'accesso",
"clickHere":"Per favore clicka qui",
@ -211,6 +212,7 @@
"resetPwd":"Reimpostare la password",
"rightsReloadNeedsLogout":"Le ricariche dei diritti necessitano di disconnettersi e di riconnettersi",
"scope":"Ambito",
"search":"Search",
"selectIdP":"Seleziona il tuo provider di identità",
"service":"Servizio",
"sendPwd":"Inviami il link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Servizio offerto da",
"sessionsDeleted":"Le sessioni seguenti sono state chiuse",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"Sessione SSO inattiva",
"stayConnected":"Resta connesso su questo dispositivo",
"submit":"Invia",
@ -237,6 +240,7 @@
"useYubikey":"Usa la tua Yubikey",
"value":"Value",
"verify":"Verifica",
"VHnotFound":"Virtual Host not found",
"wait":"Attendere",
"warning":"Avvertimento",
"welcomeOnPortal":"Benvenuto sul tuo portale di autenticazione protetta.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -211,6 +212,7 @@
"resetPwd":"Reset my password",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected":"Stay connected on this device",
"submit":"Submit",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -211,6 +212,7 @@
"resetPwd":"Reset my password",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected":"Stay connected on this device",
"submit":"Submit",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -211,6 +212,7 @@
"resetPwd":"Reset my password",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected":"Stay connected on this device",
"submit":"Submit",
@ -237,6 +240,7 @@
"useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Chấp nhận",
"accessDenied":"Bạn không có quyền truy cập vào ứng dụng này",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"Thay đổi mật khẩu của bạn",
"checkLastLogins":"Kiểm tra lần đăng nhập cuối cùng của bạn",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Chọn một ứng dụng bạn được phép truy cập vào",
"clickHere":"Vui lòng nhấp vào đây",
@ -211,6 +212,7 @@
"resetPwd":"Đặt lại mật khẩu của tôi",
"rightsReloadNeedsLogout":"Tải lại quyền cần đăng xuất và đăng nhập lại",
"scope":"Scope",
"search":"Search",
"selectIdP":"Chọn bộ cung cấp danh tính của bạn",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Dịch vụ được cung cấp bởi",
"sessionsDeleted":"Các phiên làm việc sau đã được đóng lại",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"Phiên SSO không hoạt động",
"stayConnected":"Giữ kết nối trên thiết bị này",
"submit":"Gửi",
@ -237,6 +240,7 @@
"useYubikey":"sử dụng Yubikey của bạn",
"value":"Value",
"verify":"Xác minh",
"VHnotFound":"Virtual Host not found",
"wait":"Hãy đợi",
"warning":"Cảnh báo",
"welcomeOnPortal":"Chào mừng bạn đến với cổng thông tin xác thực được bảo mật của bạn.",

View File

@ -92,6 +92,7 @@
"PE90":"Access non granted on OIDC service",
"PE91":"Access non granted on OID service",
"PE92":"Access non granted on GET service",
"PE93":"Access non granted on IMPERSONATION service",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept 方法",
"accessDenied":"您无权访问此应用",
@ -116,7 +117,7 @@
"changeKey":"Generate new key",
"changePwd":"修改您的密码",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"checkUser":"Check user SSO profile",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"请点击这里",
@ -211,6 +212,7 @@
"resetPwd":"重置我的密码",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"selectIdP":"Select your Identity Provider",
"service":"Service",
"sendPwd":"Send me a link",
@ -218,6 +220,7 @@
"serviceProvidedBy":"Service provided by",
"sessionsDeleted":"The following sessions have been closed",
"sfaManager":"2ndFA Manager",
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"stayConnected":"Stay connected on this device",
"submit":"提交",
@ -237,6 +240,7 @@
"useYubikey":"使用您的 Yubikey",
"value":"Value",
"verify":"验证",
"VHnotFound":"Virtual Host not found",
"wait":"等待",
"warning":"警告",
"welcomeOnPortal":"欢迎来到您的加密认证 portal",
@ -254,4 +258,4 @@
"yourPhone":"您的电话号码",
"yourProfile":"您的档案",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -6,6 +6,7 @@
-->
<div class="alert <TMPL_VAR NAME="ALERTE"> alert"><span trspan="<TMPL_VAR NAME="MSG">"></span></div>
<form id="checkuser" action="/checkuser" method="post" class="password" role="form">
<div class="buttons">
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
@ -21,26 +22,23 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-link"></i> </span>
</div>
<input name="url" type="text" class="form-control" value="<TMPL_VAR NAME="URL">" trplaceholder="http://auth.example.com" aria-required="true"/>
<input name="url" type="text" class="form-control" value="<TMPL_VAR NAME="URL">" trplaceholder="URL / DNS" aria-required="true"/>
</div>
<TMPL_IF NAME="ALLOWED">
<div class="alert <TMPL_VAR NAME="ALERTE_AUTH">"><span trspan="<TMPL_VAR NAME="ALLOWED">"></span></div>
</TMPL_IF>
<TMPL_IF NAME="HEADERS">
<div class="buttons">
<button type="submit" class="btn btn-success">
<span class="fa fa-sign-in"></span>
<span trspan="checkUser">Check user</span>
<span class="fa fa-search"></span>
<span trspan="search">Search</span>
</button>
</div>
<div>&nbsp;</div>
&nbsp;
<TMPL_IF NAME="ALLOWED">
<div class="alert <TMPL_VAR NAME="ALERTE_AUTH">"><b><span trspan="<TMPL_VAR NAME="ALLOWED">"></span></b></div>
</TMPL_IF>
<TMPL_IF NAME="HEADERS">
<div class="card mb-3 border-secondary">
<div class="card-body table-responsive">
<table class="table table-hover">
<thead>
<tr class="align-middle"><span trspan="headers">HEADERS</span></tr>
<tr class="align-middle"><b><span trspan="headers">HEADERS</span></b></tr>
<tr>
<th class="align-middle"><span trspan="key">Key</span></th>
<th class="align-middle"><span trspan="value">Value</span></th>
@ -62,11 +60,11 @@
<div class="container">
<div class="row">
<TMPL_IF NAME="GROUPS">
<div class="card col-md-4 border-secondary">
<div class="card col-md-2 border-secondary">
<div class="card-body table-responsive">
<table class="table table-hover">
<thead>
<tr class="align-middle"><span trspan="groups_sso">GROUPS SSO</span></tr>
<tr class="align-middle"><b><span trspan="groups_sso">SSO GROUPS</span></b></tr>
</thead>
<tbody>
<TMPL_LOOP NAME="GROUPS">
@ -85,7 +83,7 @@
<div class="card-body table-responsive">
<table class="table table-hover">
<thead>
<tr class="align-middle"><span trspan="macros">MACROS</span></tr>
<tr class="align-middle"><b><span trspan="macros">MACROS</span></b></tr>
<tr>
<th class="align-middle"><span trspan="key">Key</span></th>
<th class="align-middle"><span trspan="value">Value</span></th>
@ -105,21 +103,21 @@
</TMPL_IF>
<TMPL_IF NAME="ATTRIBUTES">
<div class="card col-md-4 border-secondary">
<div class="card col-md-6 border-secondary">
<div class="card-body table-responsive">
<table class="table table-hover">
<thead>
<tr class="align-middle"><span trspan="attributes">ATTRIBUTES</span></tr>
<tr class="align-middle"><b><span trspan="attributes">ATTRIBUTES</span></b></tr>
<tr>
<th class="align-middle"><span trspan="key">Key</span></th>
<th class="align-middle"><span trspan="value">Value</span></th>
<th class="text-left"><span trspan="key">Key</span></th>
<th class="text-left"><span trspan="value">Value</span></th>
</tr>
</thead>
<tbody>
<TMPL_LOOP NAME="ATTRIBUTES">
<tr>
<td class="align-middle"><TMPL_VAR NAME="key"></td>
<td class="align-middle"><TMPL_VAR NAME="value"></td>
<td class="text-left"><TMPL_VAR NAME="key"></td>
<td class="text-left"><TMPL_VAR NAME="value"></td>
</tr>
</TMPL_LOOP>
</tbody>
@ -131,10 +129,12 @@
</div>
<div class="buttons">
<!--
<button type="submit" class="btn btn-success">
<span class="fa fa-sign-in"></span>
<span trspan="checkUser">Check user</span>
<span trspan="search">Search</span>
</button>
-->
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
<span class="fa fa-home"></span>
<span trspan="goToPortal">Go to portal</span>

View File

@ -28,6 +28,7 @@
</TMPL_IF>
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success" >

View File

@ -7,7 +7,7 @@
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- //if:usedebianlibs
<link rel="stylesheet" type="text/css" href="/javascript/bootstrap4/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/javascript/font-awesome/css/font-awesome.min.css" />

View File

@ -0,0 +1,8 @@
<TMPL_IF NAME="SPOOFID">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user icon-blue"></i> </span>
</div>
<input name="spoofId" type="text" class="form-control" trplaceholder="spoofId" aria-required="false"/>
</div>
</TMPL_IF>

View File

@ -73,6 +73,8 @@
<TMPL_IF NAME="logoFile">
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/modules/<TMPL_VAR NAME="logoFile">" alt="<TMPL_VAR NAME="module">" class="img-thumbnail mb-3" />
</TMPL_IF>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<div class="buttons">
@ -200,6 +202,7 @@
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/modules/<TMPL_VAR NAME="module">.png" alt="<TMPL_VAR NAME="module">" class="img-thumbnail" />
</TMPL_IF>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<div class="buttons">

View File

@ -6,6 +6,7 @@
<input name="openid_identifier" type="text" class="form-control" trplaceholder="enterOpenIDLogin" aria-required="true"/>
</div>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success" >

View File

@ -10,6 +10,7 @@
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/modules/SSL.png" alt="<TMPL_VAR NAME="module">" class="img-thumbnail mb-3" />
</div>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success sslclick" >

View File

@ -10,6 +10,7 @@
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/modules/SSL.png" alt="<TMPL_VAR NAME="module">" class="img-thumbnail mb-3" />
</div>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success sslclick" >

View File

@ -28,6 +28,7 @@
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success" >

View File

@ -6,6 +6,7 @@
<input name="yubikeyOTP" type="text" class="form-control" trplaceholder="enterYubikey" aria-required="true" autocomplete="off" />
</div>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
<button type="submit" class="btn btn-success" >

View File

@ -25,11 +25,9 @@ ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu' );
ok( $res->[2]->[0] !~ m%<span id="languages"></span>%,
' No language icon found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"trOver"%,
' trOver found' )
ok( $res->[2]->[0] =~ m%"trOver"%, ' trOver found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"all":\{\}%,
' all found' )
ok( $res->[2]->[0] =~ m%"all":\{\}%, ' all found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"en":\{"PE9":"You are welcome! Please login..."\}%,
' en found' )
@ -40,14 +38,26 @@ ok( $res->[2]->[0] =~ m%"PE0":"Souriez, vous êtes surveillés !"%,
ok( $res->[2]->[0] =~ m%"selectIdP":"Portail de Fédération des Identités"%,
' selectIdP found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"fr":\{%,
' fr found' )
ok( $res->[2]->[0] =~ m%"fr":\{%, ' fr found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"PE85":"From lemonlap-ng.ini"%,
' PE85 found' )
ok( $res->[2]->[0] =~ m%"PE85":"From lemonlap-ng.ini"%, ' PE85 found' )
or print STDERR Dumper( $res->[2]->[0] );
count(9);
# Try yo authenticate
# -------------------
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho*&password=dwho'),
accept => 'text/html',
length => 24
),
'Auth query'
);
ok( $res->[2]->[0] =~ m%<span trmsg="40"></span>%, ' PE40 found' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
# Try yo authenticate
# -------------------

View File

@ -13,8 +13,7 @@ SKIP: {
skip( 'LLNGTESTLDAP is not set', $maintests ) unless ( $ENV{LLNGTESTLDAP} );
require 't/test-ldap.pm';
my $client = LLNG::Manager::Test->new(
{
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
useSafeJail => 1,

View File

@ -86,7 +86,8 @@ SKIP: {
'Post authentication'
);
ok( $res->[2]->[0] =~ /trmsg="89"/, 'Reject reason is 89' )
or print STDERR Dumper( $res->[2]->[0] );
or print STDERR Dumper( $res->[2]->[0] );
# Simple SP access
ok(
$res = $sp->_get(

View File

@ -111,7 +111,7 @@ ok(
);
count(1);
ok( $res->[2]->[0] =~ /trmsg="68"/, 'Reject reason is 68' )
or print STDERR Dumper( $res->[2]->[0] );
or print STDERR Dumper( $res->[2]->[0] );
count(1);
# Simple SP access
@ -143,8 +143,7 @@ $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
$body = $res->[2]->[0];
$body =~ s/^.*?<form.*?>//s;
$body =~ s#</form>.*$##s;
%fields =
( $body =~ /<input type="hidden".+?name="(.+?)".+?value="(.*?)"/sg );
%fields = ( $body =~ /<input type="hidden".+?name="(.+?)".+?value="(.*?)"/sg );
$fields{user} = $fields{password} = 'french';
use URI::Escape;
$s = join( '&', map { "$_=" . uri_escape( $fields{$_} ) } keys %fields );

View File

@ -114,7 +114,7 @@ ok(
);
count(1);
ok( $res->[2]->[0] =~ /trmsg="90"/, 'Reject reason is 90' )
or print STDERR Dumper( $res->[2]->[0] );
or print STDERR Dumper( $res->[2]->[0] );
count(1);
# Initialization

View File

@ -106,7 +106,7 @@ SKIP: {
'Try to authenticate'
);
ok( $res->[2]->[0] =~ /trmsg="91"/, 'Reject reason is 91' )
or print STDERR Dumper( $res->[2]->[0] );
or print STDERR Dumper( $res->[2]->[0] );
count(1);
# Simple SP access

View File

@ -45,7 +45,7 @@ ok(
);
count(1);
ok( $res->[2]->[0] =~ /trmsg="92"/, 'Reject reason is 92' )
or print STDERR Dumper( $res->[2]->[0] );
or print STDERR Dumper( $res->[2]->[0] );
count(1);
# Try to authenticate with an authorized user

View File

@ -8,34 +8,33 @@ BEGIN {
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',
checkUser => 1,
requireToken => 1,
formTimeout => 2,
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
loginHistoryEnabled => 0,
brutForceProtection => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
checkUser => 1,
requireToken => 1,
formTimeout => 2,
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
}
);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ),
'Get Menu', );
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=dwho/;
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
@ -50,7 +49,8 @@ expectRedirection( $res, 'http://auth.example.com/' );
# CheckUser form
# ------------------------
ok( $res = $client->_get(
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
@ -58,11 +58,10 @@ ok( $res = $client->_get(
'CheckUser form',
);
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
# Expired token
@ -70,7 +69,8 @@ sleep 3;
$query =~ s/user=dwho/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -79,18 +79,18 @@ ok( $res = $client->_post(
),
'POST checkuser'
);
ok( $res->[2]->[0] =~ m%<span trspan="PE82"></span>%,
'Found PE_TOKENEXPIRED' )
or explain( $res->[2]->[0], 'trspan="PE82"' );
ok( $res->[2]->[0] =~ m%<span trspan="PE82"></span>%, 'Found PE_TOKENEXPIRED' )
or explain( $res->[2]->[0], 'trspan="PE82"' );
count(2);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
# Valid token
$query =~ s/user=/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
$query =~ s/url=/url=test1.example.com/;
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -101,45 +101,45 @@ ok( $res = $client->_post(
);
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok( $res->[2]->[0]
=~ m%<div class="alert alert-success"><span trspan="allowed"></span></div>%,
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url', 'token' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok(
$res->[2]->[0] =~
m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
'Found trspan="allowed"'
) or explain( $res->[2]->[0], 'trspan="allowed"' );
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] =~ m%<span trspan="groups_sso">%,
'Found trspan="groups_sso"' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
'Found trspan="attributes"' )
or explain( $res->[2]->[0], 'trspan="attributes"' );
or explain( $res->[2]->[0], 'trspan="attributes"' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">Auth-User</td>%,
'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%,
'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%, 'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">su</td>%, 'Found su' )
or explain( $res->[2]->[0], 'SSO Groups: su' );
or explain( $res->[2]->[0], 'SSO Groups: su' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Macro Value uid' );
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="text-left">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Attribute Value uid' );
count(11);
$query =~ s/user=dwho/user=msmith/;
$query
=~ s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
$query =~
s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -148,8 +148,9 @@ ok( $res = $client->_post(
),
'POST checkuser'
);
ok( $res->[2]->[0]
=~ m%<div class="alert alert-danger"><span trspan="forbidden"></span></div>%,
ok(
$res->[2]->[0] =~
m%<div class="alert alert-danger"><b><span trspan="forbidden"></span></b></div>%,
'Found trspan="forbidden"'
) or explain( $res->[2]->[0], 'trspan="forbidden"' );
count(2);
@ -157,4 +158,4 @@ count(2);
$client->logout($id);
clean_sessions();
done_testing( count() );
done_testing( count() );

View File

@ -8,16 +8,16 @@ BEGIN {
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',
checkUser => 1,
requireToken => 0,
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
loginHistoryEnabled => 0,
brutForceProtection => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
checkUser => 1,
requireToken => 0,
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
@ -25,9 +25,10 @@ my $client = LLNG::Manager::Test->new(
);
## Try to authenticate
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/',
IO::String->new('user=rtyler&password=rtyler'),
IO::String->new('user=msmith&password=msmith'),
length => 27,
accept => 'text/html',
),
@ -38,7 +39,8 @@ count(1);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
ok( $res = $client->_get(
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
@ -47,17 +49,16 @@ ok( $res = $client->_get(
);
count(1);
ok( $res->[2]->[0] =~ m%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo' )
or explain( $res->[2]->[0], 'custom Main logo not found"' );
ok( $res->[2]->[0] =~ m%<span trspan="accessDenied">%,
'Found trspan="accessDenied"' )
or explain( $res->[2]->[0], 'trspan="accessDenied"' );
count(2);
ok( $res->[2]->[0] =~ m%An error occurs, you're going to be redirected to%,
'Found redirection page' )
or explain( $res->[2]->[0],
"An error occurs, you're going to be redirected to" );
count(1);
$client->logout($id);
## Try to authenticate
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
@ -72,7 +73,8 @@ expectRedirection( $res, 'http://auth.example.com/' );
# CheckUser form -> granted
# ------------------------
ok( $res = $client->_get(
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
@ -80,17 +82,18 @@ ok( $res = $client->_get(
'CheckUser form',
);
count(1);
my ( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
# Request with bad VH
my ( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~ s/user=dwho/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
ok( $res = $client->_post(
$query =~ s/url=/url=http%3A%2F%2Ftry.example.com/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -101,50 +104,75 @@ ok( $res = $client->_post(
);
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="VHnotFound">%,
'Found trspan="VHnotFound"' )
or explain( $res->[2]->[0], 'trspan="VHnotFound"' );
count(1);
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok( $res->[2]->[0]
=~ m%<div class="alert alert-success"><span trspan="allowed"></span></div>%,
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~
s/url=http%3A%2F%2Ftry.example.com/url=http%3A%2F%2Ftest1.example.com/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok(
$res->[2]->[0] =~
m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
'Found trspan="allowed"'
) or explain( $res->[2]->[0], 'trspan="allowed"' );
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] =~ m%<span trspan="groups_sso">%,
'Found trspan="groups_sso"' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
'Found trspan="attributes"' )
or explain( $res->[2]->[0], 'trspan="attributes"' );
or explain( $res->[2]->[0], 'trspan="attributes"' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">Auth-User</td>%,
'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%,
'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%, 'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">su</td>%, 'Found su' )
or explain( $res->[2]->[0], 'SSO Groups: su' );
or explain( $res->[2]->[0], 'SSO Groups: su' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Macro Value uid' );
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="text-left">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Attribute Value uid' );
count(11);
$query =~ s/user=dwho/user=msmith/;
$query
=~ s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
ok( $res = $client->_post(
$query =~
s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -153,8 +181,9 @@ ok( $res = $client->_post(
),
'POST checkuser'
);
ok( $res->[2]->[0]
=~ m%<div class="alert alert-danger"><span trspan="forbidden"></span></div>%,
ok(
$res->[2]->[0] =~
m%<div class="alert alert-danger"><b><span trspan="forbidden"></span></b></div>%,
'Found trspan="forbidden"'
) or explain( $res->[2]->[0], 'trspan="forbidden"' );
count(2);
@ -162,4 +191,4 @@ count(2);
$client->logout($id);
clean_sessions();
done_testing( count() );
done_testing( count() );

View File

@ -0,0 +1,121 @@
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,
checkUser => 1,
impersonationRule => 1,
checkUserDisplayPersistentInfo => 0,
checkUserDisplayEmptyValues => 0,
impersonationMergeSSOgroups => 1,
}
}
);
## Try to authenticate
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'
);
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%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~ s/url=/url=test1.example.com/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok(
$res->[2]->[0] =~
m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
'Found trspan="allowed"'
) or explain( $res->[2]->[0], 'trspan="allowed"' );
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] =~ m%<span trspan="groups_sso">%,
'Found trspan="groups_sso"' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
'Found trspan="attributes"' )
or explain( $res->[2]->[0], 'trspan="attributes"' );
ok( $res->[2]->[0] =~ m%<td class="text-left">_userDB</td>%, 'Found _userDB' )
or explain( $res->[2]->[0], '_userDB' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">Auth-User</td>%,
'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">dwho</td>%, 'Found dwho' )
or explain( $res->[2]->[0], 'Header Value: dwho' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">su</td>%, 'Found su' )
or explain( $res->[2]->[0], 'SSO Groups: su' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="text-left">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Attribute Value uid' );
count(12);
$client->logout($id);
clean_sessions();
done_testing( count() );

View File

@ -0,0 +1,154 @@
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,
checkUser => 1,
impersonationRule => 1,
checkUserDisplayPersistentInfo => 0,
checkUserDisplayEmptyValues => 0,
impersonationMergeSSOgroups => 0,
}
}
);
## Try to authenticate with bad spoofed user
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'
);
ok( $res->[2]->[0] =~ m%<span trmsg="40"></span>%, ' PE40 found' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
my $id = expectCookie($res);
$client->logout($id);
## Try to authenticate
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'
);
count(1);
$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%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~ s/url=/url=test1.example.com/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok(
$res->[2]->[0] =~
m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
'Found trspan="allowed"'
) or explain( $res->[2]->[0], 'trspan="allowed"' );
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] !~ m%<span trspan="groups_sso">%,
'trspan="groups_sso" NOT found' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
'Found trspan="attributes"' )
or explain( $res->[2]->[0], 'trspan="attributes"' );
ok( $res->[2]->[0] =~ m%<td class="text-left">_userDB</td>%, 'Found _userDB' )
or explain( $res->[2]->[0], '_userDB' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">Auth-User</td>%,
'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">dwho</td>%, 'Found dwho' )
or explain( $res->[2]->[0], 'Header Value: dwho' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="text-left">real_groups</td>%,
'Found real_groups' )
or explain( $res->[2]->[0], 'real_groups' );
ok( $res->[2]->[0] =~ m%<td class="text-left">su</td>%, 'Found su' )
or explain( $res->[2]->[0], 'su' );
ok( $res->[2]->[0] =~ m%<td class="text-left">real_uid</td>%, 'Found real_uid' )
or explain( $res->[2]->[0], 'real_groups' );
ok( $res->[2]->[0] =~ m%<td class="text-left">rtyler</td>%, 'Found rtyler' )
or explain( $res->[2]->[0], 'su' );
count(14);
$client->logout($id);
clean_sessions();
done_testing( count() );

View File

@ -10,12 +10,12 @@ count(1);
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
ext2fActivation => 1,
ext2fCodeActivation => 'A1b2C0',
ext2FSendCommand => 't/sendCode.pl -uid $uid -code $code',
authentication => 'Demo',
userDB => 'Same',
logLevel => 'error',
ext2fActivation => 1,
ext2fCodeActivation => 'A1b2C0',
ext2FSendCommand => 't/sendCode.pl -uid $uid -code $code',
authentication => 'Demo',
userDB => 'Same',
}
}
);

View File

@ -35,8 +35,9 @@
"key": "qwertyui",
"locationRules": {
"auth.example.com" : {
"(?#checkUser)/checkuser" : "$uid eq \"dwho\"",
"default" : "deny"
"(?#checkUser)^/checkuser" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
"manager.example.com": {
"(?#Configuration)^/(manager\\.html|conf/)": "$uid eq \"dwho\"",

View File

@ -4,4 +4,7 @@ use warnings;
my ( $swt1, $user, $swt2, $code ) = @ARGV;
exit !( $swt1 eq '-uid' && $user eq 'dwho' && $swt2 eq '-code' && defined $code );
exit !($swt1 eq '-uid'
&& $user eq 'dwho'
&& $swt2 eq '-code'
&& defined $code );