Merge branch 'v2.0'

This commit is contained in:
Clément OUDOT 2019-02-28 08:52:48 +01:00
commit 59d163c663
65 changed files with 1128 additions and 410 deletions

View File

@ -1,3 +1,10 @@
## map directive must be in http context
# Uncomment this if you use Auth SSL:
#map $ssl_client_s_dn $ssl_client_s_dn_cn {
# default "";
# ~/CN=(?<CN>[^/]+) $CN;
#}
server { server {
listen __PORT__; listen __PORT__;
server_name auth.__DNSDOMAIN__; server_name auth.__DNSDOMAIN__;
@ -29,11 +36,7 @@ server {
fastcgi_split_path_info ^(.*\.psgi)(/.*)$; fastcgi_split_path_info ^(.*\.psgi)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_INFO $fastcgi_path_info;
# Uncomment this if you use Auth SSL: # Uncomment this if you use Auth SSL:
#map $ssl_client_s_dn $ssl_client_s_dn_cn { #fastcgi_param SSL_CLIENT_S_DN_CN $ssl_client_s_dn_cn;
# default "";
# ~/CN=(?<CN>[^/]+) $CN;
#}
#fastcgi_param SSL_CLIENT_S_DN_CN $ssl_client_s_dn_cn
# OR TO USE uWSGI # OR TO USE uWSGI
#include /etc/nginx/uwsgi_params; #include /etc/nginx/uwsgi_params;
@ -41,6 +44,8 @@ server {
#uwsgi_param LLTYPE psgi; #uwsgi_param LLTYPE psgi;
#uwsgi_param SCRIPT_FILENAME $document_root$sc; #uwsgi_param SCRIPT_FILENAME $document_root$sc;
#uwsgi_param SCRIPT_NAME $sc; #uwsgi_param SCRIPT_NAME $sc;
# Uncomment this if you use Auth SSL:
#uwsgi_param SSL_CLIENT_S_DN_CN $ssl_client_s_dn_cn;
} }
@ -49,7 +54,7 @@ server {
try_files $uri $uri/ =404; try_files $uri $uri/ =404;
# Uncomment this if you use https only # Uncomment this if you use https only
#add_header Strict-Transport-Security "15768000"; #add_header Strict-Transport-Security max-age=15768000;
} }
location /static/ { location /static/ {

View File

@ -28,6 +28,7 @@ sub defaultValues {
'casAccessControlPolicy' => 'none', 'casAccessControlPolicy' => 'none',
'casAuthnLevel' => 1, 'casAuthnLevel' => 1,
'checkTime' => 600, 'checkTime' => 600,
'checkUserHiddenAttributes' => 'UA _2fDevices _loginHistory',
'checkXSS' => 1, 'checkXSS' => 1,
'confirmFormMethod' => 'post', 'confirmFormMethod' => 'post',
'cookieName' => 'lemonldap', 'cookieName' => 'lemonldap',
@ -50,6 +51,7 @@ sub defaultValues {
'UA' => 'HTTP_USER_AGENT' 'UA' => 'HTTP_USER_AGENT'
}, },
'ext2fActivation' => 0, 'ext2fActivation' => 0,
'ext2fCodeActivation' => '\\d{6}',
'facebookAuthnLevel' => 1, 'facebookAuthnLevel' => 1,
'facebookExportedVars' => {}, 'facebookExportedVars' => {},
'facebookUserField' => 'id', 'facebookUserField' => 'id',

View File

@ -23,7 +23,7 @@ my $dataStart = tell(DATA);
# SAML 2 description. # SAML 2 description.
# @return string # @return string
sub serviceToXML { sub serviceToXML {
my ( $self, $conf ) = @_; my ( $self, $conf, $type ) = @_;
seek DATA, $dataStart, 0; seek DATA, $dataStart, 0;
my $s = join '', <DATA>; my $s = join '', <DATA>;
@ -41,6 +41,14 @@ sub serviceToXML {
samlOrganizationURL samlOrganizationURL
); );
if ($type and $type eq 'idp') {
$template->param( 'hideSPMetadata', 1);
}
if ($type and $type eq 'sp') {
$template->param( 'hideIDPMetadata', 1);
}
foreach (@param_auto) { foreach (@param_auto) {
$template->param( $_, $self->getValue( $_, $conf ) ); $template->param( $_, $self->getValue( $_, $conf ) );
} }
@ -195,6 +203,7 @@ __DATA__
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="<TMPL_VAR NAME="samlEntityID">"> entityID="<TMPL_VAR NAME="samlEntityID">">
<TMPL_UNLESS NAME="hideIDPMetadata">
<IDPSSODescriptor <IDPSSODescriptor
WantAuthnRequestsSigned="<TMPL_VAR NAME="samlIDPSSODescriptorWantAuthnRequestsSigned">" WantAuthnRequestsSigned="<TMPL_VAR NAME="samlIDPSSODescriptorWantAuthnRequestsSigned">"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
@ -253,7 +262,9 @@ __DATA__
ResponseLocation="<TMPL_VAR NAME="samlIDPSSODescriptorSingleSignOnServiceHTTPArtifactResponseLocation">" ResponseLocation="<TMPL_VAR NAME="samlIDPSSODescriptorSingleSignOnServiceHTTPArtifactResponseLocation">"
</TMPL_IF>/> </TMPL_IF>/>
</IDPSSODescriptor> </IDPSSODescriptor>
</TMPL_UNLESS>
<TMPL_UNLESS NAME="hideSPMetadata">
<SPSSODescriptor <SPSSODescriptor
AuthnRequestsSigned="<TMPL_VAR NAME="samlSPSSODescriptorAuthnRequestsSigned">" AuthnRequestsSigned="<TMPL_VAR NAME="samlSPSSODescriptorAuthnRequestsSigned">"
WantAssertionsSigned="<TMPL_VAR NAME="samlSPSSODescriptorWantAssertionsSigned">" WantAssertionsSigned="<TMPL_VAR NAME="samlSPSSODescriptorWantAssertionsSigned">"
@ -305,7 +316,9 @@ __DATA__
Binding="<TMPL_VAR NAME="samlSPSSODescriptorAssertionConsumerServiceHTTPPostBinding">" Binding="<TMPL_VAR NAME="samlSPSSODescriptorAssertionConsumerServiceHTTPPostBinding">"
Location="<TMPL_VAR NAME="samlSPSSODescriptorAssertionConsumerServiceHTTPPostLocation">" /> Location="<TMPL_VAR NAME="samlSPSSODescriptorAssertionConsumerServiceHTTPPostLocation">" />
</SPSSODescriptor> </SPSSODescriptor>
</TMPL_UNLESS>
<TMPL_UNLESS NAME="hideIDPMetadata">
<AttributeAuthorityDescriptor <AttributeAuthorityDescriptor
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing"> <KeyDescriptor use="signing">
@ -328,6 +341,7 @@ __DATA__
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:entity</NameIDFormat> <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:entity</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat> <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
</AttributeAuthorityDescriptor> </AttributeAuthorityDescriptor>
</TMPL_UNLESS>
<Organization> <Organization>
<OrganizationName xml:lang="en"><TMPL_VAR NAME="samlOrganizationName"></OrganizationName> <OrganizationName xml:lang="en"><TMPL_VAR NAME="samlOrganizationName"></OrganizationName>

View File

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

View File

@ -60,7 +60,7 @@ sub _run {
$self->routes( $self->authRoutes ); $self->routes( $self->authRoutes );
$req->userData( $self->api->data ); $req->userData( $self->api->data );
} }
else { elsif ( $res->[0] != 403 ) {
# Unset headers (handler adds a Location header) # Unset headers (handler adds a Location header)
$self->logger->debug( $self->logger->debug(
"User not authenticated, Try in use, cancel redirection"); "User not authenticated, Try in use, cancel redirection");
@ -68,6 +68,9 @@ sub _run {
$req->respHeaders( [] ); $req->respHeaders( [] );
$self->routes( $self->unAuthRoutes ); $self->routes( $self->unAuthRoutes );
} }
else {
return $res;
}
$res = $self->handler($req); $res = $self->handler($req);
# Insert respHeaders in response only if not already set # Insert respHeaders in response only if not already set

View File

@ -8,17 +8,17 @@ sub types {
'array' => { 'array' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'authParamsText' => { 'authParamsText' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'blackWhiteList' => { 'blackWhiteList' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'bool' => { 'bool' => {
'msgFail' => '__notABoolean__', 'msgFail' => '__notABoolean__',
@ -36,17 +36,17 @@ sub types {
split( /\n/, $@, 0 ) ) split( /\n/, $@, 0 ) )
); );
return $err ? ( 1, "__badExpression__: $err" ) : 1; return $err ? ( 1, "__badExpression__: $err" ) : 1;
} }
}, },
'catAndAppList' => { 'catAndAppList' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'file' => { 'file' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'hostname' => { 'hostname' => {
'form' => 'text', 'form' => 'text',
@ -80,48 +80,48 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
if $_ =~ /exportedvars$/i and defined $conf->{$_}{$val}; if $_ =~ /exportedvars$/i and defined $conf->{$_}{$val};
} }
return 1, "__unknownAttrOrMacro__: $val"; return 1, "__unknownAttrOrMacro__: $val";
} }
}, },
'longtext' => { 'longtext' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'menuApp' => { 'menuApp' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'menuCat' => { 'menuCat' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'oidcmetadatajson' => { 'oidcmetadatajson' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'oidcmetadatajwks' => { 'oidcmetadatajwks' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'oidcOPMetaDataNode' => { 'oidcOPMetaDataNode' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'oidcRPMetaDataNode' => { 'oidcRPMetaDataNode' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'password' => { 'password' => {
'msgFail' => '__malformedValue__', 'msgFail' => '__malformedValue__',
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'pcre' => { 'pcre' => {
'form' => 'text', 'form' => 'text',
@ -132,7 +132,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
} }
}; };
return $@ ? ( 0, "__badRegexp__: $@" ) : 1; return $@ ? ( 0, "__badRegexp__: $@" ) : 1;
} }
}, },
'PerlModule' => { 'PerlModule' => {
'form' => 'text', 'form' => 'text',
@ -142,17 +142,17 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'portalskin' => { 'portalskin' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'portalskinbackground' => { 'portalskinbackground' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'post' => { 'post' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'RSAPrivateKey' => { 'RSAPrivateKey' => {
'test' => sub { 'test' => sub {
@ -160,7 +160,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\r\n]*)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$]s m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\r\n]*)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$]s
? 1 ? 1
: ( 1, '__badPemEncoding__' ); : ( 1, '__badPemEncoding__' );
} }
}, },
'RSAPublicKey' => { 'RSAPublicKey' => {
'test' => sub { 'test' => sub {
@ -168,7 +168,7 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\n
m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+PUBLIC\s+KEY\s*\-+)?[\r\n]*)?$]s m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+PUBLIC\s+KEY\s*\-+)?[\r\n]*)?$]s
? 1 ? 1
: ( 1, '__badPemEncoding__' ); : ( 1, '__badPemEncoding__' );
} }
}, },
'RSAPublicKeyOrCertificate' => { 'RSAPublicKeyOrCertificate' => {
'test' => sub { 'test' => sub {
@ -176,37 +176,37 @@ m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\
m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+)?[\r\n]*)?$]s m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+)?[\r\n]*)?$]s
? 1 ? 1
: ( 1, '__badPemEncoding__' ); : ( 1, '__badPemEncoding__' );
} }
}, },
'rule' => { 'rule' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'samlAssertion' => { 'samlAssertion' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'samlAttribute' => { 'samlAttribute' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'samlIDPMetaDataNode' => { 'samlIDPMetaDataNode' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'samlService' => { 'samlService' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'samlSPMetaDataNode' => { 'samlSPMetaDataNode' => {
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'select' => { 'select' => {
'test' => sub { 'test' => sub {
@ -216,19 +216,19 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\
return $test return $test
? 1 ? 1
: ( 1, "Invalid value '$_[0]' for this select" ); : ( 1, "Invalid value '$_[0]' for this select" );
} }
}, },
'subContainer' => { 'subContainer' => {
'keyTest' => qr/\w/, 'keyTest' => qr/\w/,
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'text' => { 'text' => {
'msgFail' => '__malformedValue__', 'msgFail' => '__malformedValue__',
'test' => sub { 'test' => sub {
1; 1;
} }
}, },
'trool' => { 'trool' => {
'msgFail' => '__authorizedValues__: -1, 0, 1', 'msgFail' => '__authorizedValues__: -1, 0, 1',
@ -767,6 +767,22 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 600, 'default' => 600,
'type' => 'int' 'type' => 'int'
}, },
'checkUser' => {
'default' => 0,
'type' => 'bool'
},
'checkUserDisplayEmptyValues' => {
'default' => 0,
'type' => 'bool'
},
'checkUserDisplayPersistentInfo' => {
'default' => 0,
'type' => 'bool'
},
'checkUserHiddenAttributes' => {
'default' => 'UA _2fDevices _loginHistory',
'type' => 'text'
},
'checkXSS' => { 'checkXSS' => {
'default' => 1, 'default' => 1,
'type' => 'bool' 'type' => 'bool'
@ -1046,7 +1062,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
split( /\n/, $@, 0 ) ) split( /\n/, $@, 0 ) )
); );
return $err ? ( 1, "__badExpression__: $err" ) : 1; return $err ? ( 1, "__badExpression__: $err" ) : 1;
} }
}, },
'type' => 'keyTextContainer' 'type' => 'keyTextContainer'
}, },
@ -1067,6 +1083,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'ext2fAuthnLevel' => { 'ext2fAuthnLevel' => {
'type' => 'int' 'type' => 'int'
}, },
'ext2fCodeActivation' => {
'default' => '\\d{6}',
'type' => 'pcre'
},
'ext2fLogo' => { 'ext2fLogo' => {
'type' => 'text' 'type' => 'text'
}, },
@ -1222,7 +1242,7 @@ qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-
and defined $conf->{$_}{$val}; and defined $conf->{$_}{$val};
} }
return 1, "__unknownAttrOrMacro__: $val"; return 1, "__unknownAttrOrMacro__: $val";
} }
}, },
'type' => 'doubleHash' 'type' => 'doubleHash'
}, },
@ -1508,7 +1528,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
split( /\n/, $@, 0 ) ) split( /\n/, $@, 0 ) )
); );
return $err ? ( 1, "__badExpression__: $err" ) : 1; return $err ? ( 1, "__badExpression__: $err" ) : 1;
} }
}, },
'type' => 'ruleContainer' 'type' => 'ruleContainer'
}, },

View File

@ -578,6 +578,30 @@ sub attributes {
documentation => 'Enable Cross Domain Authentication', documentation => 'Enable Cross Domain Authentication',
flags => 'hp', flags => 'hp',
}, },
checkUser => {
default => 0,
type => 'bool',
documentation => 'Enable check user',
flags => 'p',
},
checkUserHiddenAttributes => {
type => 'text',
default => 'UA _2fDevices _loginHistory',
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 => { checkXSS => {
default => 1, default => 1,
type => 'bool', type => 'bool',
@ -1338,6 +1362,11 @@ sub attributes {
default => 0, default => 0,
documentation => 'External second factor activation', documentation => 'External second factor activation',
}, },
ext2fCodeActivation => {
type => 'pcre',
default => '\d{6}',
documentation => 'OTP generated by Portal',
},
ext2FSendCommand => { ext2FSendCommand => {
type => 'text', type => 'text',
documentation => 'Send command of External second factor', documentation => 'Send command of External second factor',

View File

@ -637,6 +637,17 @@ sub tree {
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ 'checkState', 'checkStateSecret', ], nodes => [ 'checkState', 'checkStateSecret', ],
}, },
{
title => 'checkUsers',
help => 'checkuser.html',
form => 'simpleInputContainer',
nodes => [
'checkUser',
'checkUserHiddenAttributes',
'checkUserDisplayPersistentInfo',
'checkUserDisplayEmptyValues',
]
},
] ]
}, },
{ {
@ -691,9 +702,9 @@ sub tree {
help => 'external2f.html', help => 'external2f.html',
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes => [
'ext2fActivation', 'ext2FSendCommand', 'ext2fActivation', 'ext2fCodeActivation',
'ext2FValidateCommand', 'ext2fAuthnLevel', 'ext2FSendCommand', 'ext2FValidateCommand',
'ext2fLogo', 'ext2fAuthnLevel', 'ext2fLogo',
] ]
}, },
{ {

View File

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

View File

@ -151,6 +151,11 @@
"clickHereToForce":"انقر هنا لإجبار", "clickHereToForce":"انقر هنا لإجبار",
"checkState":"Activation", "checkState":"Activation",
"checkStateSecret":"Shared secret", "checkStateSecret":"Shared secret",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"اختيارالإعدادات", "choiceParams":"اختيارالإعدادات",
"chooseLogo":"اختيار الشعار", "chooseLogo":"اختيار الشعار",
"chooseSkin":"اختيار الغلاف", "chooseSkin":"اختيار الغلاف",
@ -243,6 +248,7 @@
"exportedVars":"المتغيرات المصدرة", "exportedVars":"المتغيرات المصدرة",
"external2f":"External second factor", "external2f":"External second factor",
"ext2fActivation":"تفعيل", "ext2fActivation":"تفعيل",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"مستوى إثبات الهوية", "ext2fAuthnLevel":"مستوى إثبات الهوية",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"إرسال الأمر", "ext2FSendCommand":"إرسال الأمر",

View File

@ -152,6 +152,11 @@
"checkState":"Activation", "checkState":"Activation",
"checkStateSecret":"Shared secret", "checkStateSecret":"Shared secret",
"choiceParams":"Choice parameters", "choiceParams":"Choice parameters",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"chooseLogo":"Choose logo", "chooseLogo":"Choose logo",
"chooseSkin":"Choose skin", "chooseSkin":"Choose skin",
"combination":"Combination", "combination":"Combination",
@ -243,6 +248,7 @@
"exportedVars":"Exported Variables", "exportedVars":"Exported Variables",
"external2f":"External second factor", "external2f":"External second factor",
"ext2fActivation":"Activation", "ext2fActivation":"Activation",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"Authentication level", "ext2fAuthnLevel":"Authentication level",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Send comand", "ext2FSendCommand":"Send comand",

View File

@ -151,6 +151,11 @@
"clickHereToForce":"Click here to force", "clickHereToForce":"Click here to force",
"checkState":"Activation", "checkState":"Activation",
"checkStateSecret":"Shared secret", "checkStateSecret":"Shared secret",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"Choice parameters", "choiceParams":"Choice parameters",
"chooseLogo":"Choose logo", "chooseLogo":"Choose logo",
"chooseSkin":"Choose skin", "chooseSkin":"Choose skin",
@ -243,6 +248,7 @@
"exportedVars":"Exported Variables", "exportedVars":"Exported Variables",
"external2f":"External second factor", "external2f":"External second factor",
"ext2fActivation":"Activation", "ext2fActivation":"Activation",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"Authentication level", "ext2fAuthnLevel":"Authentication level",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Send comand", "ext2FSendCommand":"Send comand",

View File

@ -152,6 +152,11 @@
"checkState":"Activation", "checkState":"Activation",
"checkStateSecret":"Secret partagé", "checkStateSecret":"Secret partagé",
"choiceParams":"Paramètres des choix", "choiceParams":"Paramètres des choix",
"checkUsers":"Vérification de session",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Attributs masqués",
"checkUserDisplayPersistentInfo":"Afficher les données de session persistante",
"checkUserDisplayEmptyValues":"Afficher les valeurs nulles",
"chooseLogo":"Choisir le logo", "chooseLogo":"Choisir le logo",
"chooseSkin":"Choisir le thème", "chooseSkin":"Choisir le thème",
"combination":"Combinaison", "combination":"Combinaison",
@ -243,6 +248,7 @@
"exportedVars":"Attributs à exporter", "exportedVars":"Attributs à exporter",
"external2f":"Second facteur externe", "external2f":"Second facteur externe",
"ext2fActivation":"Activation", "ext2fActivation":"Activation",
"ext2fCodeActivation":"Expression régulière pour la génération du code",
"ext2fAuthnLevel":"Niveau de l'authentification", "ext2fAuthnLevel":"Niveau de l'authentification",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Commande pour l'envoi", "ext2FSendCommand":"Commande pour l'envoi",

View File

@ -151,6 +151,11 @@
"clickHereToForce":"Clicca qui per forzare", "clickHereToForce":"Clicca qui per forzare",
"checkState":"Attivazione", "checkState":"Attivazione",
"checkStateSecret":"Segreto condiviso", "checkStateSecret":"Segreto condiviso",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"Scelta parametri", "choiceParams":"Scelta parametri",
"chooseLogo":"Scegli logo", "chooseLogo":"Scegli logo",
"chooseSkin":"Scegli interfaccia", "chooseSkin":"Scegli interfaccia",
@ -243,6 +248,7 @@
"exportedVars":"Variabili esportate", "exportedVars":"Variabili esportate",
"external2f":"2° fattore esterno", "external2f":"2° fattore esterno",
"ext2fActivation":"Attivazione", "ext2fActivation":"Attivazione",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"Livello di autenticazione", "ext2fAuthnLevel":"Livello di autenticazione",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Invia comando", "ext2FSendCommand":"Invia comando",

View File

@ -151,6 +151,11 @@
"clickHereToForce":"Nhấp vào đây để bắt buộc", "clickHereToForce":"Nhấp vào đây để bắt buộc",
"checkState":"Kích hoạt", "checkState":"Kích hoạt",
"checkStateSecret":"Shared secret", "checkStateSecret":"Shared secret",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"Các tham số lựa chọn", "choiceParams":"Các tham số lựa chọn",
"chooseLogo":"Chọn logo", "chooseLogo":"Chọn logo",
"chooseSkin":"Chọn giao diện", "chooseSkin":"Chọn giao diện",
@ -243,6 +248,7 @@
"exportedVars":"Biến đã được xuất", "exportedVars":"Biến đã được xuất",
"external2f":"Yếu tố thứ 2 bên ngoài", "external2f":"Yếu tố thứ 2 bên ngoài",
"ext2fActivation":"Kích hoạt", "ext2fActivation":"Kích hoạt",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"Mức xác thực", "ext2fAuthnLevel":"Mức xác thực",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Gửi lệnh", "ext2FSendCommand":"Gửi lệnh",

View File

@ -151,6 +151,11 @@
"clickHereToForce":"Click here to force", "clickHereToForce":"Click here to force",
"checkState":"Activation", "checkState":"Activation",
"checkStateSecret":"Shared secret", "checkStateSecret":"Shared secret",
"checkUsers":"Session check",
"checkUser":"Activation",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"Choice parameters", "choiceParams":"Choice parameters",
"chooseLogo":"Choose logo", "chooseLogo":"Choose logo",
"chooseSkin":"Choose skin", "chooseSkin":"Choose skin",
@ -243,6 +248,7 @@
"exportedVars":"Exported Variables", "exportedVars":"Exported Variables",
"external2f":"External second factor", "external2f":"External second factor",
"ext2fActivation":"激活", "ext2fActivation":"激活",
"ext2fCodeActivation":"Code regex",
"ext2fAuthnLevel":"认证级别", "ext2fAuthnLevel":"认证级别",
"ext2fLogo":"Logo", "ext2fLogo":"Logo",
"ext2FSendCommand":"Send comand", "ext2FSendCommand":"Send comand",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -329,6 +329,7 @@ site/templates/bootstrap/customhead.tpl
site/templates/bootstrap/customheader.tpl site/templates/bootstrap/customheader.tpl
site/templates/bootstrap/customLoginFooter.tpl site/templates/bootstrap/customLoginFooter.tpl
site/templates/bootstrap/customLoginHeader.tpl site/templates/bootstrap/customLoginHeader.tpl
site/templates/bootstrap/error.json.example
site/templates/bootstrap/error.tpl site/templates/bootstrap/error.tpl
site/templates/bootstrap/ext2fcheck.tpl site/templates/bootstrap/ext2fcheck.tpl
site/templates/bootstrap/footer.tpl site/templates/bootstrap/footer.tpl
@ -337,6 +338,7 @@ site/templates/bootstrap/header.tpl
site/templates/bootstrap/idpchoice.tpl site/templates/bootstrap/idpchoice.tpl
site/templates/bootstrap/info.tpl site/templates/bootstrap/info.tpl
site/templates/bootstrap/ldapPpGrace.tpl site/templates/bootstrap/ldapPpGrace.tpl
site/templates/bootstrap/login.json
site/templates/bootstrap/login.tpl site/templates/bootstrap/login.tpl
site/templates/bootstrap/mail.tpl site/templates/bootstrap/mail.tpl
site/templates/bootstrap/menu.tpl site/templates/bootstrap/menu.tpl
@ -394,6 +396,7 @@ site/templates/common/oidc_checksession.tpl
site/templates/common/redirect.tpl site/templates/common/redirect.tpl
site/templates/common/registerBrowser.tpl site/templates/common/registerBrowser.tpl
site/templates/common/script.tpl site/templates/common/script.tpl
site/templates/common/trover.tpl
site/templates/localeTranslations.txt site/templates/localeTranslations.txt
t/01-AuthDemo.t t/01-AuthDemo.t
t/01-pdata.t t/01-pdata.t
@ -496,21 +499,22 @@ t/66-CDA-already-auth.t
t/66-CDA-with-REST.t t/66-CDA-with-REST.t
t/66-CDA-with-SOAP.t t/66-CDA-with-SOAP.t
t/66-CDA.t t/66-CDA.t
t/70-2F-TOTP-with-HISTORY.t t/70-2F-TOTP-with-History.t
t/70-2F-TOTP.t t/70-2F-TOTP.t
t/70-2F-TOTP_8.t t/70-2F-TOTP_8.t
t/71-2F-U2F-with-HISTORY.t t/71-2F-U2F-with-History.t
t/71-2F-U2F.t t/71-2F-U2F.t
t/72-2F-REST-with-HISTORY.t t/72-2F-REST-with-HISTORY.t
t/73-2F-UTOTP-TOTP-and-U2F-with-HISTORY.t t/73-2F-UTOTP-TOTP-and-U2F-with-History.t
t/73-2F-UTOTP-TOTP-and-U2F.t t/73-2F-UTOTP-TOTP-and-U2F.t
t/73-2F-UTOTP-TOTP-only-with-HISTORY.t t/73-2F-UTOTP-TOTP-only-with-History.t
t/73-2F-UTOTP-TOTP-only.t t/73-2F-UTOTP-TOTP-only.t
t/74-2F-Required.t t/74-2F-Required.t
t/75-2F-Registers.t t/75-2F-Registers.t
t/76-2F-Ext-with-BruteForce.t t/76-2F-Ext-with-BruteForce.t
t/76-2F-Ext-with-CodeActivation.t
t/76-2F-Ext-with-GrantSession.t t/76-2F-Ext-with-GrantSession.t
t/76-2F-Ext-with-HISTORY.t t/76-2F-Ext-with-History.t
t/77-2F-Mail.t t/77-2F-Mail.t
t/90-Translations.t t/90-Translations.t
t/99-pod.t t/99-pod.t
@ -525,6 +529,7 @@ t/lmConf-1.json
t/pdata.pm t/pdata.pm
t/README.md t/README.md
t/saml-lib.pm t/saml-lib.pm
t/sendCode.pl
t/sendOTP.pl t/sendOTP.pl
t/sessions/lock/.exists t/sessions/lock/.exists
t/sessions/saml/lock/.exists t/sessions/saml/lock/.exists

View File

@ -2,12 +2,13 @@ package Lemonldap::NG::Portal::2F::Ext2F;
use strict; use strict;
use Mouse; use Mouse;
use String::Random;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADCREDENTIALS
PE_ERROR PE_ERROR
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
PE_SENDRESPONSE PE_SENDRESPONSE
); );
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -17,17 +18,32 @@ extends 'Lemonldap::NG::Portal::Main::SecondFactor';
# INITIALIZATION # INITIALIZATION
has prefix => ( is => 'ro', default => 'ext' ); has prefix => ( is => 'ro', default => 'ext' );
has random => ( is => 'rw' );
sub init { sub init {
my ($self) = @_; my ($self) = @_;
foreach (qw(ext2FSendCommand ext2FValidateCommand)) { unless ( $self->conf->{ext2fCodeActivation} ) {
unless ( $self->conf->{$_} ) { foreach (qw(ext2FSendCommand ext2FValidateCommand)) {
$self->error("Missing $_ parameter, aborting"); unless ( $self->conf->{$_} ) {
$self->error("Missing $_ parameter, aborting");
return 0;
}
}
$self->logo( $self->conf->{ext2fLogo} )
if ( $self->conf->{ext2fLogo} );
return $self->SUPER::init();
}
if ( $self->conf->{ext2fCodeActivation} ) {
unless ( $self->conf->{ext2FSendCommand} ) {
$self->error("Missing 'ext2FSendCommand' parameter, aborting");
return 0; return 0;
} }
$self->random( String::Random->new );
$self->logo( $self->conf->{ext2fLogo} )
if ( $self->conf->{ext2fLogo} );
return $self->SUPER::init();
} }
$self->logo( $self->conf->{ext2fLogo} ) if ( $self->conf->{ext2fLogo} ); return 0;
return $self->SUPER::init();
} }
# RUNNING METHODS # RUNNING METHODS
@ -38,14 +54,25 @@ sub run {
my $checkLogins = $req->param('checkLogins'); my $checkLogins = $req->param('checkLogins');
$self->logger->debug("Ext2F checkLogins set") if ($checkLogins); $self->logger->debug("Ext2F checkLogins set") if ($checkLogins);
# Generate Code to send
my $code;
if ( $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 # Prepare command and launch it
$self->logger->debug( 'Launching "Send" external 2F command -> ' $self->logger->debug( 'Launching "Send" external 2F command -> '
. $self->conf->{ext2FSendCommand} ); . $self->conf->{ext2FSendCommand} );
if ( my $c = if (my $c = $self->launch(
$self->launch( $req->sessionInfo, $self->conf->{ext2FSendCommand} ) ) $req->sessionInfo, $self->conf->{ext2FSendCommand}, $code
)
)
{ {
$self->logger->error("External send command failed (code $c)"); $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 # Prepare form
@ -67,24 +94,44 @@ sub run {
sub verify { sub verify {
my ( $self, $req, $session ) = @_; my ( $self, $req, $session ) = @_;
my $code; my $usercode;
unless ( $code = $req->param('code') ) { unless ( $usercode = $req->param('code') ) {
$self->userLogger->error('External 2F: no code'); $self->userLogger->error('External 2F: no code found');
return PE_FORMEMPTY; return PE_FORMEMPTY;
} }
# Prepare command and launch it unless ( $self->conf->{ext2fCodeActivation} ) {
$self->logger->debug( 'Launching "Validate" external 2F command -> '
. $self->conf->{ext2FValidateCommand} ); # Prepare command and launch it
$self->logger->debug(" code -> $code"); $self->logger->debug( 'Launching "Validate" external 2F command -> '
if ( my $c = . $self->conf->{ext2FValidateCommand} );
$self->launch( $session, $self->conf->{ext2FValidateCommand}, $code ) ) $self->logger->debug(" code -> $usercode");
{ if (my $c = $self->launch(
$self->userLogger->warn( 'Second factor failed for ' $session, $self->conf->{ext2FValidateCommand}, $usercode
. $session->{ $self->conf->{whatToTrace} } ); )
return PE_BADCREDENTIALS; )
{
$self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } );
$self->logger->error("External verify command failed (code $c)");
return PE_BADCREDENTIALS;
}
return PE_OK;
} }
PE_OK;
my $savedcode = $session->{__ext2fcode};
unless ($savedcode) {
$self->logger->error(
'Unable to find generated 2F code in token session');
return PE_ERROR;
}
$self->logger->debug("Verifying Ext 2F code: $usercode VS $savedcode");
return PE_OK if ( $usercode eq $savedcode );
$self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS;
} }
# system() is used with an array to avoid shell injection # system() is used with an array to avoid shell injection
@ -102,3 +149,4 @@ sub launch {
} }
1; 1;

View File

@ -41,19 +41,16 @@ has ott => (
sub init { sub init {
my ($self) = @_; my ($self) = @_;
foreach (qw(mail2fCodeRegex mailSessionKey)) { $self->{conf}->{mail2fCodeRegex} ||= '\d{6}';
unless ( $self->conf->{$_} ) { unless ( $self->conf->{mailSessionKey} ) {
$self->error("Missing $_ parameter, aborting"); $self->error("Missing 'mailSessionKey' parameter, aborting");
return 0; return 0;
}
} }
$self->logo( $self->conf->{mail2fLogo} ) $self->logo( $self->conf->{mail2fLogo} )
if ( $self->conf->{mail2fLogo} ); if ( $self->conf->{mail2fLogo} );
return $self->SUPER::init(); return $self->SUPER::init();
} }
# RUNNING METHODS
sub run { sub run {
my ( $self, $req, $token ) = @_; my ( $self, $req, $token ) = @_;
@ -80,7 +77,7 @@ sub run {
$subject = 'mail2fSubject'; $subject = 'mail2fSubject';
$tr->( \$subject ); $tr->( \$subject );
} }
my ($body, $html); my ( $body, $html );
if ( $self->conf->{mail2fBody} ) { if ( $self->conf->{mail2fBody} ) {
# We use a specific text message, no html # We use a specific text message, no html

View File

@ -23,7 +23,7 @@ extends 'Lemonldap::NG::Portal::Main::Issuer',
use constant beforeAuth => 'storeEnvAndCheckGateway'; use constant beforeAuth => 'storeEnvAndCheckGateway';
use constant sessionKind => 'ICAS'; use constant sessionKind => 'ICAS';
has rule => ( is => 'rw', default => sub { {} } ); has rule => ( is => 'rw' );
sub init { sub init {
my ($self) = @_; my ($self) = @_;

View File

@ -10,7 +10,7 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Issuer'; extends 'Lemonldap::NG::Portal::Main::Issuer';
has rule => ( is => 'rw', default => sub { {} } ); has rule => ( is => 'rw' );
# INITIALIZATION # INITIALIZATION

View File

@ -50,7 +50,7 @@ has spList => (
has openidPortal => ( is => 'rw' ); has openidPortal => ( is => 'rw' );
has rule => ( is => 'rw', default => sub { {} } ); has rule => ( is => 'rw' );
# INITIALIZATION # INITIALIZATION

View File

@ -29,7 +29,7 @@ sub beforeAuth { 'exportRequestParameters' }
use constant sessionKind => 'OIDCI'; use constant sessionKind => 'OIDCI';
has rule => ( is => 'rw', default => sub { {} } ); has rule => ( is => 'rw' );
has configStorage => ( has configStorage => (
is => 'ro', is => 'ro',
lazy => 1, lazy => 1,

View File

@ -21,7 +21,7 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Issuer', extends 'Lemonldap::NG::Portal::Main::Issuer',
'Lemonldap::NG::Portal::Lib::SAML'; 'Lemonldap::NG::Portal::Lib::SAML';
has rule => ( is => 'rw', default => sub { {} } ); has rule => ( is => 'rw' );
has ssoUrlRe => ( is => 'rw' ); has ssoUrlRe => ( is => 'rw' );
has ssoUrlArtifact => ( is => 'rw' ); has ssoUrlArtifact => ( is => 'rw' );
has ssoGetUrl => ( is => 'rw' ); has ssoGetUrl => ( is => 'rw' );

View File

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

View File

@ -97,14 +97,16 @@ sub init {
); );
# Load override messages from file and lemonldap-ng.ini # Load override messages from file and lemonldap-ng.ini
if ( $self->{localConfig}->{translations} ) { if ( $self->{localConfig}->{translations}
and -r $self->{localConfig}->{translations} )
{
open my $tr_file, '<', $self->{localConfig}->{translations} open my $tr_file, '<', $self->{localConfig}->{translations}
or die "Can't open" or die "Can't open"
. $self->{localConfig}->{translations} . " : $!"; . $self->{localConfig}->{translations} . " : $!";
while (<$tr_file>) { while (<$tr_file>) {
chomp; chomp;
$_ =~ /^([\w_]+)\s+=\s+(.+)$/; $_ =~ /^([\w_]+)\s+=\s+(.+)$/;
$self->{localConfig}->{ $1 } = $2; $self->{localConfig}->{$1} = $2;
} }
close $tr_file or die "Can't close $tr_file : $!"; close $tr_file or die "Can't close $tr_file : $!";
} }

View File

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

View File

@ -15,13 +15,14 @@ package Lemonldap::NG::Portal::Main;
use strict; use strict;
use URI::Escape; use URI::Escape;
use JSON;
# List constants # List constants
sub authProcess { qw(extractFormInfo getUser authenticate) } sub authProcess {qw(extractFormInfo getUser authenticate)}
sub sessionData { sub sessionData {
qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo
setLocalGroups store secondFactor); setLocalGroups store secondFactor);
} }
sub validSession { sub validSession {
@ -56,11 +57,9 @@ sub handler {
if ( $sp or %{ $req->pdata } ) { if ( $sp or %{ $req->pdata } ) {
my %v = ( my %v = (
name => $self->conf->{cookieName} . 'pdata', name => $self->conf->{cookieName} . 'pdata',
( ( %{ $req->pdata }
%{ $req->pdata }
? ( value => uri_escape( JSON::to_json( $req->pdata ) ) ) ? ( value => uri_escape( JSON::to_json( $req->pdata ) ) )
: ( : ( value => '',
value => '',
expires => 'Wed, 21 Oct 2015 00:00:00 GMT' expires => 'Wed, 21 Oct 2015 00:00:00 GMT'
) )
) )
@ -94,8 +93,7 @@ sub login {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->do( return $self->do(
$req, $req,
[ [ 'controlUrl', @{ $self->beforeAuth },
'controlUrl', @{ $self->beforeAuth },
$self->authProcess, @{ $self->betweenAuthAndData }, $self->authProcess, @{ $self->betweenAuthAndData },
$self->sessionData, @{ $self->afterData }, $self->sessionData, @{ $self->afterData },
$self->validSession, @{ $self->endAuth }, $self->validSession, @{ $self->endAuth },
@ -107,8 +105,7 @@ sub postLogin {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->do( return $self->do(
$req, $req,
[ [ 'restoreArgs', 'controlUrl',
'restoreArgs', 'controlUrl',
@{ $self->beforeAuth }, $self->authProcess, @{ $self->beforeAuth }, $self->authProcess,
@{ $self->betweenAuthAndData }, $self->sessionData, @{ $self->betweenAuthAndData }, $self->sessionData,
@{ $self->afterData }, $self->validSession, @{ $self->afterData }, $self->validSession,
@ -121,8 +118,7 @@ sub authenticatedRequest {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->do( return $self->do(
$req, $req,
[ [ 'importHandlerData', 'controlUrl',
'importHandlerData', 'controlUrl',
'checkLogout', @{ $self->forAuthUser } 'checkLogout', @{ $self->forAuthUser }
] ]
); );
@ -132,8 +128,7 @@ sub postAuthenticatedRequest {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->do( return $self->do(
$req, $req,
[ [ 'importHandlerData', 'restoreArgs',
'importHandlerData', 'restoreArgs',
'controlUrl', 'checkLogout', 'controlUrl', 'checkLogout',
@{ $self->forAuthUser } @{ $self->forAuthUser }
] ]
@ -150,8 +145,8 @@ sub refresh {
foreach ( keys %data ) { foreach ( keys %data ) {
delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ ); delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ );
} }
$req->steps( [ $req->steps(
'getUser', [ 'getUser',
@{ $self->betweenAuthAndData }, @{ $self->betweenAuthAndData },
'setAuthSessionInfo', 'setAuthSessionInfo',
'setSessionInfo', 'setSessionInfo',
@ -169,21 +164,21 @@ sub refresh {
if ($res) { if ($res) {
$req->info( $req->info(
$self->loadTemplate( $self->loadTemplate(
'simpleInfo', params => { trspan => 'rightsReloadNeedsLogout' } 'simpleInfo',
params => { trspan => 'rightsReloadNeedsLogout' }
) )
); );
$req->urldc( $self->conf->{portal} ); $req->urldc( $self->conf->{portal} );
return $self->do( $req, [ sub { PE_INFO } ] ); return $self->do( $req, [ sub {PE_INFO} ] );
} }
return $self->do( $req, [ sub { PE_OK } ] ); return $self->do( $req, [ sub {PE_OK} ] );
} }
sub logout { sub logout {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->do( return $self->do(
$req, $req,
[ [ 'controlUrl', @{ $self->beforeLogout },
'controlUrl', @{ $self->beforeLogout },
'authLogout', 'deleteSession' 'authLogout', 'deleteSession'
] ]
); );
@ -200,9 +195,9 @@ sub do {
# Update status # Update status
if ( my $p = $self->HANDLER->tsv->{statusPipe} ) { if ( my $p = $self->HANDLER->tsv->{statusPipe} ) {
$p->print( ( $req->user ? $req->user : $req->address ) . ' => ' $p->print(( $req->user ? $req->user : $req->address ) . ' => '
. $req->uri . $req->uri
. " $err\n" ); . " $err\n" );
} }
# Update history # Update history
@ -228,16 +223,14 @@ sub do {
else { else {
return $self->sendJSONresponse( return $self->sendJSONresponse(
$req, $req,
{ { result => 1,
result => 1,
code => $err code => $err
} }
); );
} }
} }
else { else {
if ( if ( $err
$err
and $err != PE_LOGOUT_OK and $err != PE_LOGOUT_OK
and ( and (
$err != PE_REDIRECT $err != PE_REDIRECT
@ -246,7 +239,7 @@ sub do {
and $req->data->{redirectFormMethod} eq 'post' ) and $req->data->{redirectFormMethod} eq 'post' )
or $req->info or $req->info
) )
) )
{ {
my ( $tpl, $prms ) = $self->display($req); my ( $tpl, $prms ) = $self->display($req);
$self->logger->debug("Calling sendHtml with template $tpl"); $self->logger->debug("Calling sendHtml with template $tpl");
@ -264,21 +257,20 @@ sub do {
sub getModule { sub getModule {
my ( $self, $req, $type ) = @_; my ( $self, $req, $type ) = @_;
if ( if (my $mod = {
my $mod = {
auth => '_authentication', auth => '_authentication',
user => '_userDB', user => '_userDB',
password => '_passwordDB' password => '_passwordDB'
}->{$type} }->{$type}
) )
{ {
if ( my $sub = $self->$mod->can('name') ) { if ( my $sub = $self->$mod->can('name') ) {
return $sub->( $self->$mod, $req, $type ); return $sub->( $self->$mod, $req, $type );
} }
else { else {
my $s = ref( $self->$mod ); my $s = ref( $self->$mod );
$s =~ $s
s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//; =~ s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//;
return $s; return $s;
} }
} }
@ -295,7 +287,7 @@ sub autoRedirect {
# Set redirection URL if needed # Set redirection URL if needed
$req->{urldc} ||= $self->conf->{portal} $req->{urldc} ||= $self->conf->{portal}
if ( $req->mustRedirect and not( $req->info ) ); if ( $req->mustRedirect and not( $req->info ) );
# Redirection should be made if urldc defined # Redirection should be made if urldc defined
if ( $req->{urldc} ) { if ( $req->{urldc} ) {
@ -305,8 +297,9 @@ sub autoRedirect {
$req->data->{redirectFormMethod} = "get"; $req->data->{redirectFormMethod} = "get";
} }
else { else {
return [ 302, return [
[ Location => $req->{urldc}, @{ $req->respHeaders } ], [] ]; 302, [ Location => $req->{urldc}, @{ $req->respHeaders } ], []
];
} }
} }
my ( $tpl, $prms ) = $self->display($req); my ( $tpl, $prms ) = $self->display($req);
@ -326,8 +319,8 @@ sub getApacheSession {
$self->logger->debug("Try to get a new $args{kind} session"); $self->logger->debug("Try to get a new $args{kind} session");
} }
my $as = Lemonldap::NG::Common::Session->new( { my $as = Lemonldap::NG::Common::Session->new(
storageModule => $self->conf->{globalStorage}, { storageModule => $self->conf->{globalStorage},
storageModuleOptions => $self->conf->{globalStorageOptions}, storageModuleOptions => $self->conf->{globalStorageOptions},
cacheModule => $self->conf->{localSessionStorage}, cacheModule => $self->conf->{localSessionStorage},
cacheModuleOptions => $self->conf->{localSessionStorageOptions}, cacheModuleOptions => $self->conf->{localSessionStorageOptions},
@ -341,8 +334,7 @@ sub getApacheSession {
if ( my $err = $as->error ) { if ( my $err = $as->error ) {
$self->lmLog( $self->lmLog(
$err, $err,
( ( $err =~ /(?:Object does not exist|Invalid session ID)/
$err =~ /(?:Object does not exist|Invalid session ID)/
? 'notice' ? 'notice'
: 'error' : 'error'
) )
@ -357,21 +349,19 @@ sub getApacheSession {
$self->logger->debug("Get session $id from Portal::Main::Run") if ($id); $self->logger->debug("Get session $id from Portal::Main::Run") if ($id);
$self->logger->debug( $self->logger->debug(
"Check session validity -> " . $self->conf->{timeoutActivity} . "s" ) "Check session validity -> " . $self->conf->{timeoutActivity} . "s" )
if ( $self->conf->{timeoutActivity} ); if ( $self->conf->{timeoutActivity} );
my $now = time; my $now = time;
if ( if ( $id
$id
and defined $as->data->{_utime} and defined $as->data->{_utime}
and ( and (
( ( $now - $as->data->{_utime} ) > $self->conf->{timeout} ) ( ( $now - $as->data->{_utime} ) > $self->conf->{timeout} )
or ( or ( $self->conf->{timeoutActivity}
$self->conf->{timeoutActivity}
and $as->data->{_lastSeen} and $as->data->{_lastSeen}
and ( ( $now - $as->data->{_lastSeen} ) > and ( ( $now - $as->data->{_lastSeen} )
$self->conf->{timeoutActivity} ) > $self->conf->{timeoutActivity} )
) )
) )
) )
{ {
$self->logger->debug("Session $args{kind} $id expired"); $self->logger->debug("Session $args{kind} $id expired");
return; return;
@ -393,8 +383,8 @@ sub getPersistentSession {
$info->{_session_uid} = $uid; $info->{_session_uid} = $uid;
my $ps = Lemonldap::NG::Common::Session->new( { my $ps = Lemonldap::NG::Common::Session->new(
storageModule => $self->conf->{persistentStorage}, { storageModule => $self->conf->{persistentStorage},
storageModuleOptions => $self->conf->{persistentStorageOptions}, storageModuleOptions => $self->conf->{persistentStorageOptions},
id => $pid, id => $pid,
force => 1, force => 1,
@ -435,10 +425,11 @@ sub updatePersistentSession {
# Return if no infos to update # Return if no infos to update
return () unless ( ref $infos eq 'HASH' and %$infos ); return () unless ( ref $infos eq 'HASH' and %$infos );
$uid ||= $req->{sessionInfo}->{ $self->conf->{whatToTrace} } $uid ||= $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|| $req->userData->{ $self->conf->{whatToTrace} }; || $req->userData->{ $self->conf->{whatToTrace} };
$self->logger->debug("Found 'whatToTrace' -> $uid"); $self->logger->debug("Found 'whatToTrace' -> $uid");
unless ($uid) { unless ($uid) {
$self->logger->debug('No uid found, skipping updatePersistentSession'); $self->logger->debug(
'No uid found, skipping updatePersistentSession');
return (); return ();
} }
$self->logger->debug("Update $uid persistent session"); $self->logger->debug("Update $uid persistent session");
@ -480,14 +471,14 @@ sub updateSession {
foreach ( keys %$infos ) { foreach ( keys %$infos ) {
$self->logger->debug( $self->logger->debug(
"Update sessionInfo $_ with " . $infos->{$_} ); "Update sessionInfo $_ with " . $infos->{$_} );
$req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_} = $req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_}
$infos->{$_}; = $infos->{$_};
} }
# Update session in global storage with _updateTime # Update session in global storage with _updateTime
$infos->{_updateTime} = strftime( "%Y%m%d%H%M%S", localtime() ); $infos->{_updateTime} = strftime( "%Y%m%d%H%M%S", localtime() );
if ( my $apacheSession = if ( my $apacheSession
$self->getApacheSession( $id, info => $infos ) ) = $self->getApacheSession( $id, info => $infos ) )
{ {
if ( $apacheSession->error ) { if ( $apacheSession->error ) {
$self->logger->error("Cannot update session $id"); $self->logger->error("Cannot update session $id");
@ -570,10 +561,10 @@ sub isTrustedUrl {
sub stamp { sub stamp {
my $self = shift; my $self = shift;
my $res = my $res
$self->conf->{cipher} = $self->conf->{cipher}
? $self->conf->{cipher}->encrypt( time() ) ? $self->conf->{cipher}->encrypt( time() )
: 1; : 1;
$res =~ s/\+/%2B/g; $res =~ s/\+/%2B/g;
return $res; return $res;
} }
@ -705,7 +696,7 @@ sub cookie {
$h{path} ||= '/'; $h{path} ||= '/';
$h{HttpOnly} //= $self->conf->{httpOnly}; $h{HttpOnly} //= $self->conf->{httpOnly};
$h{max_age} //= $self->conf->{cookieExpiration} $h{max_age} //= $self->conf->{cookieExpiration}
if ( $self->conf->{cookieExpiration} ); if ( $self->conf->{cookieExpiration} );
foreach (qw(domain path expires max_age HttpOnly)) { foreach (qw(domain path expires max_age HttpOnly)) {
my $f = $_; my $f = $_;
$f =~ s/_/-/g; $f =~ s/_/-/g;
@ -726,16 +717,46 @@ sub _dump {
sub sendHtml { sub sendHtml {
my ( $self, $req, $template, %args ) = @_; my ( $self, $req, $template, %args ) = @_;
$args{params}->{TROVER} = $self->trOver; $args{params}->{TROVER} = $self->trOver;
$args{templateDir} = $args{templateDir}
$self->conf->{templateDir} . '/' . $self->getSkin($req); = $self->conf->{templateDir} . '/' . $self->getSkin($req);
my $tmpl = $args{templateDir} . "/$template.tpl";
my $troverJson = $args{templateDir} . "/$template.json";
unless ( -f $tmpl ) {
$self->logger->debug("Template : $tmpl NOT found!!!");
$args{templateDir} = $self->conf->{templateDir} . '/bootstrap';
$tmpl = $args{templateDir} . "/$template.tpl";
$troverJson = $args{templateDir} . "/$template.json";
$self->logger->debug("-> Trying to load $tmpl");
}
if ( -r $troverJson ) {
open my $tr_file, '<', $troverJson
or die "Can't open" . $troverJson . " : $!";
while (<$tr_file>) {
chomp;
$args{params}->{TROVERbyJSON} .= $_;
}
close $tr_file or die "Can't close $tr_file : $!";
eval { decode_json( $args{params}->{TROVERbyJSON} ) };
if ($@) {
$self->logger->debug("$troverJson is NOT a regular JSON file!!!");
$args{params}->{TROVERbyJSON} = '';
}
else {
$self->logger->debug(" -> Overriding messages with $troverJson");
$self->logger->debug(
" -> File content : $args{params}->{TROVERbyJSON}");
}
}
my $res = $self->SUPER::sendHtml( $req, $template, %args ); my $res = $self->SUPER::sendHtml( $req, $template, %args );
push @{ $res->[1] }, push @{ $res->[1] },
'X-XSS-Protection' => '1; mode=block', 'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff', 'X-Content-Type-Options' => 'nosniff',
'Cache-Control' => 'no-cache, no-store, must-revalidate',# HTTP 1.1 'Cache-Control' => 'no-cache, no-store, must-revalidate', # HTTP 1.1
'Pragma' => 'no-cache', # HTTP 1.0 'Pragma' => 'no-cache', # HTTP 1.0
'Expires' => '0'; # Proxies 'Expires' => '0'; # Proxies
# Set authorized URL for POST # Set authorized URL for POST
my $csp = $self->csp . "form-action " . $self->conf->{cspFormAction}; my $csp = $self->csp . "form-action " . $self->conf->{cspFormAction};
@ -749,13 +770,14 @@ sub sendHtml {
if ( defined $url ) { if ( defined $url ) {
$self->logger->debug("Required Params URL : $url"); $self->logger->debug("Required Params URL : $url");
if ( $url =~ s#(https?://[^/]+).*#$1# ) { if ( $url =~ s#(https?://[^/]+).*#$1# ) {
$self->logger->debug("Set CSP form-action with Params URL : $url"); $self->logger->debug(
"Set CSP form-action with Params URL : $url");
$csp .= " $url"; $csp .= " $url";
} }
} }
if ( defined $req->{cspFormAction} ) { if ( defined $req->{cspFormAction} ) {
$self->logger->debug( $self->logger->debug( "Set CSP form-action with request URL: "
"Set CSP form-action with request URL: " . $req->{cspFormAction} ); . $req->{cspFormAction} );
$csp .= " " . $req->{cspFormAction}; $csp .= " " . $req->{cspFormAction};
} }
@ -781,7 +803,7 @@ sub sendHtml {
my @url; my @url;
if ( $req->info ) { if ( $req->info ) {
@url = map { s#https?://([^/]+).*#$1#; $_ } @url = map { s#https?://([^/]+).*#$1#; $_ }
( $req->info =~ /<iframe.*?src="(.*?)"/sg ); ( $req->info =~ /<iframe.*?src="(.*?)"/sg );
} }
if (@url) { if (@url) {
$csp .= join( ' ', 'child-src', @url ) . ';'; $csp .= join( ' ', 'child-src', @url ) . ';';
@ -797,18 +819,17 @@ sub sendCss {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my $s = '/* LL::NG Portal CSS */'; my $s = '/* LL::NG Portal CSS */';
if ( $self->conf->{portalSkinBackground} ) { if ( $self->conf->{portalSkinBackground} ) {
$s .= $s
'html,body{background:url("' .= 'html,body{background:url("'
. $self->staticPrefix . $self->staticPrefix
. '/common/backgrounds/' . '/common/backgrounds/'
. $self->conf->{portalSkinBackground} . $self->conf->{portalSkinBackground}
. '") no-repeat center fixed;' . '") no-repeat center fixed;'
. 'background-size:cover;}'; . 'background-size:cover;}';
} }
return [ return [
200, 200,
[ [ 'Content-Type' => 'text/css',
'Content-Type' => 'text/css',
'Content-Length' => length($s), 'Content-Length' => length($s),
'Cache-Control' => 'public,max-age=3600', 'Cache-Control' => 'public,max-age=3600',
], ],
@ -832,16 +853,16 @@ sub lmError {
# Error code # Error code
$templateParams{"ERROR$_"} = ( $httpError == $_ ? 1 : 0 ) $templateParams{"ERROR$_"} = ( $httpError == $_ ? 1 : 0 )
foreach ( 403, 404, 500, 502, 503 ); foreach ( 403, 404, 500, 502, 503 );
return $self->sendHtml( $req, 'error', params => \%templateParams ); return $self->sendHtml( $req, 'error', params => \%templateParams );
} }
sub rebuildCookies { sub rebuildCookies {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my @tmp; my @tmp;
for ( my $i = 0 ; $i < @{ $req->{respHeaders} } ; $i += 2 ) { for ( my $i = 0; $i < @{ $req->{respHeaders} }; $i += 2 ) {
push @tmp, $req->respHeaders->[0], $req->respHeaders->[1] push @tmp, $req->respHeaders->[0], $req->respHeaders->[1]
unless ( $req->respHeaders->[0] eq 'Set-Cookie' ); unless ( $req->respHeaders->[0] eq 'Set-Cookie' );
} }
$req->{respHeaders} = \@tmp; $req->{respHeaders} = \@tmp;
$self->buildCookie($req); $self->buildCookie($req);
@ -856,8 +877,8 @@ sub tplParams {
$portalPath =~ s#[^/]+\.fcgi$##; $portalPath =~ s#[^/]+\.fcgi$##;
for my $session_key ( keys %{ $req->{sessionInfo} } ) { for my $session_key ( keys %{ $req->{sessionInfo} } ) {
$templateParams{ "session_" . $session_key } = $templateParams{ "session_" . $session_key }
$req->{sessionInfo}->{$session_key}; = $req->{sessionInfo}->{$session_key};
} }
for my $env_key ( keys %{ $req->env } ) { for my $env_key ( keys %{ $req->env } ) {
@ -878,7 +899,7 @@ sub tplParams {
sub registerLogin { sub registerLogin {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return return
unless ( $self->conf->{loginHistoryEnabled} unless ( $self->conf->{loginHistoryEnabled}
and defined $req->authResult ); and defined $req->authResult );
my $history = $req->sessionInfo->{_loginHistory} ||= {}; my $history = $req->sessionInfo->{_loginHistory} ||= {};
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login'; my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
@ -888,17 +909,18 @@ sub registerLogin {
# Gather current login's parameters # Gather current login's parameters
my $login = $self->_sumUpSession( $req->{sessionInfo}, 1 ); my $login = $self->_sumUpSession( $req->{sessionInfo}, 1 );
$login->{error} = $self->error( $req->authResult ) $login->{error} = $self->error( $req->authResult )
if ( $req->authResult ); if ( $req->authResult );
$self->logger->debug( " Current login -> " . $login->{error} ) $self->logger->debug( " Current login -> " . $login->{error} )
if ( $login->{error} ); if ( $login->{error} );
# Add current login into history # Add current login into history
unshift @{ $history->{$type} }, $login; unshift @{ $history->{$type} }, $login;
# Forget oldest logins # Forget oldest logins
splice @{ $history->{$type} }, $self->conf->{ $type . "Number" } splice @{ $history->{$type} }, $self->conf->{ $type . "Number" }
if ( scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } ); if (
scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
# Save into persistent session # Save into persistent session
$self->updatePersistentSession( $req, { _loginHistory => $history, } ); $self->updatePersistentSession( $req, { _loginHistory => $history, } );
@ -911,12 +933,12 @@ sub registerLogin {
# @return hashref # @return hashref
sub _sumUpSession { sub _sumUpSession {
my ( $self, $session, $withoutUser ) = @_; my ( $self, $session, $withoutUser ) = @_;
my $res = my $res
$withoutUser = $withoutUser
? {} ? {}
: { user => $session->{ $self->conf->{whatToTrace} } }; : { user => $session->{ $self->conf->{whatToTrace} } };
$res->{$_} = $session->{$_} $res->{$_} = $session->{$_}
foreach ( "_utime", "ipAddr", foreach ( "_utime", "ipAddr",
keys %{ $self->conf->{sessionDataToRemember} } ); keys %{ $self->conf->{sessionDataToRemember} } );
return $res; return $res;
} }
@ -925,12 +947,12 @@ sub _sumUpSession {
sub loadTemplate { sub loadTemplate {
my ( $self, $name, %prm ) = @_; my ( $self, $name, %prm ) = @_;
$name .= '.tpl'; $name .= '.tpl';
my $file = my $file
$self->conf->{templateDir} . '/' = $self->conf->{templateDir} . '/'
. $self->conf->{portalSkin} . '/' . $self->conf->{portalSkin} . '/'
. $name; . $name;
$file = $self->conf->{templateDir} . '/common/' . $name $file = $self->conf->{templateDir} . '/common/' . $name
unless ( -e $file ); unless ( -e $file );
unless ( -e $file ) { unless ( -e $file ) {
die "Unable to find $name in $self->conf->{templateDir}"; die "Unable to find $name in $self->conf->{templateDir}";
} }

View File

@ -0,0 +1,215 @@
package Lemonldap::NG::Portal::Plugins::CheckUser;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_TOKENEXPIRED
PE_NOTOKEN
);
our $VERSION = '2.0.3';
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
has ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott = $_[0]->{p}
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{formTimeout} );
return $ott;
}
);
sub hAttr {
$_[0]->{conf}->{checkUserHiddenAttributes} . ' '
. $_[0]->{conf}->{hiddenAttributes};
}
sub init {
my ($self) = @_;
$self->addAuthRoute( checkuser => 'check', ['POST'] );
$self->addAuthRoute( checkuser => 'display', ['GET'] );
return 1;
}
# RUNNING METHOD
sub check {
my ( $self, $req ) = @_;
my ( $attrs, $array_attrs, $array_hdrs ) = ( {}, [], [] );
my $msg = my $auth = '';
# 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 );
}
unless ( $self->ott->getToken($token) ) {
$self->userLogger->warn('Ask try with expired/bad token');
$msg = PE_TOKENEXPIRED;
$token = $self->ott->createToken( $req->sessionInfo );
}
return $self->p->sendHtml(
$req,
'checkuser',
params => {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => "PE$msg",
ALERTE => 'alert-warning',
TOKEN => $token,
}
) if $msg;
}
## 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};
$attrs = {};
}
else {
# Create an array of hashes for template loop
$self->logger->debug("Delete hidden or empty attributes");
foreach my $k ( sort keys %$attrs ) {
# Ignore hidden attributes or empty values
if ( $self->conf->{checkUserDisplayEmptyValues} ) {
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ );
}
else {
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ or !$attrs->{$k} );
}
}
$msg = 'checkUser';
}
# Check if user is allowed to access submitted URL and compute headers
if ( $url and %$attrs ) {
# User is allowed ?
$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" );
# Return VirtualHost headers
$array_hdrs = $self->_headers( $req, $url );
}
my $token = $self->ott->createToken( $req->sessionInfo );
# 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,
TOKEN => $token,
}
);
}
sub display {
my ( $self, $req ) = @_;
my $token = $self->ott->createToken( $req->sessionInfo );
# 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 => $token,
}
);
}
sub _userDatas {
my ( $self, $req ) = @_;
# Search user in database
my $steps = [ 'getUser', 'setSessionInfo', 'setMacros', 'setGroups' ];
$self->conf->{checkUserDisplayPersistentInfo}
? 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->logger->debug("Process returned error: $error");
return $req->error($error);
}
return $req->{sessionInfo};
}
sub _authorization {
my ( $self, $req, $uri ) = @_;
# Check rights
my ( $vhost, $appuri ) = $uri =~ m#^https?://([^/]*)(.*)#;
$vhost =~ s/:\d+$//;
$vhost = $self->p->HANDLER->resolveAlias($vhost);
$appuri ||= '/';
return $self->p->HANDLER->grant( $req, $req->{sessionInfo}, $appuri,
undef, $vhost );
}
sub _headers {
my ( $self, $req, $uri ) = @_;
my ( $vhost, $appuri ) = $uri =~ m#^https?://([^/]*)(.*)#;
$vhost =~ s/:\d+$//;
$req->{env}->{HTTP_HOST} = $vhost;
$self->p->HANDLER->headersInit( $self->{conf} );
return $self->p->HANDLER->checkHeaders( $req, $req->{sessionInfo} );
}
1;

View File

@ -29,6 +29,9 @@ translatePage = (lang) ->
$(this).text txt $(this).text txt
$("[trmsg]").each -> $("[trmsg]").each ->
$(this).text translate "PE#{$(this).attr 'trmsg'}" $(this).text translate "PE#{$(this).attr 'trmsg'}"
msg = translate "PE#{$(this).attr 'trmsg'}"
if msg.match /_hide_/
$(this).parent().hide()
$("[trplaceholder]").each -> $("[trplaceholder]").each ->
$(this).attr 'placeholder', translate($(this).attr('trplaceholder')) $(this).attr 'placeholder', translate($(this).attr('trplaceholder'))
$("[localtime]").each -> $("[localtime]").each ->
@ -53,6 +56,7 @@ getValues = () ->
catch e catch e
console.log 'Parsing error', e console.log 'Parsing error', e
console.log 'JSON', $(this).text() console.log 'JSON', $(this).text()
console.log values
values values
# Code from http://snipplr.com/view/29434/ # Code from http://snipplr.com/view/29434/

View File

@ -37,7 +37,12 @@ LemonLDAP::NG Portal jQuery scripts
return $(this).text(txt); return $(this).text(txt);
}); });
$("[trmsg]").each(function() { $("[trmsg]").each(function() {
return $(this).text(translate("PE" + ($(this).attr('trmsg')))); var msg;
$(this).text(translate("PE" + ($(this).attr('trmsg'))));
msg = translate("PE" + ($(this).attr('trmsg')));
if (msg.match(/_hide_/)) {
return $(this).parent().hide();
}
}); });
$("[trplaceholder]").each(function() { $("[trplaceholder]").each(function() {
return $(this).attr('placeholder', translate($(this).attr('trplaceholder'))); return $(this).attr('placeholder', translate($(this).attr('trplaceholder')));
@ -78,6 +83,7 @@ LemonLDAP::NG Portal jQuery scripts
return console.log('JSON', $(this).text()); return console.log('JSON', $(this).text());
} }
}); });
console.log(values);
return values; return values;
}; };

File diff suppressed because one or more lines are too long

View File

@ -98,10 +98,12 @@
"accountCreated":"تم إنشاء حسابك و إرسال كلمة المرور المؤقتة إلى بريدك الإلكتروني.", "accountCreated":"تم إنشاء حسابك و إرسال كلمة المرور المؤقتة إلى بريدك الإلكتروني.",
"accountCreationSuccess":"تم إنشاء حسابك بنجاح.", "accountCreationSuccess":"تم إنشاء حسابك بنجاح.",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"معلومات أخرى:", "anotherInformation":"معلومات أخرى:",
"areYouSure":"هل أنت واثق؟", "areYouSure":"هل أنت واثق؟",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"بوابة إثبات الهوية", "authPortal":"بوابة إثبات الهوية",
"authRemaining":"٪ s المصادقة المتبقية، غيير كلمة المرور الخاصة بك!", "authRemaining":"٪ s المصادقة المتبقية، غيير كلمة المرور الخاصة بك!",
"autoAccept":"تقبل تلقائيا في 30 ثانية", "autoAccept":"تقبل تلقائيا في 30 ثانية",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"غير كلمة المرور الخاصة بك", "changePwd":"غير كلمة المرور الخاصة بك",
"checkLastLogins":"تحقق من آخر تسجيلات دخول الخاصة بي", "checkLastLogins":"تحقق من آخر تسجيلات دخول الخاصة بي",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"اختر أحد التطبيقات المسموح لك بالدخول إليها", "chooseApp":"اختر أحد التطبيقات المسموح لك بالدخول إليها",
"clickHere":"الرجاء الضغط هنا", "clickHere":"الرجاء الضغط هنا",
@ -137,15 +140,18 @@
"errorMsg":"رسالة خاطئة", "errorMsg":"رسالة خاطئة",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"الاسم الاول", "firstName":"الاسم الاول",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"نسيت كلمة المرور؟", "forgotPwd":"نسيت كلمة المرور؟",
"generatePwd":"إنشاء كلمة المرور تلقائيا", "generatePwd":"إنشاء كلمة المرور تلقائيا",
"gotNewMessages":"لديك بعض الرسائل الجديدة", "gotNewMessages":"لديك بعض الرسائل الجديدة",
"goToPortal":"انتقل إلى البوابة", "goToPortal":"انتقل إلى البوابة",
"gplSoft":"البرمجيات الحرة التي تغطيها رخصة GPL", "gplSoft":"البرمجيات الحرة التي تغطيها رخصة GPL",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"انا متاكد", "imSure":"انا متاكد",
"info":"معلومات", "info":"معلومات",
"ipAddr":"عنوان الأي بي", "ipAddr":"عنوان الأي بي",
"key":"Key",
"lastFailedLogins":"عمليات تسجيل الدخول الأخيرة الغير الناجحة", "lastFailedLogins":"عمليات تسجيل الدخول الأخيرة الغير الناجحة",
"lastLogins":"آخر تسجيلات دخول", "lastLogins":"آخر تسجيلات دخول",
"lastName":"اسم العائلة", "lastName":"اسم العائلة",
@ -227,6 +233,7 @@
"upgradeSession":"ترقية الجلسة", "upgradeSession":"ترقية الجلسة",
"user":"المستخدم", "user":"المستخدم",
"useYubikey":"استخدم اليوبي كي الخاص بك", "useYubikey":"استخدم اليوبي كي الخاص بك",
"value":"Value",
"verify":"التحقق", "verify":"التحقق",
"wait":"انتظر", "wait":"انتظر",
"warning":"تحذير", "warning":"تحذير",

View File

@ -98,10 +98,12 @@
"accountCreated":"Ihr Konto wurde erstellt, das temporäre Passwort wurde an Ihre E-Mail-Adresse gesendet.", "accountCreated":"Ihr Konto wurde erstellt, das temporäre Passwort wurde an Ihre E-Mail-Adresse gesendet.",
"accountCreationSuccess":"Ihr Account wurde erfolgreich erstellt.", "accountCreationSuccess":"Ihr Account wurde erfolgreich erstellt.",
"action":"Aktion", "action":"Aktion",
"allowed":"Access ALLOWED",
"anotherInformation":"Eine weitere Information:", "anotherInformation":"Eine weitere Information:",
"areYouSure":"Sind Sie sicher ?", "areYouSure":"Sind Sie sicher ?",
"askToRenew":"Diese Anwendung benötigt eine neuere Authentifizierung. Möchten Sie sich erneut authentifizieren?", "askToRenew":"Diese Anwendung benötigt eine neuere Authentifizierung. Möchten Sie sich erneut authentifizieren?",
"askToUpgrade":"Diese Anwendung benötigt eine höhere Authentifizierungsstufe. Möchten Sie sich erneut authentifizieren?", "askToUpgrade":"Diese Anwendung benötigt eine höhere Authentifizierungsstufe. Möchten Sie sich erneut authentifizieren?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentifizierungsportal", "authPortal":"Authentifizierungsportal",
"authRemaining":"%sverbleibende Authentifizierungen, bitte Passwort ändern!", "authRemaining":"%sverbleibende Authentifizierungen, bitte Passwort ändern!",
"autoAccept":"Automatisch in 30 Sekunden annehmen", "autoAccept":"Automatisch in 30 Sekunden annehmen",
@ -114,6 +116,7 @@
"changeKey":"Neuen Schlüssel erzeugen", "changeKey":"Neuen Schlüssel erzeugen",
"changePwd":"Ändere dein Passwort", "changePwd":"Ändere dein Passwort",
"checkLastLogins":"Überprüfe meine letzten Logins", "checkLastLogins":"Überprüfe meine letzten Logins",
"checkUser":"Check user session",
"choose2f":"Wählen deinen Ihren zweiten Faktor", "choose2f":"Wählen deinen Ihren zweiten Faktor",
"chooseApp":"Wählen Sie eine Anwendung aus, auf die du zugreifen darfst", "chooseApp":"Wählen Sie eine Anwendung aus, auf die du zugreifen darfst",
"clickHere":"Bitte hier klicken", "clickHere":"Bitte hier klicken",
@ -137,15 +140,18 @@
"errorMsg":"Fehlermeldung", "errorMsg":"Fehlermeldung",
"fillTheForm":"Fülle das Formular aus", "fillTheForm":"Fülle das Formular aus",
"firstName":"Vorname", "firstName":"Vorname",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Passwort vergessen ?", "forgotPwd":"Passwort vergessen ?",
"generatePwd":"Passwort automatisch generieren", "generatePwd":"Passwort automatisch generieren",
"gotNewMessages":"Du hast neue Nachrichten", "gotNewMessages":"Du hast neue Nachrichten",
"goToPortal":"Zum Portal", "goToPortal":"Zum Portal",
"gplSoft":"Freie Software, die von der GPL-Lizenz abgedeckt wird", "gplSoft":"Freie Software, die von der GPL-Lizenz abgedeckt wird",
"headers":"HEADERS",
"id":"ID", "id":"ID",
"imSure":"Ich bin sicher", "imSure":"Ich bin sicher",
"info":"Information", "info":"Information",
"ipAddr":"IP Adresse", "ipAddr":"IP Adresse",
"key":"Key",
"lastFailedLogins":"Letzte fehlgeschlagene Anmeldungen", "lastFailedLogins":"Letzte fehlgeschlagene Anmeldungen",
"lastLogins":"Letzte Anmeldungen", "lastLogins":"Letzte Anmeldungen",
"lastName":"Nachname", "lastName":"Nachname",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"Benutzer", "user":"Benutzer",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify", "verify":"Verify",
"wait":"Warten", "wait":"Warten",
"warning":"Warnung", "warning":"Warnung",

View File

@ -98,10 +98,12 @@
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.", "accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
"accountCreationSuccess":"Your account was successfully created.", "accountCreationSuccess":"Your account was successfully created.",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"areYouSure":"Are you sure?", "areYouSure":"Are you sure?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"Automatically accept in 30 seconds", "autoAccept":"Automatically accept in 30 seconds",
@ -114,6 +116,7 @@
"changeKey": "Generate new key", "changeKey": "Generate new key",
"changePwd":"Change your password", "changePwd":"Change your password",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here", "clickHere":"Please click here",
@ -137,15 +140,18 @@
"errorMsg":"Error Message", "errorMsg":"Error Message",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"First name", "firstName":"First name",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Forgot your password?", "forgotPwd":"Forgot your password?",
"generatePwd":"Generate the password automatically", "generatePwd":"Generate the password automatically",
"gotNewMessages":"You have some new messages", "gotNewMessages":"You have some new messages",
"goToPortal":"Go to portal", "goToPortal":"Go to portal",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"I'm sure", "imSure":"I'm sure",
"info":"Information", "info":"Information",
"ipAddr":"IP address", "ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Last failed logins", "lastFailedLogins":"Last failed logins",
"lastLogins":"Last logins", "lastLogins":"Last logins",
"lastName":"Last name", "lastName":"Last name",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"User", "user":"User",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify": "Verify", "verify": "Verify",
"wait":"Wait", "wait":"Wait",
"warning":"Warning", "warning":"Warning",

View File

@ -98,10 +98,12 @@
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.", "accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
"accountCreationSuccess":"Your account was successfully created.", "accountCreationSuccess":"Your account was successfully created.",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"areYouSure":"Are you sure?", "areYouSure":"Are you sure?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"Automatically accept in 30 seconds", "autoAccept":"Automatically accept in 30 seconds",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"Change your password", "changePwd":"Change your password",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here", "clickHere":"Please click here",
@ -137,15 +140,18 @@
"errorMsg":"Error Message", "errorMsg":"Error Message",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"First name", "firstName":"First name",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Forgot your password?", "forgotPwd":"Forgot your password?",
"generatePwd":"Generate the password automatically", "generatePwd":"Generate the password automatically",
"gotNewMessages":"You have some new messages", "gotNewMessages":"You have some new messages",
"goToPortal":"Go to portal", "goToPortal":"Go to portal",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"I'm sure", "imSure":"I'm sure",
"info":"Information", "info":"Information",
"ipAddr":"IP address", "ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Last failed logins", "lastFailedLogins":"Last failed logins",
"lastLogins":"Last logins", "lastLogins":"Last logins",
"lastName":"Last name", "lastName":"Last name",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"User", "user":"User",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify", "verify":"Verify",
"wait":"Wait", "wait":"Wait",
"warning":"Warning", "warning":"Warning",

View File

@ -4,7 +4,7 @@
"PE2":"Identifiant ou mot de passe non renseigné", "PE2":"Identifiant ou mot de passe non renseigné",
"PE3":"Compte ou mot de passe LDAP de l'application incorrect", "PE3":"Compte ou mot de passe LDAP de l'application incorrect",
"PE4":"Utilisateur inexistant", "PE4":"Utilisateur inexistant",
"PE5":"Mot de passe ou identifiant incorrect", "PE5":"Identifiant ou mot de passe incorrect",
"PE6":"Connexion impossible au serveur LDAP", "PE6":"Connexion impossible au serveur LDAP",
"PE7":"Erreur anormale du serveur LDAP", "PE7":"Erreur anormale du serveur LDAP",
"PE8":"Erreur du module Apache::Session choisi", "PE8":"Erreur du module Apache::Session choisi",
@ -98,10 +98,12 @@
"accountCreated":"Votre compte a été créé, un mot de passe temporaire a été envoyé à votre adresse mail.", "accountCreated":"Votre compte a été créé, un mot de passe temporaire a été envoyé à votre adresse mail.",
"accountCreationSuccess":"Votre compte a bien été créé.", "accountCreationSuccess":"Votre compte a bien été créé.",
"action":"Action", "action":"Action",
"allowed":"Accès AUTORISE",
"anotherInformation":"Une autre information :", "anotherInformation":"Une autre information :",
"areYouSure":"Êtes-vous sûr ?", "areYouSure":"Êtes-vous sûr ?",
"askToRenew":"Cette application nécessite une authentification plus récente. Voulez-vous vous réauthentifier ?", "askToRenew":"Cette application nécessite une authentification plus récente. Voulez-vous vous réauthentifier ?",
"askToUpgrade":"Cette application nécessite un plus haut niveau d'authentification. Voulez-vous vous réauthentifier ?", "askToUpgrade":"Cette application nécessite un plus haut niveau d'authentification. Voulez-vous vous réauthentifier ?",
"attributes":"ATTRIBUTS",
"authPortal":"Portail d'authentification", "authPortal":"Portail d'authentification",
"authRemaining":"%s authentifications restantes, changez votre mot de passe !", "authRemaining":"%s authentifications restantes, changez votre mot de passe !",
"autoAccept":"Acceptation automatique dans 30 secondes", "autoAccept":"Acceptation automatique dans 30 secondes",
@ -114,6 +116,7 @@
"changeKey": "Générer une nouvelle clef", "changeKey": "Générer une nouvelle clef",
"changePwd":"Changez votre mot de passe", "changePwd":"Changez votre mot de passe",
"checkLastLogins":"Voir mes dernières connexions", "checkLastLogins":"Voir mes dernières connexions",
"checkUser":"Vérifier la session d'un utilisateur",
"choose2f":"Choisissez votre second facteur", "choose2f":"Choisissez votre second facteur",
"chooseApp":"Choisissez une application à laquelle vous êtes autorisé à accéder", "chooseApp":"Choisissez une application à laquelle vous êtes autorisé à accéder",
"clickHere":"Cliquez ici", "clickHere":"Cliquez ici",
@ -136,16 +139,19 @@
"enterYubikey":"Utilisez votre Yubikey", "enterYubikey":"Utilisez votre Yubikey",
"errorMsg":"Message d'erreur", "errorMsg":"Message d'erreur",
"fillTheForm":"Remplissez le formulaire", "fillTheForm":"Remplissez le formulaire",
"forbidden":"Accès INTERDIT",
"firstName":"Prénom", "firstName":"Prénom",
"forgotPwd":"Mot de passe oublié ?", "forgotPwd":"Mot de passe oublié ?",
"generatePwd":"Générer le mot de passe automatiquement", "generatePwd":"Générer le mot de passe automatiquement",
"gotNewMessages":"Vous avez de nouveaux messages", "gotNewMessages":"Vous avez de nouveaux messages",
"goToPortal":"Aller au portail", "goToPortal":"Aller au portail",
"gplSoft":"logiciel libre protégé par la licence GPL", "gplSoft":"logiciel libre protégé par la licence GPL",
"headers":"ENTETES",
"id":"Id", "id":"Id",
"imSure":"Je suis sûr", "imSure":"Je suis sûr",
"info":"Information", "info":"Information",
"ipAddr":"Adresse IP", "ipAddr":"Adresse IP",
"key":"Clef",
"lastFailedLogins":"Dernières connexions refusées", "lastFailedLogins":"Dernières connexions refusées",
"lastLogins":"Dernières connexions", "lastLogins":"Dernières connexions",
"lastName":"Nom", "lastName":"Nom",
@ -227,6 +233,7 @@
"upgradeSession":"Se réauthentifier", "upgradeSession":"Se réauthentifier",
"user":"Utilisateur", "user":"Utilisateur",
"useYubikey":"Utilisez votre Yubikey", "useYubikey":"Utilisez votre Yubikey",
"value":"Valeur",
"verify": "Vérifier", "verify": "Vérifier",
"wait":"Attendre", "wait":"Attendre",
"warning":"Attention", "warning":"Attention",

View File

@ -98,10 +98,12 @@
"accountCreated":"Il tuo account è stato creato, la tua password temporanea è stata inviata all'indirizzo email.", "accountCreated":"Il tuo account è stato creato, la tua password temporanea è stata inviata all'indirizzo email.",
"accountCreationSuccess":"Il tuo account è stato creato con successo.", "accountCreationSuccess":"Il tuo account è stato creato con successo.",
"action":"Azione", "action":"Azione",
"allowed":"Access ALLOWED",
"anotherInformation":"Un'altra informazione:", "anotherInformation":"Un'altra informazione:",
"areYouSure":"Sei sicuro?", "areYouSure":"Sei sicuro?",
"askToRenew":"Questa applicazione richiede un'autenticazione più recente. Vuoi reautenticare?", "askToRenew":"Questa applicazione richiede un'autenticazione più recente. Vuoi reautenticare?",
"askToUpgrade":"Questa applicazione richiede un livello di autenticazione superiore. Vuoi reautenticare?", "askToUpgrade":"Questa applicazione richiede un livello di autenticazione superiore. Vuoi reautenticare?",
"attributes":"ATTRIBUTES",
"authPortal":"Portale di autenticazione", "authPortal":"Portale di autenticazione",
"authRemaining":"Rimangono ancora %s autenticazioni, modifica la password!", "authRemaining":"Rimangono ancora %s autenticazioni, modifica la password!",
"autoAccept":"Accetta automaticamente in 30 secondi", "autoAccept":"Accetta automaticamente in 30 secondi",
@ -114,6 +116,7 @@
"changeKey":"Genera nuova chiave", "changeKey":"Genera nuova chiave",
"changePwd":"Cambia la tua password", "changePwd":"Cambia la tua password",
"checkLastLogins":"Controllare i miei ultimi accessi", "checkLastLogins":"Controllare i miei ultimi accessi",
"checkUser":"Check user session",
"choose2f":"Scegli il tuo secondo fattore", "choose2f":"Scegli il tuo secondo fattore",
"chooseApp":"Scegli un'applicazione alla quale ti è consentito l'accesso", "chooseApp":"Scegli un'applicazione alla quale ti è consentito l'accesso",
"clickHere":"Per favore clicka qui", "clickHere":"Per favore clicka qui",
@ -137,15 +140,18 @@
"errorMsg":"Messaggio di errore", "errorMsg":"Messaggio di errore",
"fillTheForm":"Compila il modulo", "fillTheForm":"Compila il modulo",
"firstName":"Nome", "firstName":"Nome",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Password dimenticata?", "forgotPwd":"Password dimenticata?",
"generatePwd":"Generare automaticamente la password", "generatePwd":"Generare automaticamente la password",
"gotNewMessages":"Hai dei nuovi messaggi", "gotNewMessages":"Hai dei nuovi messaggi",
"goToPortal":"Vai al portale", "goToPortal":"Vai al portale",
"gplSoft":"Software libero coperto dalla licenza GPL", "gplSoft":"Software libero coperto dalla licenza GPL",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"Sono sicuro", "imSure":"Sono sicuro",
"info":"Informazioni", "info":"Informazioni",
"ipAddr":"Indirizzo IP", "ipAddr":"Indirizzo IP",
"key":"Key",
"lastFailedLogins":"Ultimi login non riusciti", "lastFailedLogins":"Ultimi login non riusciti",
"lastLogins":"Ultimi accessi", "lastLogins":"Ultimi accessi",
"lastName":"Cognome", "lastName":"Cognome",
@ -227,6 +233,7 @@
"upgradeSession":"Sessione di aggiornamento", "upgradeSession":"Sessione di aggiornamento",
"user":"Utente", "user":"Utente",
"useYubikey":"Usa la tua Yubikey", "useYubikey":"Usa la tua Yubikey",
"value":"Value",
"verify":"Verifica", "verify":"Verifica",
"wait":"Attendere", "wait":"Attendere",
"warning":"Avvertimento", "warning":"Avvertimento",

View File

@ -98,10 +98,12 @@
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.", "accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
"accountCreationSuccess":"Your account was successfully created.", "accountCreationSuccess":"Your account was successfully created.",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"areYouSure":"Are you sure?", "areYouSure":"Are you sure?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"Automatically accept in 30 seconds", "autoAccept":"Automatically accept in 30 seconds",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"Change your password", "changePwd":"Change your password",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here", "clickHere":"Please click here",
@ -137,15 +140,18 @@
"errorMsg":"Error Message", "errorMsg":"Error Message",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"First name", "firstName":"First name",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Forgot your password?", "forgotPwd":"Forgot your password?",
"generatePwd":"Generate the password automatically", "generatePwd":"Generate the password automatically",
"gotNewMessages":"You have some new messages", "gotNewMessages":"You have some new messages",
"goToPortal":"Go to portal", "goToPortal":"Go to portal",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"I'm sure", "imSure":"I'm sure",
"info":"Information", "info":"Information",
"ipAddr":"IP address", "ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Last failed logins", "lastFailedLogins":"Last failed logins",
"lastLogins":"Last logins", "lastLogins":"Last logins",
"lastName":"Last name", "lastName":"Last name",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"User", "user":"User",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify", "verify":"Verify",
"wait":"Wait", "wait":"Wait",
"warning":"Warning", "warning":"Warning",

View File

@ -98,10 +98,12 @@
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.", "accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
"accountCreationSuccess":"Your account was successfully created.", "accountCreationSuccess":"Your account was successfully created.",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"areYouSure":"Are you sure?", "areYouSure":"Are you sure?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"Automatically accept in 30 seconds", "autoAccept":"Automatically accept in 30 seconds",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"Change your password", "changePwd":"Change your password",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here", "clickHere":"Please click here",
@ -137,15 +140,18 @@
"errorMsg":"Error Message", "errorMsg":"Error Message",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"First name", "firstName":"First name",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Forgot your password?", "forgotPwd":"Forgot your password?",
"generatePwd":"Generate the password automatically", "generatePwd":"Generate the password automatically",
"gotNewMessages":"You have some new messages", "gotNewMessages":"You have some new messages",
"goToPortal":"Go to portal", "goToPortal":"Go to portal",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"I'm sure", "imSure":"I'm sure",
"info":"Information", "info":"Information",
"ipAddr":"IP address", "ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Last failed logins", "lastFailedLogins":"Last failed logins",
"lastLogins":"Last logins", "lastLogins":"Last logins",
"lastName":"Last name", "lastName":"Last name",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"User", "user":"User",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify", "verify":"Verify",
"wait":"Wait", "wait":"Wait",
"warning":"Warning", "warning":"Warning",

View File

@ -99,9 +99,11 @@
"accountCreationSuccess":"Your account was successfully created.", "accountCreationSuccess":"Your account was successfully created.",
"action":"Action", "action":"Action",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"allowed":"Access ALLOWED",
"areYouSure":"Are you sure?", "areYouSure":"Are you sure?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"Automatically accept in 30 seconds", "autoAccept":"Automatically accept in 30 seconds",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"Change your password", "changePwd":"Change your password",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here", "clickHere":"Please click here",
@ -137,15 +140,18 @@
"errorMsg":"Error Message", "errorMsg":"Error Message",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"First name", "firstName":"First name",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Forgot your password?", "forgotPwd":"Forgot your password?",
"generatePwd":"Generate the password automatically", "generatePwd":"Generate the password automatically",
"gotNewMessages":"You have some new messages", "gotNewMessages":"You have some new messages",
"goToPortal":"Go to portal", "goToPortal":"Go to portal",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"I'm sure", "imSure":"I'm sure",
"info":"Information", "info":"Information",
"ipAddr":"IP address", "ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Last failed logins", "lastFailedLogins":"Last failed logins",
"lastLogins":"Last logins", "lastLogins":"Last logins",
"lastName":"Last name", "lastName":"Last name",
@ -227,6 +233,7 @@
"upgradeSession":"Upgrade session", "upgradeSession":"Upgrade session",
"user":"User", "user":"User",
"useYubikey":"use your Yubikey", "useYubikey":"use your Yubikey",
"value":"Value",
"verify":"Verify", "verify":"Verify",
"wait":"Wait", "wait":"Wait",
"warning":"Warning", "warning":"Warning",

View File

@ -99,9 +99,11 @@
"accountCreationSuccess":"Tài khoản của bạn đã được tạo thành công.", "accountCreationSuccess":"Tài khoản của bạn đã được tạo thành công.",
"action":"Action", "action":"Action",
"anotherInformation":"Thông tin khác:", "anotherInformation":"Thông tin khác:",
"allowed":"Access ALLOWED",
"areYouSure":"Bạn có chắc không?", "areYouSure":"Bạn có chắc không?",
"askToRenew":"Ứng dụng này cần có chứng thực gần đây hơn. Bạn có muốn chứng thực lại?", "askToRenew":"Ứng dụng này cần có chứng thực gần đây hơn. Bạn có muốn chứng thực lại?",
"askToUpgrade":"Ứng dụng này cần một mức xác thực cao hơn. Bạn có muốn chứng thực lại?", "askToUpgrade":"Ứng dụng này cần một mức xác thực cao hơn. Bạn có muốn chứng thực lại?",
"attributes":"ATTRIBUTES",
"authPortal":"Cổng thông tin xác thực", "authPortal":"Cổng thông tin xác thực",
"authRemaining":"%s xác thực vẫn còn, thay đổi mật khẩu của bạn!", "authRemaining":"%s xác thực vẫn còn, thay đổi mật khẩu của bạn!",
"autoAccept":"Tự động chấp nhận trong 30 giây", "autoAccept":"Tự động chấp nhận trong 30 giây",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"Thay đổi mật khẩu của bạn", "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", "checkLastLogins":"Kiểm tra lần đăng nhập cuối cùng của bạn",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Chọn một ứng dụng bạn được phép truy cập vào", "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", "clickHere":"Vui lòng nhấp vào đây",
@ -137,15 +140,18 @@
"errorMsg":"Thông báo lỗi", "errorMsg":"Thông báo lỗi",
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"Tên", "firstName":"Tên",
"forbidden":"Access FORBIDDEN",
"forgotPwd":"Quên mật khẩu của bạn?", "forgotPwd":"Quên mật khẩu của bạn?",
"generatePwd":"Tạo mật khẩu tự động", "generatePwd":"Tạo mật khẩu tự động",
"gotNewMessages":"Bạn có một số tin nhắn mới", "gotNewMessages":"Bạn có một số tin nhắn mới",
"goToPortal":"Đi tới cổng thông tin", "goToPortal":"Đi tới cổng thông tin",
"gplSoft":"phần mềm tự do được cấp phép bởi GPL", "gplSoft":"phần mềm tự do được cấp phép bởi GPL",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"Tôi chắc chắn", "imSure":"Tôi chắc chắn",
"info":"Thông tin", "info":"Thông tin",
"ipAddr":"Địa chỉ IP", "ipAddr":"Địa chỉ IP",
"key":"Key",
"lastFailedLogins":"Lần cuối đăng nhập thất bại", "lastFailedLogins":"Lần cuối đăng nhập thất bại",
"lastLogins":"Đăng nhập lần cuối", "lastLogins":"Đăng nhập lần cuối",
"lastName":"Họ", "lastName":"Họ",
@ -227,6 +233,7 @@
"upgradeSession":"Phiên nâng cấp", "upgradeSession":"Phiên nâng cấp",
"user":"Người dùng", "user":"Người dùng",
"useYubikey":"sử dụng Yubikey của bạn", "useYubikey":"sử dụng Yubikey của bạn",
"value":"Value",
"verify":"Xác minh", "verify":"Xác minh",
"wait":"Hãy đợi", "wait":"Hãy đợi",
"warning":"Cảnh báo", "warning":"Cảnh báo",

View File

@ -98,10 +98,12 @@
"accountCreated":"您的账号已创建,临时密码已发送至您的邮箱", "accountCreated":"您的账号已创建,临时密码已发送至您的邮箱",
"accountCreationSuccess":"你的账户已创建", "accountCreationSuccess":"你的账户已创建",
"action":"Action", "action":"Action",
"allowed":"Access ALLOWED",
"anotherInformation":"Another information:", "anotherInformation":"Another information:",
"areYouSure":"您确定吗?", "areYouSure":"您确定吗?",
"askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?", "askToRenew":"This application needs a more recent authentication. Do you want to reauthenticate?",
"askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?", "askToUpgrade":"This application needs an higher authentication level. Do you want to reauthenticate?",
"attributes":"ATTRIBUTES",
"authPortal":"Authentication portal", "authPortal":"Authentication portal",
"authRemaining":"%s authentications remaining, change your password!", "authRemaining":"%s authentications remaining, change your password!",
"autoAccept":"30秒后自动接受", "autoAccept":"30秒后自动接受",
@ -114,6 +116,7 @@
"changeKey":"Generate new key", "changeKey":"Generate new key",
"changePwd":"修改您的密码", "changePwd":"修改您的密码",
"checkLastLogins":"Check my last logins", "checkLastLogins":"Check my last logins",
"checkUser":"Check user session",
"choose2f":"Choose your second factor", "choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to", "chooseApp":"Choose an application your are allowed to access to",
"clickHere":"请点击这里", "clickHere":"请点击这里",
@ -138,14 +141,17 @@
"fillTheForm":"Fill the form", "fillTheForm":"Fill the form",
"firstName":"名", "firstName":"名",
"forgotPwd":"忘记密码?", "forgotPwd":"忘记密码?",
"forbidden":"Access FORBIDDEN",
"generatePwd":"自动生成密码", "generatePwd":"自动生成密码",
"gotNewMessages":"您有一些新消息", "gotNewMessages":"您有一些新消息",
"goToPortal":"回到首页", "goToPortal":"回到首页",
"gplSoft":"free software covered by the GPL license", "gplSoft":"free software covered by the GPL license",
"headers":"HEADERS",
"id":"Id", "id":"Id",
"imSure":"我确认", "imSure":"我确认",
"info":"信息", "info":"信息",
"ipAddr":"IP 地址", "ipAddr":"IP 地址",
"key":"Key",
"lastFailedLogins":"上次失败的认证", "lastFailedLogins":"上次失败的认证",
"lastLogins":"上次登陆", "lastLogins":"上次登陆",
"lastName":"姓氏", "lastName":"姓氏",
@ -227,6 +233,7 @@
"upgradeSession":"升级会话", "upgradeSession":"升级会话",
"user":"用户", "user":"用户",
"useYubikey":"使用您的 Yubikey", "useYubikey":"使用您的 Yubikey",
"value":"Value",
"verify":"验证", "verify":"验证",
"wait":"等待", "wait":"等待",
"warning":"警告", "warning":"警告",

View File

@ -19,14 +19,15 @@
<th><span trspan="date">Date</span></th> <th><span trspan="date">Date</span></th>
<th> <th>
<TMPL_IF NAME="ACTION"> <TMPL_IF NAME="ACTION">
<span trspan="action">Action</span></th> <span trspan="action">Action</span>
</TMPL_IF> </TMPL_IF>
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<TMPL_LOOP NAME="SFDEVICES"> <TMPL_LOOP NAME="SFDEVICES">
<tr id='delete-<TMPL_VAR NAME="epoch">'> <tr id='delete-<TMPL_VAR NAME="epoch">'>
<td class="align-middle" ><TMPL_VAR NAME="type"></td> <td class="align-middle"><TMPL_VAR NAME="type"></td>
<td class="align-middle"><TMPL_VAR NAME="name"></td> <td class="align-middle"><TMPL_VAR NAME="name"></td>
<td class="data-epoch"><TMPL_VAR NAME="epoch"></td> <td class="data-epoch"><TMPL_VAR NAME="epoch"></td>
<td> <td>

View File

@ -0,0 +1,99 @@
<TMPL_INCLUDE NAME="header.tpl">
<div id="errorcontent" class="container">
<!--
<div class="message message-positive alert"><span trspan="<TMPL_VAR NAME="MSG">"></span></div>
-->
<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">
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-user"></i> </span>
</div>
<input name="user" type="text" class="form-control" value="<TMPL_VAR NAME="LOGIN">" trplaceholder="user" aria-required="true"/>
</div>
<div class="input-group mb-3">
<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"/>
</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>
</button>
</div>
<div>&nbsp;</div>
<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>
<th class="align-middle"><span trspan="key">Key</span></th>
<th class="align-middle"><span trspan="value">Value</span></th>
</tr>
</thead>
<tbody>
<TMPL_LOOP NAME="HEADERS">
<tr>
<td class="align-middle"><TMPL_VAR NAME="key"></td>
<td class="align-middle"><TMPL_VAR NAME="value"></td>
</tr>
</TMPL_LOOP>
</tbody>
</table>
</div>
</div>
</TMPL_IF>
<TMPL_IF NAME="ATTRIBUTES">
<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="attributes">ATTRIBUTES</span></tr>
<tr>
<th class="align-middle"><span trspan="key">Key</span></th>
<th class="align-middle"><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>
</tr>
</TMPL_LOOP>
</tbody>
</table>
</div>
</div>
</TMPL_IF>
<div class="buttons">
<button type="submit" class="btn btn-success">
<span class="fa fa-sign-in"></span>
<span trspan="checkUser">Check user</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>
</a>
</div>
</form>
</div>
<TMPL_INCLUDE NAME="footer.tpl">

View File

@ -0,0 +1,3 @@
{
"trOver":{"all":{},"fr":{"PE5":"Pas de chance, râté ! Merci de réessayer ..."},"en":{}}
}

View File

@ -1,4 +1,5 @@
<TMPL_INCLUDE NAME="header.tpl"> <TMPL_INCLUDE NAME="header.tpl">
<div id="errorcontent" class="container"> <div id="errorcontent" class="container">
<TMPL_IF AUTH_ERROR> <TMPL_IF AUTH_ERROR>
<div class="message message-<TMPL_VAR NAME="AUTH_ERROR_TYPE"> alert"><span trmsg="<TMPL_VAR NAME="AUTH_ERROR">"></span></div> <div class="message message-<TMPL_VAR NAME="AUTH_ERROR_TYPE"> alert"><span trmsg="<TMPL_VAR NAME="AUTH_ERROR">"></span></div>

View File

@ -13,8 +13,8 @@
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-lock"></i> </span> <span class="input-group-text"><i class="fa fa-lock"></i> </span>
<input name="code" value="" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />
</div> </div>
<input name="code" value="" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />
</div> </div>
</div> </div>
<div class="buttons"> <div class="buttons">
@ -23,13 +23,13 @@
<span trspan="connect">Connect</span> <span trspan="connect">Connect</span>
</button> </button>
</div> </div>
<br/>
<div class="buttons"> <div class="buttons">
<a href="<TMPL_VAR NAME="PORTAL_URL">?cancel=1" class="btn btn-primary" role="button"> <a href="<TMPL_VAR NAME="PORTAL_URL">?cancel=1" class="btn btn-primary" role="button">
<span class="fa fa-home"></span> <span class="fa fa-home"></span>
<span trspan="cancel">Cancel</span> <span trspan="cancel">Cancel</span>
</a> </a>
</div> </div>
</form>
</div> </div>
</main> </main>

View File

@ -39,6 +39,7 @@
<link rel="openid2.provider" href="<TMPL_VAR NAME="PROVIDERURI">" /> <link rel="openid2.provider" href="<TMPL_VAR NAME="PROVIDERURI">" />
</TMPL_IF> </TMPL_IF>
<TMPL_INCLUDE NAME="../common/script.tpl"> <TMPL_INCLUDE NAME="../common/script.tpl">
<TMPL_INCLUDE NAME="../common/trover.tpl">
<!-- //if:usedebianlibs <!-- //if:usedebianlibs
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX"><TMPL_VAR NAME="SKIN">/js/skin.min.js"></script> <script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX"><TMPL_VAR NAME="SKIN">/js/skin.min.js"></script>
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">common/js/portal.min.js"></script> <script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">common/js/portal.min.js"></script>

View File

@ -0,0 +1,3 @@
{
"trOver":{"all":{},"en":{"PE9":"You are welcome! Please login..."},"fr":{}}
}

View File

@ -0,0 +1,5 @@
<TMPL_IF NAME="TROVERbyJSON">
<script type="application/init">
<TMPL_VAR NAME="TROVERbyJSON">
</script>
</TMPL_IF>

View File

@ -1,3 +1,4 @@
error_en_5 = Big brother is watching you, authenticated user error_en_5 = Big brother is watching you, authenticated user
error_en_9 = Do you really want to login ?
error_fr_0 = Souriez, vous êtes surveillés ! error_fr_0 = Souriez, vous êtes surveillés !
msg_fr_selectIdP = Portail de Fédération des Identités msg_fr_selectIdP = Portail de Fédération des Identités

View File

@ -31,7 +31,7 @@ ok( $res->[2]->[0] =~ m%"trOver"%,
ok( $res->[2]->[0] =~ m%"all":\{\}%, ok( $res->[2]->[0] =~ m%"all":\{\}%,
' all found' ) ' all found' )
or print STDERR Dumper( $res->[2]->[0] ); or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"en":\{"PE5":"Big brother is watching you, authenticated user"\}%, ok( $res->[2]->[0] =~ m%"en":\{"PE9":"You are welcome! Please login..."\}%,
' en found' ) ' en found' )
or print STDERR Dumper( $res->[2]->[0] ); or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%"PE0":"Souriez, vous êtes surveillés !"%, ok( $res->[2]->[0] =~ m%"PE0":"Souriez, vous êtes surveillés !"%,
@ -48,6 +48,7 @@ ok( $res->[2]->[0] =~ m%"PE85":"From lemonlap-ng.ini"%,
or print STDERR Dumper( $res->[2]->[0] ); or print STDERR Dumper( $res->[2]->[0] );
count(9); count(9);
# Try yo authenticate # Try yo authenticate
# ------------------- # -------------------
ok( ok(

View File

@ -42,7 +42,10 @@ SKIP: {
else { else {
run( [ 'gpg', '--clearsign', '--homedir', 't/gpghome' ], run( [ 'gpg', '--clearsign', '--homedir', 't/gpghome' ],
\$token, \$out, \$err, IPC::Run::timeout(10) ); \$token, \$out, \$err, IPC::Run::timeout(10) );
ok( $? == 0, "Succeed to sign" ); unless ( $? == 0 ) {
skip "Local GPG signature fails, aborting", 2;
}
pass("Succeed to sign");
} }
$query .= '&' . build_urlencoded( password => $out ); $query .= '&' . build_urlencoded( password => $out );
ok( ok(

View File

@ -12,6 +12,7 @@ my $client = LLNG::Manager::Test->new( {
ini => { ini => {
logLevel => 'error', logLevel => 'error',
ext2fActivation => 1, ext2fActivation => 1,
ext2fCodeActivation => 0,
ext2FSendCommand => 't/sendOTP.pl -uid $uid', ext2FSendCommand => 't/sendOTP.pl -uid $uid',
ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code', ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code',
authentication => 'Demo', authentication => 'Demo',

View File

@ -0,0 +1,66 @@
use Test::More;
use strict;
use IO::String;
use Data::Dumper;
require 't/test-lib.pm';
use_ok('Lemonldap::NG::Common::FormEncode');
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',
}
}
);
my $res;
# Try to authenticate
# -------------------
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
count(1);
my ( $host, $url, $query ) =
expectForm( $res, undef, '/ext2fcheck', 'token', 'code' );
ok(
$res->[2]->[0] =~
qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
'Found EXTCODE input'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
$query =~ s/code=/code=A1b2C0/;
ok(
$res = $client->_post(
'/ext2fcheck',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Post code'
);
count(1);
my $id = expectCookie($res);
$client->logout($id);
clean_sessions();
done_testing( count() );

View File

@ -12,6 +12,7 @@ my $client = LLNG::Manager::Test->new( {
ini => { ini => {
logLevel => 'error', logLevel => 'error',
ext2fActivation => 1, ext2fActivation => 1,
ext2fCodeActivation => 0,
ext2FSendCommand => 't/sendOTP.pl -uid $uid', ext2FSendCommand => 't/sendOTP.pl -uid $uid',
ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code', ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code',
authentication => 'Demo', authentication => 'Demo',

View File

@ -19,6 +19,7 @@ my $client = LLNG::Manager::Test->new( {
rest2fLogo => 'u2f.png', rest2fLogo => 'u2f.png',
totp2fActivation => '$uid eq "dwho"', totp2fActivation => '$uid eq "dwho"',
ext2fActivation => 1, ext2fActivation => 1,
ext2fCodeActivation => 0,
ext2FSendCommand => 't/sendOTP.pl -uid $uid', ext2FSendCommand => 't/sendOTP.pl -uid $uid',
ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code', ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code',
ext2fLogo => 'yubikey.png', ext2fLogo => 'yubikey.png',

View File

@ -0,0 +1,7 @@
#!/usr/bin/perl
use strict;
use warnings;
my ( $swt1, $user, $swt2, $code ) = @ARGV;
exit !( $swt1 eq '-uid' && $user eq 'dwho' && $swt2 eq '-code' && defined $code );