diff --git a/lemonldap-ng-manager/MANIFEST b/lemonldap-ng-manager/MANIFEST
index 803590522..db002fdda 100644
--- a/lemonldap-ng-manager/MANIFEST
+++ b/lemonldap-ng-manager/MANIFEST
@@ -5,6 +5,7 @@ KINEMATIC.md
lib/Lemonldap/NG/Manager.pm
lib/Lemonldap/NG/Manager/Attributes.pm
lib/Lemonldap/NG/Manager/Conf.pm
+lib/Lemonldap/NG/Manager/Conf/Tests.pm
lib/Lemonldap/NG/Manager/ConfParser.pm
lib/Lemonldap/NG/Manager/Constants.pm
lib/Lemonldap/NG/Manager/CTrees.pm
diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm
new file mode 100644
index 000000000..5179bcb26
--- /dev/null
+++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm
@@ -0,0 +1,315 @@
+package Lemonldap::NG::Manager::Conf::Tests;
+
+## @method hashref tests(hashref conf)
+# Return a hash ref where keys are the names of the tests and values
+# subroutines to execute.
+#
+# Subroutines can return one of the followings :
+# - (1) : everything is OK
+# - (1,message) : OK with a warning
+# - (0,message) : NOK
+# - (-1,message) : OK, but must be confirmed (ignored if confirm parameter is
+# set
+#
+# Those subroutines can also modify configuration.
+#
+# @param $conf Configuration to test
+# @return hash ref where keys are the names of the tests and values
+sub tests {
+ my $conf = shift;
+ return {
+
+ # 1. CHECKS
+
+ # Check if portal is in domain
+ portalIsInDomain => sub {
+ return (
+ 1,
+ (
+ index( $conf->{portal}, $conf->{domain} ) > 0
+ ? ''
+ : "Portal seems not to be in the domain $conf->{domain}"
+ )
+ );
+ },
+
+ # Check if virtual hosts are in the domain
+ vhostInDomainOrCDA => sub {
+ return 1 if ( $conf->{cda} );
+ my @pb;
+ foreach my $vh ( keys %{ $conf->{locationRules} } ) {
+ push @pb, $vh unless ( index( $vh, $conf->{domain} ) >= 0 );
+ }
+ return (
+ 1,
+ (
+ @pb
+ ? 'Virtual hosts '
+ . join( ', ', @pb )
+ . " are not in $conf->{domain} and cross-domain-authentication is not set"
+ : undef
+ )
+ );
+ },
+
+ # Check if virtual host do not contain a port
+ vhostWithPort => sub {
+ my @pb;
+ foreach my $vh ( keys %{ $conf->{locationRules} } ) {
+ push @pb, $vh if ( $vh =~ /:/ );
+ }
+ if (@pb) {
+ return ( 0,
+ 'Virtual hosts '
+ . join( ', ', @pb )
+ . " contain a port, this is not allowed" );
+ }
+ else { return 1; }
+ },
+
+ # Force vhost to be lowercase
+ vhostUpperCase => sub {
+ my @pb;
+ foreach my $vh ( keys %{ $conf->{locationRules} } ) {
+ push @pb, $vh if ( $vh ne lc $vh );
+ }
+ if (@pb) {
+ return ( 0,
+ 'Virtual hosts '
+ . join( ', ', @pb )
+ . " must be in lower case" );
+ }
+ else { return 1; }
+ },
+
+ # Check if "userDB" and "authentication" are consistent
+ authAndUserDBConsistency => sub {
+ foreach my $type (qw(Facebook Google OpenID SAML WebID)) {
+ return ( 0,
+"\"$type\" can not be used as user database without using \"$type\" for authentication"
+ )
+ if ( $conf->{userDB} =~ /$type/
+ and $conf->{authentication} !~ /$type/ );
+ }
+ return 1;
+ },
+
+ # Check that OpenID macros exists
+ checkAttrAndMacros => sub {
+ my @tmp;
+ foreach my $k ( keys %$conf ) {
+ 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 (
+ defined(
+ $conf->{exportedVars}->{$v}
+ or defined( $conf->{macros}->{$v} )
+ )
+ );
+ }
+ }
+ return (
+ 1,
+ (
+ @tmp
+ ? 'Values of parameter(s) "'
+ . join( ', ', @tmp )
+ . '" are not defined in exported attributes or macros'
+ : ''
+ )
+ );
+ },
+
+ # Test that variables are exported if Google is used as UserDB
+ checkUserDBGoogleAXParams => sub {
+ my @tmp;
+ if ( $conf->{userDB} =~ /^Google/ ) {
+ while ( my ( $k, $v ) = each %{ $conf->{exportedVars} } ) {
+ if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() ) {
+ push @tmp, $v;
+ }
+ }
+ }
+ return (
+ 1,
+ (
+ @tmp
+ ? 'Values of parameter(s) "'
+ . join( ', ', @tmp )
+ . '" are not exported by Google'
+ : ''
+ )
+ );
+ },
+
+ # Test that variables are exported if OpenID is used as UserDB
+ checkUserDBOpenIDParams => sub {
+ my @tmp;
+ if ( $conf->{userDB} =~ /^OpenID/ ) {
+ while ( my ( $k, $v ) = each %{ $conf->{exportedVars} } ) {
+ if ( $v !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
+ {
+ push @tmp, $v;
+ }
+ }
+ }
+ return (
+ 1,
+ (
+ @tmp
+ ? 'Values of parameter(s) "'
+ . join( ', ', @tmp )
+ . '" are not exported by OpenID SREG'
+ : ''
+ )
+ );
+ },
+
+ # Try to use Apache::Session module
+ testApacheSession => sub {
+ my ( $id, %h );
+ return 1
+ if ( $Lemonldap::NG::Handler::_CGI::tsv->{globalStorage} eq
+ $conf->{globalStorage}
+ or $conf->{globalStorage} eq
+ 'Lemonldap::NG::Common::Apache::Session::SOAP' );
+ eval "use $conf->{globalStorage}";
+ return ( -1, "Unknown package $conf->{globalStorage}" ) if ($@);
+ eval {
+ tie %h, $conf->{globalStorage}, undef,
+ $conf->{globalStorageOptions};
+ };
+ return ( -1, "Unable to create a session ($@)" )
+ if ( $@ or not tied(%h) );
+ eval {
+ $h{a} = 1;
+ $id = $h{_session_id} or return ( -1, 'No _session_id' );
+ untie(%h);
+ tie %h, $conf->{globalStorage}, $id,
+ $conf->{globalStorageOptions};
+ };
+ return ( -1, "Unable to insert datas ($@)" ) if ($@);
+ return ( -1, "Unable to recover data stored" )
+ unless ( $h{a} == 1 );
+ eval { tied(%h)->delete; };
+ return ( -1, "Unable to delete session ($@)" ) if ($@);
+ my $gc = $Lemonldap::NG::Handler::_CGI::tsv->{globalStorage};
+ return ( -1,
+'All sessions may be lost and you must restart all your Apache servers'
+ ) if ( $conf->{globalStorage} ne $gc );
+ return 1;
+ },
+
+ # Warn if cookie name has changed
+ cookieNameChanged => sub {
+ return (
+ 1,
+ (
+ $Lemonldap::NG::Handler::_CGI::tsv->{cookieName} ne
+ $conf->{cookieName}
+ ? 'Cookie name has changed, you must restart all your Apache servers'
+ : ()
+ )
+ );
+ },
+
+ # Warn if manager seems to be unprotected
+ managerProtection => sub {
+ return (
+ 1,
+ (
+ $conf->{cfgAuthor} eq 'anonymous'
+ ? 'Your manager seems to be unprotected'
+ : ''
+ )
+ );
+ },
+
+ # Test SMTP connection and authentication
+ smtpConnectionAuthentication => sub {
+
+ # Skip test if no SMTP configuration
+ return 1 unless ( $conf->{SMTPServer} );
+
+ # Use SMTP
+ eval "use Net::SMTP";
+ return ( 0, "Net::SMTP module is required to use SMTP server" )
+ if ($@);
+
+ # Create SMTP object
+ my $smtp = Net::SMTP->new( $conf->{SMTPServer} );
+ return ( 0,
+ "SMTP connection to " . $conf->{SMTPServer} . " failed" )
+ unless ($smtp);
+
+ # Skip other tests if no authentication
+ return 1
+ unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
+
+ # Try authentication
+ return ( 0, "SMTP authentication failed" )
+ unless $smtp->auth( $conf->{SMTPAuthUser},
+ $conf->{SMTPAuthPass} );
+
+ # Return
+ return 1;
+ },
+
+ # 2. MODIFICATIONS
+
+ # Remove unused and non-customized parameters
+ compactModules => sub {
+ foreach my $k ( keys %$conf ) {
+
+ # No analysis for hash keys
+ next if ( ref $conf->{$k} );
+
+ # Check federation modules
+ foreach my $type (qw(CAS OpenID SAML)) {
+
+ # Check authChoice values
+ my ( $authChoice, $userDBChoice ) = ( undef, undef );
+ if ( $conf->{authentication} eq 'Choice'
+ and defined $conf->{authChoiceModules} )
+ {
+ foreach ( keys %{ $conf->{authChoiceModules} } ) {
+ my ( $auth, $userDB, $passwordDB ) =
+ split( '|', $conf->{authChoiceModules}->{$_} );
+ $authChoice = 1 if $auth =~ /$type/i;
+ $userDBChoice = 1 if $userDB =~ /$type/i;
+ }
+ }
+
+ if (
+ (
+ $k =~ /^$type/i
+ and not( $conf->{"issuerDB${type}Activation"} )
+ and not( $conf->{authentication} =~ /$type/i )
+ and not( $conf->{userDB} =~ /$type/i )
+ and not( defined $authChoice
+ or defined $userDBChoice )
+ )
+ )
+ {
+ my $confAttributes =
+ Lemonldap::NG::Common::Conf::Attributes->new();
+ my $v = $confAttributes->$k;
+ if ( defined($v) and $conf->{$k} eq $v ) {
+ delete $conf->{$k};
+ }
+ }
+ }
+ return 1;
+ }
+ },
+ };
+}
+
+1;