Tests.pm 21.3 KB
Newer Older
Xavier Guimard's avatar
Xavier Guimard committed
1 2
package Lemonldap::NG::Manager::Conf::Tests;

Xavier Guimard's avatar
Xavier Guimard committed
3
use utf8;
4 5
use Lemonldap::NG::Common::Regexp;

Xavier Guimard's avatar
Xavier Guimard committed
6
our $VERSION = '2.0.2';
Xavier Guimard's avatar
Xavier Guimard committed
7

Xavier Guimard's avatar
Xavier Guimard committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
## @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,
33
                (   index( $conf->{portal}, $conf->{domain} ) > 0
Xavier Guimard's avatar
Xavier Guimard committed
34 35 36 37 38 39
                    ? ''
                    : "Portal seems not to be in the domain $conf->{domain}"
                )
            );
        },

40 41
        # Check if portal URL is well formated
        portalURL => sub {
42 43

            # Checking for ending slash
44
            $conf->{portal} .= '/'
45
                unless ( $conf->{portal} =~ qr#/$# );
46 47 48 49 50

            # Deleting trailing ending slash
            my $regex = qr#/+$#;
            $conf->{portal} =~ s/$regex/\//;

51 52 53
            return 1;
        },

Xavier Guimard's avatar
Xavier Guimard committed
54 55 56 57 58 59 60 61 62
        # 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,
63
                (   @pb
Xavier Guimard's avatar
Xavier Guimard committed
64
                    ? 'Virtual hosts '
65 66
                        . join( ', ', @pb )
                        . " are not in $conf->{domain} and cross-domain-authentication is not set"
Xavier Guimard's avatar
Xavier Guimard committed
67 68 69 70 71 72 73 74 75 76 77 78 79
                    : 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,
80 81 82
                          'Virtual hosts '
                        . join( ', ', @pb )
                        . " contain a port, this is not allowed" );
Xavier Guimard's avatar
Xavier Guimard committed
83 84 85 86 87 88 89 90 91 92 93 94
            }
            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,
95 96 97
                          'Virtual hosts '
                        . join( ', ', @pb )
                        . " must be in lower case" );
Xavier Guimard's avatar
Xavier Guimard committed
98 99 100 101 102 103
            }
            else { return 1; }
        },

        # Check if "userDB" and "authentication" are consistent
        authAndUserDBConsistency => sub {
104
            foreach
105
                my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID))
106
            {
Xavier Guimard's avatar
Xavier Guimard committed
107
                return ( 0,
108 109 110
                    "\"$type\" can not be used as user database without using \"$type\" for authentication"
                    )
                    if ($conf->{userDB} =~ /$type/
Xavier Guimard's avatar
Xavier Guimard committed
111 112 113 114 115 116 117 118 119
                    and $conf->{authentication} !~ /$type/ );
            }
            return 1;
        },

        # Check that OpenID macros exists
        checkAttrAndMacros => sub {
            my @tmp;
            foreach my $k ( keys %$conf ) {
120 121 122
                if ( $k
                    =~ /^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/
                    )
Xavier Guimard's avatar
Xavier Guimard committed
123 124 125 126 127
                {
                    my $v = $conf->{$k};
                    $v =~ s/^$//;
                    next if ( $v =~ /^_/ );
                    push @tmp,
128 129
                        $k
                        unless (
Xavier Guimard's avatar
Xavier Guimard committed
130 131
                        defined(
                            $conf->{exportedVars}->{$v}
132
                                or defined( $conf->{macros}->{$v} )
Xavier Guimard's avatar
Xavier Guimard committed
133
                        )
134
                        );
Xavier Guimard's avatar
Xavier Guimard committed
135 136 137 138
                }
            }
            return (
                1,
139
                (   @tmp
Xavier Guimard's avatar
Xavier Guimard committed
140
                    ? 'Values of parameter(s) "'
141 142
                        . join( ', ', @tmp )
                        . '" are not defined in exported attributes or macros'
Xavier Guimard's avatar
Xavier Guimard committed
143 144 145 146 147 148 149 150
                    : ''
                )
            );
        },

        # Test that variables are exported if Google is used as UserDB
        checkUserDBGoogleAXParams => sub {
            my @tmp;
151
            if ( $conf->{userDB} =~ /^Google$/ ) {
152 153
                foreach my $k ( keys %{ $conf->{exportedVars} } ) {
                    my $v = $conf->{exportedVars}->{$k};
154 155
                    if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() )
                    {
Xavier Guimard's avatar
Xavier Guimard committed
156 157 158 159 160 161
                        push @tmp, $v;
                    }
                }
            }
            return (
                1,
162
                (   @tmp
Xavier Guimard's avatar
Xavier Guimard committed
163
                    ? 'Values of parameter(s) "'
164 165
                        . join( ', ', @tmp )
                        . '" are not exported by Google'
Xavier Guimard's avatar
Xavier Guimard committed
166 167 168 169 170 171 172 173
                    : ''
                )
            );
        },

        # Test that variables are exported if OpenID is used as UserDB
        checkUserDBOpenIDParams => sub {
            my @tmp;
174
            if ( $conf->{userDB} =~ /^OpenID$/ ) {
175 176
                foreach my $k ( keys %{ $conf->{exportedVars} } ) {
                    my $v = $conf->{exportedVars}->{$k};
177 178
                    if ( $v
                        !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
Xavier Guimard's avatar
Xavier Guimard committed
179 180 181 182 183 184 185
                    {
                        push @tmp, $v;
                    }
                }
            }
            return (
                1,
186
                (   @tmp
Xavier Guimard's avatar
Xavier Guimard committed
187
                    ? 'Values of parameter(s) "'
188 189
                        . join( ', ', @tmp )
                        . '" are not exported by OpenID SREG'
Xavier Guimard's avatar
Xavier Guimard committed
190 191 192 193 194 195 196 197
                    : ''
                )
            );
        },

        # Try to use Apache::Session module
        testApacheSession => sub {
            my ( $id, %h );
198 199
            my $gc = $Lemonldap::NG::Handler::PSGI::Main::tsv
                ->{sessionStorageModule};
Xavier Guimard's avatar
Xavier Guimard committed
200
            return 1
201 202 203
                if ( ( $gc and $gc eq $conf->{globalStorage} )
                or $conf->{globalStorage}
                =~ /^Lemonldap::NG::Common::Apache::Session::/ );
Xavier Guimard's avatar
Xavier Guimard committed
204 205 206
            eval "use $conf->{globalStorage}";
            return ( -1, "Unknown package $conf->{globalStorage}" ) if ($@);
            eval {
Xavier Guimard's avatar
Xavier Guimard committed
207
                tie %h, 'Lemonldap::NG::Common::Apache::Session', undef,
208
                    {
Xavier Guimard's avatar
Xavier Guimard committed
209 210
                    %{ $conf->{globalStorageOptions} },
                    backend => $conf->{globalStorage}
211
                    };
Xavier Guimard's avatar
Xavier Guimard committed
212 213
            };
            return ( -1, "Unable to create a session ($@)" )
214
                if ( $@ or not tied(%h) );
Xavier Guimard's avatar
Xavier Guimard committed
215 216 217 218
            eval {
                $h{a} = 1;
                $id = $h{_session_id} or return ( -1, 'No _session_id' );
                untie(%h);
Xavier Guimard's avatar
Xavier Guimard committed
219
                tie %h, 'Lemonldap::NG::Common::Apache::Session', $id,
220
                    {
Xavier Guimard's avatar
Xavier Guimard committed
221 222
                    %{ $conf->{globalStorageOptions} },
                    backend => $conf->{globalStorage}
223
                    };
Xavier Guimard's avatar
Xavier Guimard committed
224
            };
Xavier Guimard's avatar
Xavier Guimard committed
225
            return ( -1, "Unable to insert data ($@)" ) if ($@);
Xavier Guimard's avatar
Xavier Guimard committed
226
            return ( -1, "Unable to recover data stored" )
227
                unless ( $h{a} == 1 );
Xavier Guimard's avatar
Xavier Guimard committed
228 229 230
            eval { tied(%h)->delete; };
            return ( -1, "Unable to delete session ($@)" ) if ($@);
            return ( -1,
231
                'All sessions may be lost and you must restart all your Apache servers'
Xavier Guimard's avatar
Xavier Guimard committed
232
            ) if ( $gc and $conf->{globalStorage} ne $gc );
Xavier Guimard's avatar
Xavier Guimard committed
233 234 235 236 237
            return 1;
        },

        # Warn if cookie name has changed
        cookieNameChanged => sub {
238
            my $cn = $Lemonldap::NG::Handler::PSGI::API::tsv->{cookieName};
Xavier Guimard's avatar
Xavier Guimard committed
239 240
            return (
                1,
241 242
                (   $cn
                        and $cn ne $conf->{cookieName}
243
                    ? 'Cookie name has changed, you must restart all your web servers'
Xavier Guimard's avatar
Xavier Guimard committed
244 245 246 247
                    : ()
                )
            );
        },
Christophe Maudoux's avatar
Christophe Maudoux committed
248

249
        # Warn if cookie TTL is equal or lower than one hour
250 251
        cookieTTL => sub {
            return 1 unless ( defined $conf->{cookieExpiration} );
252
            return ( 0, "Cookie TTL must be higher than one minute" )
253
                unless ( $conf->{cookieExpiration} > 60 );
254
            return ( 1, "Cookie TTL should be higher or equal than one hour" )
255
                unless ( $conf->{cookieExpiration} >= 3600
256
                || $conf->{cookieExpiration} == 0 );
Christophe Maudoux's avatar
Christophe Maudoux committed
257

258 259 260
            # Return
            return 1;
        },
Xavier Guimard's avatar
Xavier Guimard committed
261

262
        # Warn if session timeout is lower than 10 minutes
263
        sessionTimeout => sub {
264
            return 1 unless ( defined $conf->{timeout} );
265
            return ( -1, "Session timeout should be higher than ten minutes" )
266
                unless ( $conf->{timeout} > 600
267 268 269 270 271 272 273
                || $conf->{timeout} == 0 );

            # Return
            return 1;
        },

        # Error if session Activity Timeout is equal or lower than one minute
274
        sessionTimeoutActivity => sub {
275 276
            return 1 unless ( defined $conf->{timeoutActivity} );
            return ( 0,
277 278 279
                "Session activity timeout must be higher or equal than one minute"
                )
                unless ( $conf->{timeoutActivity} > 59
280 281 282 283 284 285 286 287 288 289
                || $conf->{timeoutActivity} == 0 );

            # Return
            return 1;
        },

        # Error if session Activity Timeout is equal or lower than one minute
        timeoutActivityInterval => sub {
            return 1 unless ( defined $conf->{timeoutActivityInterval} );
            return ( 0,
290 291 292 293 294
                "Activity timeout interval must be lower than session activity timeout"
                )
                if ($conf->{timeoutActivity}
                and $conf->{timeoutActivity}
                <= $conf->{timeoutActivityInterval} );
295 296 297 298 299

            # Return
            return 1;
        },

Xavier Guimard's avatar
Xavier Guimard committed
300 301 302 303
        # Warn if manager seems to be unprotected
        managerProtection => sub {
            return (
                1,
304
                (   $conf->{cfgAuthor} eq 'anonymous'
Xavier Guimard's avatar
Xavier Guimard committed
305 306 307 308 309 310
                    ? 'Your manager seems to be unprotected'
                    : ''
                )
            );
        },

311
        # Test SMTP connection and authentication (warning only)
Xavier Guimard's avatar
Xavier Guimard committed
312 313 314 315 316 317 318
        smtpConnectionAuthentication => sub {

            # Skip test if no SMTP configuration
            return 1 unless ( $conf->{SMTPServer} );

            # Use SMTP
            eval "use Net::SMTP";
319
            return ( 1, "Net::SMTP module is required to use SMTP server" )
320
                if ($@);
Xavier Guimard's avatar
Xavier Guimard committed
321 322

            # Create SMTP object
323
            my $smtp = Net::SMTP->new( $conf->{SMTPServer}, Timeout => 5 );
324
            return ( 1,
Xavier Guimard's avatar
Xavier Guimard committed
325
                "SMTP connection to " . $conf->{SMTPServer} . " failed" )
326
                unless ($smtp);
Xavier Guimard's avatar
Xavier Guimard committed
327 328 329

            # Skip other tests if no authentication
            return 1
330
                unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
Xavier Guimard's avatar
Xavier Guimard committed
331 332

            # Try authentication
333
            return ( 1, "SMTP authentication failed" )
334
                unless $smtp->auth( $conf->{SMTPAuthUser},
Xavier Guimard's avatar
Xavier Guimard committed
335 336 337 338 339
                $conf->{SMTPAuthPass} );

            # Return
            return 1;
        },
340

341
        # SAML entity ID must be uniq
342 343
        samlIDPEntityIdUniqueness => sub {
            return 1
344
                unless ( $conf->{samlIDPMetaDataXML}
345 346 347 348 349
                and %{ $conf->{samlIDPMetaDataXML} } );
            my @msg;
            my $res = 1;
            my %entityIds;
            foreach my $idpId ( keys %{ $conf->{samlIDPMetaDataXML} } ) {
350 351
                unless ( $conf->{samlIDPMetaDataXML}->{$idpId}
                    ->{samlIDPMetaDataXML} =~ /entityID=(['"])(.+?)\1/si )
352 353 354 355 356 357 358 359
                {
                    push @msg, "$idpId SAML metadata has no EntityID";
                    $res = 0;
                    next;
                }
                my $eid = $2;
                if ( defined $entityIds{$eid} ) {
                    push @msg,
360
                        "$idpId and $entityIds{$eid} have the same SAML EntityID";
361 362 363 364
                    $res = 0;
                    next;
                }
                $entityIds{$eid} = $idpId;
365 366 367 368 369
            }
            return ( $res, join( ', ', @msg ) );
        },
        samlSPEntityIdUniqueness => sub {
            return 1
370
                unless ( $conf->{samlSPMetaDataXML}
371 372 373 374 375 376
                and %{ $conf->{samlSPMetaDataXML} } );
            my @msg;
            my $res = 1;
            my %entityIds;
            foreach my $spId ( keys %{ $conf->{samlSPMetaDataXML} } ) {
                unless (
377 378
                    $conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML}
                    =~ /entityID=(['"])(.+?)\1/si )
379 380 381 382 383 384 385 386
                {
                    push @msg, "$spId SAML metadata has no EntityID";
                    $res = 0;
                    next;
                }
                my $eid = $2;
                if ( defined $entityIds{$eid} ) {
                    push @msg,
387
                        "$spId and $entityIds{$eid} have the same SAML EntityID";
388 389 390 391
                    $res = 0;
                    next;
                }
                $entityIds{$eid} = $spId;
392 393 394 395
            }
            return ( $res, join( ', ', @msg ) );
        },

396
        # Try to parse combination with declared modules
397 398 399 400
        checkCombinations => sub {
            return 1 unless ( $conf->{authentication} eq 'Combination' );
            require Lemonldap::NG::Common::Combination::Parser;
            return ( 0, 'No module declared for combination' )
401
                unless ( $conf->{combModules} and %{ $conf->{combModules} } );
402 403 404 405 406 407 408 409 410 411
            my $moduleList;
            foreach my $md ( keys %{ $conf->{combModules} } ) {
                my $entry = $conf->{combModules}->{$md};
                $moduleList->{$md} = (
                      $entry->{for} == 2 ? [ undef, {} ]
                    : $entry->{for} == 1 ? [ {}, undef ]
                    :                      [ {}, {} ]
                );
            }
            eval {
412 413
                Lemonldap::NG::Common::Combination::Parser->parse(
                    $moduleList, $conf->{combination} );
414 415
            };
            return ( 0, $@ ) if ($@);
Christophe Maudoux's avatar
Christophe Maudoux committed
416

417 418 419 420
            # Return
            return 1;
        },

Christophe Maudoux's avatar
Christophe Maudoux committed
421 422 423 424 425 426 427 428 429 430 431 432
        # Check Combination parameters
        combinationParameters => sub {
            return 1 unless ( $conf->{authentication} eq "Combination" );
            return ( 0, "Combination rule must be defined" )
                unless ( $conf->{combination} );
            return ( 0, 'userDB must be set to "Same" to enable Combination' )
                unless ( $conf->{userDB} eq "Same" );

            # Return
            return 1;
        },

433
        # Warn if 2F dependencies seem missing
434
        sfaDependencies => sub {
435

Christophe Maudoux's avatar
Christophe Maudoux committed
436
            my $ok = 0;
437
            foreach (qw(u totp utotp yubikey)) {
Christophe Maudoux's avatar
Christophe Maudoux committed
438 439
                $ok ||= $conf->{ $_ . '2fActivation' };
                last if ($ok);
440
            }
Christophe Maudoux's avatar
Christophe Maudoux committed
441
            return 1 unless ($ok);
442 443 444 445 446 447

            # Use TOTP
            if (   $conf->{totp2fActivation}
                or $conf->{utotp2fActivation} )
            {
                eval "use Convert::Base32";
448
                return ( 1,
449
                    "Convert::Base32 module is required to enable TOTP" )
450
                    if ($@);
451 452 453 454 455 456 457
            }

            # Use U2F
            if (   $conf->{u2fActivation}
                or $conf->{utotp2fActivation} )
            {
                eval "use Crypt::U2F::Server::Simple";
458
                return ( 1,
459
                    "Crypt::U2F::Server::Simple module is required to enable U2F"
460 461
                ) if ($@);
            }
Christophe Maudoux's avatar
Christophe Maudoux committed
462

Christophe Maudoux's avatar
typo  
Christophe Maudoux committed
463 464 465
            # Use Yubikey
            if ( $conf->{yubikey2fActivation} ) {
                eval "use Auth::Yubikey_WebClient";
466
                return ( 1,
467
                    "Auth::Yubikey_WebClient module is required to enable Yubikey"
Christophe Maudoux's avatar
typo  
Christophe Maudoux committed
468 469 470
                ) if ($@);
            }

471
            # Return
472 473
            return 1;
        },
474 475 476 477

        # Warn if TOTP or U2F is enabled with UTOTP (U2F + TOTP)
        utotp => sub {
            return 1 unless ( $conf->{utotp2fActivation} );
478
            my $w = "";
479
            foreach ( 'totp', 'u' ) {
Christophe Maudoux's avatar
Christophe Maudoux committed
480
                $w .= uc($_) . "2F is activated twice \n"
481
                    if ( $conf->{ $_ . '2fActivation' } eq '1' );
482 483 484
            }
            return ( 1, ( $w ? $w : () ) );
        },
485 486

        # Warn if TOTP not 6 or 8 digits long
Xavier Guimard's avatar
Xavier Guimard committed
487
        totp2fDigits => sub {
Christophe Maudoux's avatar
Christophe Maudoux committed
488
            return 1 unless ( $conf->{totp2fActivation} );
Xavier Guimard's avatar
Xavier Guimard committed
489 490 491
            return 1 unless ( defined $conf->{totp2fDigits} );
            return (
                1,
492 493
                (   (          $conf->{totp2fDigits} == 6
                            or $conf->{totp2fDigits} == 8
Xavier Guimard's avatar
Xavier Guimard committed
494 495 496 497 498 499
                    )
                    ? ''
                    : 'TOTP should be 6 or 8 digits long'
                )
            );
        },
Christophe Maudoux's avatar
Christophe Maudoux committed
500

Christophe Maudoux's avatar
Christophe Maudoux committed
501
        # Test TOTP params
Christophe Maudoux's avatar
Christophe Maudoux committed
502 503 504
        totp2fParams => sub {
            return 1 unless ( $conf->{totp2fActivation} );
            return ( 0, 'TOTP range must be defined' )
505
                unless ( $conf->{totp2fRange} );
Christophe Maudoux's avatar
Christophe Maudoux committed
506
            return ( 1, "TOTP interval should be higher than 10s" )
507
                unless ( $conf->{totp2fInterval} > 10 );
Christophe Maudoux's avatar
Christophe Maudoux committed
508 509 510 511 512 513 514 515 516 517

            # Return
            return 1;
        },

        # Error if Yubikey client ID and secret key are missing
        # Warn if Yubikey public ID size is not 12 digits long
        yubikey2fParams => sub {
            return 1 unless ( $conf->{yubikey2fActivation} );
            return ( 0, "Yubikey client ID and secret key must be set" )
518
                unless ( defined $conf->{yubikey2fSecretKey}
Christophe Maudoux's avatar
Christophe Maudoux committed
519 520 521
                && defined $conf->{yubikey2fClientID} );
            return (
                1,
522
                (   ( $conf->{yubikey2fPublicIDSize} == 12 )
Christophe Maudoux's avatar
Christophe Maudoux committed
523 524 525 526 527 528
                    ? ''
                    : 'Yubikey public ID size should be 12 digits long'
                )
            );
        },

529 530
        # Error if REST 2F verify URL is missing
        rest2fVerifyUrl => sub {
Christophe Maudoux's avatar
Christophe Maudoux committed
531
            return 1 unless ( $conf->{rest2fActivation} );
532
            return ( 0, "REST 2F Verify URL must be set" )
533
                unless ( defined $conf->{rest2fVerifyUrl} );
Christophe Maudoux's avatar
Christophe Maudoux committed
534 535 536 537 538

            # Return
            return 1;
        },

Christophe Maudoux's avatar
Christophe Maudoux committed
539
        # Warn if 2FA is required without a registrable 2F module enabled
Christophe Maudoux's avatar
Christophe Maudoux committed
540 541
        required2FA => sub {
            return 1 unless ( $conf->{sfRequired} );
542

Christophe Maudoux's avatar
Christophe Maudoux committed
543 544
            my $msg = '';
            my $ok  = 0;
Christophe Maudoux's avatar
Christophe Maudoux committed
545
            foreach (qw(u totp yubikey)) {
Christophe Maudoux's avatar
Christophe Maudoux committed
546
                $ok ||= $conf->{ $_ . '2fActivation' }
547
                    && $conf->{ $_ . '2fSelfRegistration' };
Christophe Maudoux's avatar
Christophe Maudoux committed
548
                last if ($ok);
549
            }
Christophe Maudoux's avatar
Christophe Maudoux committed
550

Christophe Maudoux's avatar
Christophe Maudoux committed
551
            $ok ||= $conf->{'utotp2fActivation'}
552
                && ( $conf->{'u2fSelfRegistration'}
Christophe Maudoux's avatar
Christophe Maudoux committed
553
                || $conf->{'totp2fSelfRegistration'} );
554 555 556
            $msg
                = "A self registrable module should be enabled to require 2FA"
                unless ($ok);
Christophe Maudoux's avatar
Christophe Maudoux committed
557

Christophe Maudoux's avatar
Christophe Maudoux committed
558
            return ( 1, $msg );
Christophe Maudoux's avatar
Christophe Maudoux committed
559 560 561 562 563
        },

        # Error if external 2F Send or Validate command is missing
        ext2fCommands => sub {
            return 1 unless ( $conf->{ext2fActivation} );
564
            return ( 0, "External 2F Send or Validate command must be set" )
565
                unless ( defined $conf->{ext2FSendCommand}
Christophe Maudoux's avatar
Christophe Maudoux committed
566
                && defined $conf->{ext2FValidateCommand} );
567 568 569

            # Return
            return 1;
570 571
        },

Christophe Maudoux's avatar
Christophe Maudoux committed
572
        # Warn if XSRF token TTL is higher than 10s
Xavier Guimard's avatar
Xavier Guimard committed
573
        formTimeout => sub {
Christophe Maudoux's avatar
Christophe Maudoux committed
574
            return 1 unless ( defined $conf->{formTimeout} );
Christophe Maudoux's avatar
Christophe Maudoux committed
575
            return ( 0, "XSRF form token TTL must be higher than 30s" )
576
                unless ( $conf->{formTimeout} > 30 );
Christophe Maudoux's avatar
Christophe Maudoux committed
577
            return ( 1, "XSRF form token TTL should not be higher than 2mn" )
578
                if ( $conf->{formTimeout} > 120 );
579 580 581

            # Return
            return 1;
582
        },
Christophe Maudoux's avatar
Christophe Maudoux committed
583

584 585 586
        # Warn if number of password reset retries is null
        passwordResetRetries => sub {
            return 1 unless ( $conf->{portalDisplayResetPassword} );
587 588 589
            return ( 1,
                "Number of reset password retries should not be null" )
                unless ( $conf->{passwordResetAllowedRetries} );
590 591 592 593 594

            # Return
            return 1;
        },

595
        # Warn if bruteForceProtection enabled without History
596 597 598
        bruteForceProtection => sub {
            return 1 unless ( $conf->{bruteForceProtection} );
            return ( 1,
599
                '"History" plugin is required to enable "BruteForceProtection" plugin'
600
            ) unless ( $conf->{loginHistoryEnabled} );
601
            return ( 1,
602
                'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin'
603
            ) unless ( $conf->{failedLoginNumber} > 2 );
604 605 606

            # Return
            return 1;
Xavier Guimard's avatar
Xavier Guimard committed
607
        },
608

609 610 611
        # Warn if Mailrest plugin is enabled without Token or Captcha
        checkMailResetSecurity => sub {
            return 1 unless ( $conf->{portalDisplayResetPassword} );
612 613 614 615 616
            return ( -1,
                '"passwordMailReset" plugin is enabled without CSRF Token neither Captcha required !!!'
                )
                unless ( $conf->{requireToken}
                or $conf->{captcha_mail_enabled} );
617 618 619 620

            # Return
            return 1;
        },
621

Xavier Guimard's avatar
Xavier Guimard committed
622 623 624 625
    };
}

1;