diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm index 28d24adc9..921c6e34d 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm @@ -30,8 +30,7 @@ sub tests { portalIsInDomain => sub { return ( 1, - ( - index( $conf->{portal}, $conf->{domain} ) > 0 + ( index( $conf->{portal}, $conf->{domain} ) > 0 ? '' : "Portal seems not to be in the domain $conf->{domain}" ) @@ -43,7 +42,7 @@ sub tests { # Checking for ending slash $conf->{portal} .= '/' - unless ( $conf->{portal} =~ qr#/$# ); + unless ( $conf->{portal} =~ qr#/$# ); # Deleting trailing ending slash my $regex = qr#/+$#; @@ -61,11 +60,10 @@ sub tests { } return ( 1, - ( - @pb + ( @pb ? 'Virtual hosts ' - . join( ', ', @pb ) - . " are not in $conf->{domain} and cross-domain-authentication is not set" + . join( ', ', @pb ) + . " are not in $conf->{domain} and cross-domain-authentication is not set" : undef ) ); @@ -79,9 +77,9 @@ sub tests { } if (@pb) { return ( 0, - 'Virtual hosts ' - . join( ', ', @pb ) - . " contain a port, this is not allowed" ); + 'Virtual hosts ' + . join( ', ', @pb ) + . " contain a port, this is not allowed" ); } else { return 1; } }, @@ -94,9 +92,9 @@ sub tests { } if (@pb) { return ( 0, - 'Virtual hosts ' - . join( ', ', @pb ) - . " must be in lower case" ); + 'Virtual hosts ' + . join( ', ', @pb ) + . " must be in lower case" ); } else { return 1; } }, @@ -104,12 +102,12 @@ sub tests { # Check if "userDB" and "authentication" are consistent authAndUserDBConsistency => sub { foreach - my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID)) + my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID)) { return ( 0, -"\"$type\" can not be used as user database without using \"$type\" for authentication" - ) - if ( $conf->{userDB} =~ /$type/ + "\"$type\" can not be used as user database without using \"$type\" for authentication" + ) + if ($conf->{userDB} =~ /$type/ and $conf->{authentication} !~ /$type/ ); } return 1; @@ -119,30 +117,29 @@ sub tests { checkAttrAndMacros => sub { my @tmp; foreach my $k ( keys %$conf ) { - if ( $k =~ -/^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/ - ) + if ( $k + =~ /^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/ + ) { my $v = $conf->{$k}; $v =~ s/^$//; next if ( $v =~ /^_/ ); push @tmp, - $k - unless ( + $k + unless ( defined( $conf->{exportedVars}->{$v} - or defined( $conf->{macros}->{$v} ) + or defined( $conf->{macros}->{$v} ) ) - ); + ); } } return ( 1, - ( - @tmp + ( @tmp ? 'Values of parameter(s) "' - . join( ', ', @tmp ) - . '" are not defined in exported attributes or macros' + . join( ', ', @tmp ) + . '" are not defined in exported attributes or macros' : '' ) ); @@ -154,18 +151,18 @@ sub tests { if ( $conf->{userDB} =~ /^Google$/ ) { foreach my $k ( keys %{ $conf->{exportedVars} } ) { my $v = $conf->{exportedVars}->{$k}; - if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() ) { + if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() ) + { push @tmp, $v; } } } return ( 1, - ( - @tmp + ( @tmp ? 'Values of parameter(s) "' - . join( ', ', @tmp ) - . '" are not exported by Google' + . join( ', ', @tmp ) + . '" are not exported by Google' : '' ) ); @@ -177,7 +174,8 @@ sub tests { if ( $conf->{userDB} =~ /^OpenID$/ ) { foreach my $k ( keys %{ $conf->{exportedVars} } ) { my $v = $conf->{exportedVars}->{$k}; - if ( $v !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() ) + if ( $v + !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() ) { push @tmp, $v; } @@ -185,11 +183,10 @@ sub tests { } return ( 1, - ( - @tmp + ( @tmp ? 'Values of parameter(s) "' - . join( ', ', @tmp ) - . '" are not exported by OpenID SREG' + . join( ', ', @tmp ) + . '" are not exported by OpenID SREG' : '' ) ); @@ -198,40 +195,40 @@ sub tests { # Try to use Apache::Session module testApacheSession => sub { my ( $id, %h ); - my $gc = - $Lemonldap::NG::Handler::PSGI::Main::tsv->{sessionStorageModule}; + my $gc = $Lemonldap::NG::Handler::PSGI::Main::tsv + ->{sessionStorageModule}; return 1 - if ( ( $gc and $gc eq $conf->{globalStorage} ) - or $conf->{globalStorage} =~ - /^Lemonldap::NG::Common::Apache::Session::/ ); + if ( ( $gc and $gc eq $conf->{globalStorage} ) + or $conf->{globalStorage} + =~ /^Lemonldap::NG::Common::Apache::Session::/ ); eval "use $conf->{globalStorage}"; return ( -1, "Unknown package $conf->{globalStorage}" ) if ($@); eval { tie %h, 'Lemonldap::NG::Common::Apache::Session', undef, - { + { %{ $conf->{globalStorageOptions} }, backend => $conf->{globalStorage} - }; + }; }; return ( -1, "Unable to create a session ($@)" ) - if ( $@ or not tied(%h) ); + if ( $@ or not tied(%h) ); eval { $h{a} = 1; $id = $h{_session_id} or return ( -1, 'No _session_id' ); untie(%h); tie %h, 'Lemonldap::NG::Common::Apache::Session', $id, - { + { %{ $conf->{globalStorageOptions} }, backend => $conf->{globalStorage} - }; + }; }; return ( -1, "Unable to insert data ($@)" ) if ($@); return ( -1, "Unable to recover data stored" ) - unless ( $h{a} == 1 ); + unless ( $h{a} == 1 ); eval { tied(%h)->delete; }; return ( -1, "Unable to delete session ($@)" ) if ($@); return ( -1, -'All sessions may be lost and you must restart all your Apache servers' + 'All sessions may be lost and you must restart all your Apache servers' ) if ( $gc and $conf->{globalStorage} ne $gc ); return 1; }, @@ -241,9 +238,8 @@ sub tests { my $cn = $Lemonldap::NG::Handler::PSGI::API::tsv->{cookieName}; return ( 1, - ( - $cn - and $cn ne $conf->{cookieName} + ( $cn + and $cn ne $conf->{cookieName} ? 'Cookie name has changed, you must restart all your web servers' : () ) @@ -254,9 +250,9 @@ sub tests { cookieTTL => sub { return 1 unless ( defined $conf->{cookieExpiration} ); return ( 0, "Cookie TTL must be higher than one minute" ) - unless ( $conf->{cookieExpiration} > 60 ); + unless ( $conf->{cookieExpiration} > 60 ); return ( 1, "Cookie TTL should be higher or equal than one hour" ) - unless ( $conf->{cookieExpiration} >= 3600 + unless ( $conf->{cookieExpiration} >= 3600 || $conf->{cookieExpiration} == 0 ); # Return @@ -267,8 +263,7 @@ sub tests { managerProtection => sub { return ( 1, - ( - $conf->{cfgAuthor} eq 'anonymous' + ( $conf->{cfgAuthor} eq 'anonymous' ? 'Your manager seems to be unprotected' : '' ) @@ -284,21 +279,21 @@ sub tests { # Use SMTP eval "use Net::SMTP"; return ( 1, "Net::SMTP module is required to use SMTP server" ) - if ($@); + if ($@); # Create SMTP object my $smtp = Net::SMTP->new( $conf->{SMTPServer}, Timeout => 5 ); return ( 1, "SMTP connection to " . $conf->{SMTPServer} . " failed" ) - unless ($smtp); + unless ($smtp); # Skip other tests if no authentication return 1 - unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} ); + unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} ); # Try authentication return ( 1, "SMTP authentication failed" ) - unless $smtp->auth( $conf->{SMTPAuthUser}, + unless $smtp->auth( $conf->{SMTPAuthUser}, $conf->{SMTPAuthPass} ); # Return @@ -308,15 +303,14 @@ sub tests { # SAML entity ID must be uniq samlIDPEntityIdUniqueness => sub { return 1 - unless ( $conf->{samlIDPMetaDataXML} + unless ( $conf->{samlIDPMetaDataXML} and %{ $conf->{samlIDPMetaDataXML} } ); my @msg; my $res = 1; my %entityIds; foreach my $idpId ( keys %{ $conf->{samlIDPMetaDataXML} } ) { - unless ( - $conf->{samlIDPMetaDataXML}->{$idpId}->{samlIDPMetaDataXML} - =~ /entityID=(['"])(.+?)\1/si ) + unless ( $conf->{samlIDPMetaDataXML}->{$idpId} + ->{samlIDPMetaDataXML} =~ /entityID=(['"])(.+?)\1/si ) { push @msg, "$idpId SAML metadata has no EntityID"; $res = 0; @@ -325,7 +319,7 @@ sub tests { my $eid = $2; if ( defined $entityIds{$eid} ) { push @msg, - "$idpId and $entityIds{$eid} have the same SAML EntityID"; + "$idpId and $entityIds{$eid} have the same SAML EntityID"; $res = 0; next; } @@ -335,15 +329,15 @@ sub tests { }, samlSPEntityIdUniqueness => sub { return 1 - unless ( $conf->{samlSPMetaDataXML} + unless ( $conf->{samlSPMetaDataXML} and %{ $conf->{samlSPMetaDataXML} } ); my @msg; my $res = 1; my %entityIds; foreach my $spId ( keys %{ $conf->{samlSPMetaDataXML} } ) { unless ( - $conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML} =~ - /entityID=(['"])(.+?)\1/si ) + $conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML} + =~ /entityID=(['"])(.+?)\1/si ) { push @msg, "$spId SAML metadata has no EntityID"; $res = 0; @@ -352,7 +346,7 @@ sub tests { my $eid = $2; if ( defined $entityIds{$eid} ) { push @msg, - "$spId and $entityIds{$eid} have the same SAML EntityID"; + "$spId and $entityIds{$eid} have the same SAML EntityID"; $res = 0; next; } @@ -366,7 +360,7 @@ sub tests { return 1 unless ( $conf->{authentication} eq 'Combination' ); require Lemonldap::NG::Common::Combination::Parser; return ( 0, 'No module declared for combination' ) - unless ( $conf->{combModules} and %{ $conf->{combModules} } ); + unless ( $conf->{combModules} and %{ $conf->{combModules} } ); my $moduleList; foreach my $md ( keys %{ $conf->{combModules} } ) { my $entry = $conf->{combModules}->{$md}; @@ -377,8 +371,8 @@ sub tests { ); } eval { - Lemonldap::NG::Common::Combination::Parser->parse( $moduleList, - $conf->{combination} ); + Lemonldap::NG::Common::Combination::Parser->parse( + $moduleList, $conf->{combination} ); }; return ( 0, $@ ) if ($@); @@ -403,7 +397,7 @@ sub tests { eval "use Convert::Base32"; return ( 1, "Convert::Base32 module is required to enable TOTP" ) - if ($@); + if ($@); } # Use U2F @@ -412,7 +406,7 @@ sub tests { { eval "use Crypt::U2F::Server::Simple"; return ( 1, -"Crypt::U2F::Server::Simple module is required to enable U2F" + "Crypt::U2F::Server::Simple module is required to enable U2F" ) if ($@); } @@ -420,7 +414,7 @@ sub tests { if ( $conf->{yubikey2fActivation} ) { eval "use Auth::Yubikey_WebClient"; return ( 1, -"Auth::Yubikey_WebClient module is required to enable Yubikey" + "Auth::Yubikey_WebClient module is required to enable Yubikey" ) if ($@); } @@ -434,7 +428,7 @@ sub tests { my $w = ""; foreach ( 'totp', 'u' ) { $w .= uc($_) . "2F is activated twice \n" - if ( $conf->{ $_ . '2fActivation' } eq '1' ); + if ( $conf->{ $_ . '2fActivation' } eq '1' ); } return ( 1, ( $w ? $w : () ) ); }, @@ -445,10 +439,8 @@ sub tests { return 1 unless ( defined $conf->{totp2fDigits} ); return ( 1, - ( - ( - $conf->{totp2fDigits} == 6 - or $conf->{totp2fDigits} == 8 + ( ( $conf->{totp2fDigits} == 6 + or $conf->{totp2fDigits} == 8 ) ? '' : 'TOTP should be 6 or 8 digits long' @@ -460,9 +452,9 @@ sub tests { totp2fParams => sub { return 1 unless ( $conf->{totp2fActivation} ); return ( 0, 'TOTP range must be defined' ) - unless ( $conf->{totp2fRange} ); + unless ( $conf->{totp2fRange} ); return ( 1, "TOTP interval should be higher than 10s" ) - unless ( $conf->{totp2fInterval} > 10 ); + unless ( $conf->{totp2fInterval} > 10 ); # Return return 1; @@ -473,12 +465,11 @@ sub tests { yubikey2fParams => sub { return 1 unless ( $conf->{yubikey2fActivation} ); return ( 0, "Yubikey client ID and secret key must be set" ) - unless ( defined $conf->{yubikey2fSecretKey} + unless ( defined $conf->{yubikey2fSecretKey} && defined $conf->{yubikey2fClientID} ); return ( 1, - ( - ( $conf->{yubikey2fPublicIDSize} == 12 ) + ( ( $conf->{yubikey2fPublicIDSize} == 12 ) ? '' : 'Yubikey public ID size should be 12 digits long' ) @@ -489,7 +480,7 @@ sub tests { rest2fVerifyUrl => sub { return 1 unless ( $conf->{rest2fActivation} ); return ( 0, "REST 2F Verify URL must be set" ) - unless ( defined $conf->{rest2fVerifyUrl} ); + unless ( defined $conf->{rest2fVerifyUrl} ); # Return return 1; @@ -503,15 +494,16 @@ sub tests { my $ok = 0; foreach (qw(u totp yubikey)) { $ok ||= $conf->{ $_ . '2fActivation' } - && $conf->{ $_ . '2fSelfRegistration' }; + && $conf->{ $_ . '2fSelfRegistration' }; last if ($ok); } $ok ||= $conf->{'utotp2fActivation'} - && ( $conf->{'u2fSelfRegistration'} + && ( $conf->{'u2fSelfRegistration'} || $conf->{'totp2fSelfRegistration'} ); - $msg = "A self registrable module should be enabled to require 2FA" - unless ($ok); + $msg + = "A self registrable module should be enabled to require 2FA" + unless ($ok); return ( 1, $msg ); }, @@ -520,7 +512,7 @@ sub tests { ext2fCommands => sub { return 1 unless ( $conf->{ext2fActivation} ); return ( 0, "External 2F Send or Validate command must be set" ) - unless ( defined $conf->{ext2FSendCommand} + unless ( defined $conf->{ext2FSendCommand} && defined $conf->{ext2FValidateCommand} ); # Return @@ -531,9 +523,9 @@ sub tests { formTimeout => sub { return 1 unless ( defined $conf->{formTimeout} ); return ( 0, "XSRF form token TTL must be higher than 30s" ) - unless ( $conf->{formTimeout} > 30 ); + unless ( $conf->{formTimeout} > 30 ); return ( 1, "XSRF form token TTL should not be higher than 2mn" ) - if ( $conf->{formTimeout} > 120 ); + if ( $conf->{formTimeout} > 120 ); # Return return 1; @@ -543,8 +535,11 @@ sub tests { bruteForceProtection => sub { return 1 unless ( $conf->{bruteForceProtection} ); return ( 1, - '"History" plugin is required for "BruteForceProtection" plugin' + '"History" plugin is required to enable "BruteForceProtection" plugin' ) unless ( $conf->{loginHistoryEnabled} ); + return ( 1, + 'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin' + ) unless ( $conf->{failedLoginNumber} > 2 ); # Return return 1;