Attributes.pm 119 KB
Newer Older
Christophe Maudoux's avatar
Christophe Maudoux committed
1
#  This file contains the description of all configuration parameters
2 3 4
# It may be included only by batch files, never in portal or handler chain
# for performances reasons

Xavier Guimard's avatar
Xavier Guimard committed
5
# DON'T FORGET TO RUN "make json" AFTER EACH CHANGE
6 7 8

package Lemonldap::NG::Manager::Build::Attributes;

Xavier Guimard's avatar
Xavier Guimard committed
9
our $VERSION = '2.0.2';
10 11 12 13
use strict;
use Regexp::Common qw/URI/;

my $perlExpr = sub {
14
    my ( $val, $conf ) = @_;
15
    my $s = '';
16
    Safe->new->reval("BEGIN { warnings->unimport; } $s $val");
17 18 19
    my $err = join( '',
        grep { $_ =~ /Undefined subroutine/ ? () : $_ } split( /\n/, $@ ) );
    return $err ? ( 1, "__badExpression__: $err" ) : (1);
20 21
};

22
my $url = $RE{URI}{HTTP}{ -scheme => "https?" };
Xavier Guimard's avatar
Xavier Guimard committed
23 24 25
$url =~ s/(?<=[^\\])\$/\\\$/g;
$url = qr/$url/;

26 27 28 29 30
sub types {
    return {

        # Simple text types
        text => {
31
            test    => sub { 1 },
32 33 34
            msgFail => '__malformedValue__',
        },
        password => {
35
            test    => sub { 1 },
36 37 38
            msgFail => '__malformedValue__',
        },
        longtext => {
39
            test => sub { 1 }
40 41 42
        },
        url => {
            form    => 'text',
Xavier Guimard's avatar
Xavier Guimard committed
43
            test    => $url,
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
            msgFail => '__badUrl__',
        },
        PerlModule => {
            form    => 'text',
            test    => qr/^[a-zA-Z][a-zA-Z0-9]*(?:::[a-zA-Z][a-zA-Z0-9]*)*$/,
            msgFail => '__badPerlPackageName__',
        },
        hostname => {
            form    => 'text',
            test    => qr/^(?:$Regexp::Common::URI::RFC2396::host)?$/,
            msgFail => '__badHostname__',
        },
        pcre => {
            form => 'text',
            test => sub {
59
                eval { qr/$_[0]/ };
60 61 62 63 64
                return $@ ? ( 0, "__badRegexp__: $@" ) : (1);
            },
        },
        lmAttrOrMacro => {
            form => 'text',
Xavier Guimard's avatar
Xavier Guimard committed
65 66
            test => sub {
                my ( $val, $conf ) = @_;
67
                return 1
68
                  if ( defined $conf->{macros}->{$val}
69
                    or $val eq '_timezone' );
70 71
                foreach ( keys %$conf ) {
                    return 1
72
                      if ( $_ =~ /exportedvars$/i
73
                        and defined $conf->{$_}->{$val} );
Xavier Guimard's avatar
Xavier Guimard committed
74
                }
75
                return ( 1, "__unknownAttrOrMacro__: $val" );
Xavier Guimard's avatar
Xavier Guimard committed
76
            },
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
        },

        # Other types
        int => {
            test    => qr/^\-?\d+$/,
            msgFail => '__notAnInteger__',
        },
        bool => {
            test    => qr/^[01]$/,
            msgFail => '__notABoolean__',
        },
        trool => {
            test    => qr/^(?:-1|0|1)$/,
            msgFail => '__authorizedValues__: -1, 0, 1',
        },
        boolOrExpr => {
            test    => $perlExpr,
            msgFail => '__notAValidPerlExpression__',
        },
Xavier Guimard's avatar
Xavier Guimard committed
96 97
        keyTextContainer => {
            test       => qr/./,
98
            msgFail    => '__emptyValueNotAllowed__',
Xavier Guimard's avatar
Xavier Guimard committed
99
            keyTest    => qr/^\w[\w\.\-]*$/,
100
            keyMsgFail => '__badKeyName__',
Xavier Guimard's avatar
Xavier Guimard committed
101 102 103
        },
        subContainer => {
            keyTest => qr/\w/,
104
            test    => sub { 1 },
Xavier Guimard's avatar
Xavier Guimard committed
105
        },
Xavier Guimard's avatar
Xavier Guimard committed
106 107
        select => {
            test => sub {
108
                my $test = grep ( { $_ eq $_[0] }
109
                    map ( { $_->{k} } @{ $_[2]->{select} } ) );
Xavier Guimard's avatar
Xavier Guimard committed
110
                return $test
111 112
                  ? 1
                  : ( 1, "Invalid value '$_[0]' for this select" );
Xavier Guimard's avatar
Xavier Guimard committed
113 114
            },
        },
115 116 117

        # Files type (long text)
        file => {
118
            test => sub { 1 }
119 120
        },
        RSAPublicKey => {
121
            test => sub {
122
                return (
123 124
                    $_[0] =~
/^(?:(?:\-+\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
125
                    ? (1)
126 127
                    : ( 1, '__badPemEncoding__' )
                );
128
            },
129
        },
130
        'RSAPublicKeyOrCertificate' => {
131
            'test' => sub {
132
                return (
133 134
                    $_[0] =~
/^(?:(?:\-+\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
135
                    ? (1)
136 137
                    : ( 1, '__badPemEncoding__' )
                );
138
            },
139
        },
140
        RSAPrivateKey => {
141
            test => sub {
142
                return (
143 144
                    $_[0] =~
/^(?:(?:\-+\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
145
                    ? (1)
146 147
                    : ( 1, '__badPemEncoding__' )
                );
148
            },
149 150 151
        },

        authParamsText => {
152
            test => sub { 1 }
153 154
        },
        blackWhiteList => {
155
            test => sub { 1 }
156 157
        },
        catAndAppList => {
158
            test => sub { 1 }
159 160 161
        },
        keyText => {
            keyTest => qr/^[a-zA-Z0-9_]+$/,
162
            test    => qr/^.*$/,
163 164 165
            msgFail => '__badValue__',
        },
        menuApp => {
166
            test => sub { 1 }
167 168
        },
        menuCat => {
169
            test => sub { 1 }
170 171
        },
        oidcOPMetaDataNode => {
172
            test => sub { 1 }
173 174
        },
        oidcRPMetaDataNode => {
175
            test => sub { 1 }
176 177
        },
        oidcmetadatajson => {
178
            test => sub { 1 }
179 180
        },
        oidcmetadatajwks => {
181
            test => sub { 1 }
182 183
        },
        portalskin => {
184
            test => sub { 1 }
185 186
        },
        portalskinbackground => {
187
            test => sub { 1 }
188 189
        },
        post => {
190
            test => sub { 1 }
191 192
        },
        rule => {
193
            test => sub { 1 }
194 195
        },
        samlAssertion => {
196
            test => sub { 1 }
197 198
        },
        samlAttribute => {
199
            test => sub { 1 }
200 201
        },
        samlIDPMetaDataNode => {
202
            test => sub { 1 }
203 204
        },
        samlSPMetaDataNode => {
205
            test => sub { 1 }
206 207
        },
        samlService => {
208
            test => sub { 1 }
209
        },
210
        array => {
211
            test => sub { 1 }
212
        },
213 214 215 216 217 218 219
    };
}

sub attributes {
    return {

        # Other
220 221 222
        checkTime => {
            type => 'int',
            documentation =>
223
              'Timeout to check new configuration in local cache',
224
            default => 600,
225 226 227 228 229 230
            flags   => 'hp',
        },
        mySessionAuthorizedRWKeys => {
            type          => 'array',
            documentation => 'Alterable session keys by user itself',
            default =>
231
              [ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
232
        },
Xavier Guimard's avatar
Xavier Guimard committed
233 234 235 236 237 238 239 240 241 242 243 244
        configStorage => {
            type          => 'text',
            documentation => 'Configuration storage',
            flags         => 'hmp',
        },
        localStorage => {
            type          => 'text',
            documentation => 'Local cache',
            flags         => 'hmp',
        },
        localStorageOptions => {
            type          => 'keyTextContainer',
Xavier Guimard's avatar
Xavier Guimard committed
245
            documentation => 'Local cache parameters',
Xavier Guimard's avatar
Xavier Guimard committed
246 247
            flags         => 'hmp',
        },
248 249 250 251 252 253
        cfgNum => {
            type          => 'int',
            default       => 0,
            documentation => 'Enable Cross Domain Authentication',
        },
        cfgAuthor => {
254 255
            type          => 'text',
            documentation => 'Name of the author of the current configuration',
256 257
        },
        cfgAuthorIP => {
258 259
            type          => 'text',
            documentation => 'Uploader IP address of the current configuration',
260 261 262 263 264 265 266 267 268
        },
        cfgDate => {
            type          => 'int',
            documentation => 'Timestamp of the current configuration',
        },
        cfgLog => {
            type          => 'longtext',
            documentation => 'Configuration update log',
        },
269 270 271 272
        cfgVersion => {
            type          => 'text',
            documentation => 'Version of LLNG which build configuration',
        },
Xavier Guimard's avatar
Xavier Guimard committed
273 274 275 276 277
        status => {
            type          => 'bool',
            documentation => 'Status daemon activation',
            flags         => 'h',
        },
278 279 280
        confirmFormMethod => {
            type => "select",
            select =>
281
              [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
282 283 284
            default       => 'post',
            documentation => 'HTTP method for confirm page form',
        },
285 286
        customFunctions => {
            type          => 'text',
287
            test          => qr/^(?:\w+(?:::\w+)*(?:\s+\w+(?:::\w+)*)*)?$/,
288
            help          => 'customfunctions.html',
289
            msgFail       => "__badCustomFuncName__",
Xavier Guimard's avatar
Xavier Guimard committed
290 291
            documentation => 'List of custom functions',
            flags         => 'hmp',
292 293
        },
        https => {
294 295
            default       => -1,
            type          => 'trool',
296
            documentation => 'Use HTTPS for redirection from portal',
Xavier Guimard's avatar
Xavier Guimard committed
297
            flags         => 'h',
298 299 300 301
        },
        infoFormMethod => {
            type => "select",
            select =>
302
              [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
303 304 305
            default       => 'get',
            documentation => 'HTTP method for info page form',
        },
Xavier Guimard's avatar
Xavier Guimard committed
306
        port => {
307
            default       => -1,
Xavier Guimard's avatar
Xavier Guimard committed
308 309 310 311
            type          => 'int',
            documentation => 'Force port in redirection',
            flags         => 'h',
        },
312 313 314 315 316 317
        jsRedirect => {
            type          => 'boolOrExpr',
            default       => 0,
            documentation => 'Use javascript for redirections',
        },
        logoutServices => {
318 319 320 321
            type          => 'keyTextContainer',
            help          => 'logoutforward.html',
            default       => {},
            documentation => 'Send logout trough GET request to these services',
322 323 324 325 326
        },
        maintenance => {
            default       => 0,
            type          => 'bool',
            documentation => 'Maintenance mode for all virtual hosts',
Xavier Guimard's avatar
Xavier Guimard committed
327
            flags         => 'h',
328
        },
329 330 331 332
        nginxCustomHandlers => {
            type    => 'keyTextContainer',
            keyTest => qr/^\w+$/,
            test    => qr/^[a-zA-Z][a-zA-Z0-9]*(?:::[a-zA-Z][a-zA-Z0-9]*)*$/,
333
            help    => 'handlerarch.html',
334
            msgFail => '__badPerlPackageName__',
Xavier Guimard's avatar
Xavier Guimard committed
335
            documentation => 'Custom Nginx handler (deprecated)',
336
        },
337 338 339 340 341
        noAjaxHook => {
            default       => 0,
            type          => 'bool',
            documentation => 'Avoid replacing 302 by 401 for Ajax responses',
        },
342 343 344 345
        portal => {
            type          => 'url',
            default       => 'http://auth.example.com/',
            documentation => 'Portal URL',
Xavier Guimard's avatar
Xavier Guimard committed
346
            flags         => 'hmp',
347 348
            test          => $url,
            msgFail       => '__badUrl__',
349
        },
350 351 352
        portalStatus => {
            type          => 'bool',
            default       => 0,
Xavier Guimard's avatar
Xavier Guimard committed
353
            help          => 'status.html',
354 355
            documentation => 'Enable portal status',
        },
356 357 358 359
        portalUserAttr => {
            type    => 'text',
            default => '_user',
            documentation =>
360
              'Session parameter to display connected user in portal',
361 362 363 364
        },
        redirectFormMethod => {
            type => "select",
            select =>
365
              [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
366 367 368
            default       => 'get',
            documentation => 'HTTP method for redirect page form',
        },
369 370 371 372 373 374
        reloadTimeout => {
            type          => 'int',
            default       => 5,
            documentation => 'Configuration reload timeout',
            flags         => 'm',
        },
375
        reloadUrls => {
376 377 378 379 380
            type          => 'keyTextContainer',
            help          => 'configlocation.html#configuration_reload',
            keyTest       => qr/^$Regexp::Common::URI::RFC2396::host(?::\d+)?$/,
            test          => $url,
            msgFail       => '__badUrl__',
Xavier Guimard's avatar
Xavier Guimard committed
381
            documentation => 'URL to call on reload',
382
        },
Clément OUDOT's avatar
Clément OUDOT committed
383
        portalMainLogo => {
384 385 386 387
            type          => 'text',
            default       => 'common/logos/logo_llng_400px.png',
            documentation => 'Portal main logo path',
        },
388 389 390 391 392
        showLanguages => {
            type          => 'bool',
            default       => 1,
            documentation => 'Display langs icons',
        },
393 394 395 396
        staticPrefix => {
            type          => 'text',
            documentation => 'Prefix of static files for HTML templates',
        },
397 398 399 400
        multiValuesSeparator => {
            type          => 'authParamsText',
            default       => '; ',
            documentation => 'Separator for multiple values',
Xavier Guimard's avatar
Xavier Guimard committed
401
            flags         => 'hmp',
402
        },
403
        stayConnected => {
Xavier Guimard's avatar
Xavier Guimard committed
404 405
            type => 'bool',

406
            #help          => 'stayconnected.html',
407
            default       => 0,
408 409
            documentation => 'Enable StayConnected plugin',
        },
410
        checkState => {
411
            type          => 'bool',
412
            default       => 0,
413 414
            documentation => 'Enable CheckState plugin',
        },
415
        checkStateSecret => {
416
            type          => 'text',
417 418
            documentation => 'Secret token for CheckState plugin',
        },
419
        skipRenewConfirmation => {
420 421
            type    => 'bool',
            default => 0,
422
            documentation =>
423
              'Avoid asking confirmation when an Issuer asks to renew auth',
424
        },
425 426 427 428
        handlerInternalCache => {
            type          => 'int',
            default       => 15,
            documentation => 'Handler internal cache timeout',
Christophe Maudoux's avatar
Christophe Maudoux committed
429
            flags         => 'hp',
430
        },
431

432 433 434 435 436 437 438 439 440
        # Loggers (ini only)
        logLevel => {
            type          => 'text',
            documentation => 'Log level, must be set in .ini',
            flags         => 'hmp',
        },
        logger => {
            type          => 'text',
            documentation => 'technical logger',
441
            flags         => 'hmp',
442 443 444 445
        },
        userLogger => {
            type          => 'text',
            documentation => 'User actions logger',
446
            flags         => 'hmp',
447 448 449 450
        },
        log4perlConfFile => {
            type          => 'text',
            documentation => 'Log4Perl logger configuration file',
451
            flags         => 'hmp',
452 453 454 455
        },
        sentryDsn => {
            type          => 'text',
            documentation => 'Sentry logger DSN',
456
            flags         => 'hmp',
457 458 459 460
        },
        syslogFacility => {
            type          => 'text',
            documentation => 'Syslog logger technical facility',
461
            flags         => 'hmp',
462 463 464 465
        },
        userSyslogFacility => {
            type          => 'text',
            documentation => 'Syslog logger user-actions facility',
466
            flags         => 'hmp',
467 468 469
        },

        # Manager or PSGI protected apps
470
        protection => {
471 472 473
            type          => 'text',
            test          => qr/^(?:none|authenticate|manager|)$/,
            msgFail       => '__authorizedValues__: none authenticate manager',
474
            documentation => 'Manager protection method',
Xavier Guimard's avatar
Xavier Guimard committed
475
            flags         => 'hm',
476 477 478 479 480 481 482 483 484 485
        },

        # Menu
        activeTimer => {
            type          => 'bool',
            default       => 1,
            documentation => 'Enable timers on portal pages',
        },
        applicationList => {
            type    => 'catAndAppList',
486
            keyTest => qr/\w/,
487 488
            help    => 'portalmenu.html#categories_and_applications',
            default => {
489
                default => { catname => 'Default category', type => "category" }
490 491 492
            },
            documentation => 'Applications list',
        },
493 494 495 496 497
        portalErrorOnExpiredSession => {
            type          => 'bool',
            default       => 1,
            documentation => 'Show error if session is expired',
        },
498
        portalErrorOnMailNotFound => {
dcoutadeur dcoutadeur's avatar
dcoutadeur dcoutadeur committed
499 500 501
            type    => 'bool',
            default => 0,
            documentation =>
502
              'Show error if mail is not found in password reset process',
503
        },
504 505 506 507 508 509 510 511 512 513 514 515 516 517
        portalOpenLinkInNewWindow => {
            type          => 'bool',
            default       => 0,
            documentation => 'Open applications in new windows',
        },
        portalPingInterval => {
            type          => 'int',
            default       => 60000,
            documentation => 'Interval in ms between portal Ajax pings ',
        },
        portalSkin => {
            type          => 'portalskin',
            default       => 'bootstrap',
            documentation => 'Name of portal skin',
Xavier Guimard's avatar
Xavier Guimard committed
518
            select        => [ { k => 'bootstrap', v => 'Bootstrap' }, ],
519 520 521 522 523 524
        },
        portalSkinBackground => {
            type          => 'portalskinbackground',
            documentation => 'Background image of portal skin',
            select        => [
                { k => "", v => 'None' },
525 526
                {
                    k => "1280px-Anse_Source_d'Argent_2-La_Digue.jpg",
527 528
                    v => 'Anse'
                },
529 530 531
                {
                    k =>
"1280px-Autumn-clear-water-waterfall-landscape_-_Virginia_-_ForestWander.jpg",
532 533 534
                    v => 'Waterfall'
                },
                { k => "1280px-BrockenSnowedTrees.jpg", v => 'Snowed Trees' },
535 536
                {
                    k => "1280px-Cedar_Breaks_National_Monument_partially.jpg",
537 538
                    v => 'National Monument'
                },
539 540
                {
                    k => "1280px-Parry_Peak_from_Winter_Park.jpg",
541 542
                    v => 'Winter'
                },
543 544
                {
                    k => "Aletschgletscher_mit_Pinus_cembra1.jpg",
545 546
                    v => 'Pinus'
                },
547 548 549
            ],
        },
        portalSkinRules => {
Xavier Guimard's avatar
Xavier Guimard committed
550 551 552 553 554 555 556
            type          => 'keyTextContainer',
            help          => 'portalcustom.html',
            keyTest       => $perlExpr,
            keyMsgFail    => '__badSkinRule__',
            test          => qr/^\w+$/,
            msgFail       => '__badValue__',
            documentation => 'Rules to choose portal skin',
557 558 559
        },

        # Security
560
        formTimeout => {
Xavier Guimard's avatar
Xavier Guimard committed
561 562
            default       => 120,
            type          => 'int',
563 564 565
            documentation => 'Token timeout for forms',
        },
        requireToken => {
Xavier Guimard's avatar
Xavier Guimard committed
566 567
            default       => 1,
            type          => 'bool',
568 569
            documentation => 'Enable token for forms',
        },
570 571 572 573 574
        tokenUseGlobalStorage => {
            default       => 0,
            type          => 'bool',
            documentation => 'Enable global token storage',
        },
575 576 577 578
        cda => {
            default       => 0,
            type          => 'bool',
            documentation => 'Enable Cross Domain Authentication',
Xavier Guimard's avatar
Xavier Guimard committed
579
            flags         => 'hp',
580
        },
581
        checkUser => {
582
            default       => 0,
583
            type          => 'bool',
584
            documentation => 'Enable check user',
585 586
            flags         => 'p',
        },
587 588
        checkUserHiddenAttributes => {
            type          => 'text',
589
            default       => 'UA _2fDevices _loginHistory',
590 591 592
            documentation => 'Attributes to hide in CheckUser plugin',
            flags         => 'p',
        },
593 594 595 596 597 598 599 600 601 602 603 604
        checkUserDisplayPersistentInfo => {
            default       => 0,
            type          => 'bool',
            documentation => 'Display persistent session info',
            flags         => 'p',
        },
        checkUserDisplayEmptyValues => {
            default       => 0,
            type          => 'bool',
            documentation => 'Display session empty values',
            flags         => 'p',
        },
605 606 607 608 609
        checkXSS => {
            default       => 1,
            type          => 'bool',
            documentation => 'Check XSS',
        },
610
        portalForceAuthn => {
611
            default => 0,
612
            help    => 'forcereauthn.html',
613 614
            type    => 'bool',
            documentation =>
615
              'Enable force to authenticate when displaying portal',
616
        },
617 618
        portalForceAuthnInterval => {
            default => 5,
619 620
            type    => 'int',
            documentation =>
621
'Maximum interval in seconds since last authentication to force reauthentication',
622
        },
623
        bruteForceProtection => {
624
            default       => 0,
625
            help          => 'bruteforceprotection.html',
626 627 628 629 630 631 632
            type          => 'bool',
            documentation => 'Enable brute force attack protection',
        },
        bruteForceProtectionTempo => {
            default => 30,
            type    => 'int',
            documentation =>
633
              'Brute force attack protection -> Tempo before try again',
634 635 636 637 638
        },
        bruteForceProtectionMaxAge => {
            default => 300,
            type    => 'int',
            documentation =>
639
'Brute force attack protection -> Max age between last and first allowed failed login',
640 641 642 643 644
        },
        bruteForceProtectionMaxFailed => {
            default => 3,
            type    => 'int',
            documentation =>
645
              'Brute force attack protection -> Max allowed failed login',
646
        },
647
        grantSessionRules => {
Xavier Guimard's avatar
Xavier Guimard committed
648 649
            type          => 'grantContainer',
            keyTest       => $perlExpr,
650
            test          => sub { 1 },
Xavier Guimard's avatar
Xavier Guimard committed
651
            documentation => 'Rules to grant sessions',
652 653 654 655 656 657 658 659 660 661
        },
        hiddenAttributes => {
            type          => 'text',
            default       => '_password',
            documentation => 'Name of attributes to hide in logs',
        },
        key => {
            type          => 'password',
            documentation => 'Secret key',
        },
662 663
        cspDefault => {
            type          => 'text',
Xavier Guimard's avatar
Xavier Guimard committed
664
            default       => "'self'",
665 666
            documentation => 'Default value for Content-Security-Policy',
        },
667
        cspFormAction => {
668 669 670
            type    => 'text',
            default => "'self'",
            documentation =>
671
              'Form action destination for Content-Security-Policy',
672
        },
673 674
        cspImg => {
            type          => 'text',
675
            default       => "'self' data:",
676 677 678 679 680 681 682 683 684
            documentation => 'Image source for Content-Security-Policy',
        },
        cspScript => {
            type          => 'text',
            default       => "'self'",
            documentation => 'Javascript source for Content-Security-Policy',
        },
        cspStyle => {
            type          => 'text',
685
            default       => "'self'",
686 687 688 689 690 691
            documentation => 'Style source for Content-Security-Policy',
        },
        cspConnect => {
            type    => 'text',
            default => "'self'",
            documentation =>
692
              'Authorized Ajax destination for Content-Security-Policy',
693 694 695 696 697 698
        },
        cspFont => {
            type          => 'text',
            default       => "'self'",
            documentation => 'Font source for Content-Security-Policy',
        },
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
        portalAntiFrame => {
            default       => 1,
            type          => 'bool',
            documentation => 'Avoid portal to be displayed inside frames',
        },
        portalCheckLogins => {
            default       => 1,
            type          => 'bool',
            documentation => 'Display login history checkbox in portal',
        },
        randomPasswordRegexp => {
            type          => 'pcre',
            default       => '[A-Z]{3}[a-z]{5}.\d{2}',
            documentation => 'Regular expression to create a random password',
        },
Xavier Guimard's avatar
Xavier Guimard committed
714
        trustedDomains =>
715
          { type => 'text', documentation => 'Trusted domains', },
Xavier Guimard's avatar
Xavier Guimard committed
716
        storePassword => {
717 718 719 720 721 722
            default       => 0,
            type          => 'bool',
            documentation => 'Store password in session',
        },
        timeout => {
            type          => 'int',
Xavier Guimard's avatar
Xavier Guimard committed
723
            test          => sub { $_[0] > 0 },
724 725 726 727
            default       => 72000,
            documentation => 'Session timeout on server side',
        },
        timeoutActivity => {
728
            type          => 'int',
Xavier Guimard's avatar
Xavier Guimard committed
729
            test          => sub { $_[0] >= 0 },
730 731 732
            default       => 0,
            documentation => 'Session activity timeout on server side',
        },
733 734 735 736 737 738
        timeoutActivityInterval => {
            type          => 'int',
            test          => sub { $_[0] >= 0 },
            default       => 60,
            documentation => 'Update session timeout interval on server side',
        },
739 740 741 742 743 744 745 746 747
        userControl => {
            type          => 'pcre',
            default       => '^[\w\.\-@]+$',
            documentation => 'Regular expression to validate login',
        },
        useRedirectOnError => {
            type          => 'bool',
            default       => 1,
            documentation => 'Use 302 redirect code for error (500)',
Xavier Guimard's avatar
Xavier Guimard committed
748
            flags         => 'h',
749 750 751 752 753 754 755 756 757
        },
        useRedirectOnForbidden => {
            default       => 0,
            type          => 'bool',
            documentation => 'Use 302 redirect code for forbidden (403)',
        },
        useSafeJail => {
            default       => 1,
            type          => 'bool',
Xavier Guimard's avatar
Xavier Guimard committed
758
            help          => 'safejail.html',
759
            documentation => 'Activate Safe jail',
Xavier Guimard's avatar
Xavier Guimard committed
760
            flags         => 'hp',
761 762 763 764 765
        },
        whatToTrace => {
            type          => 'lmAttrOrMacro',
            default       => 'uid',
            documentation => 'Session parameter used to fill REMOTE_USER',
Xavier Guimard's avatar
Xavier Guimard committed
766
            flags         => 'hp',
767
        },
768
        lwpOpts => {
769 770 771
            type          => 'keyTextContainer',
            documentation => 'Options given to LWP::UserAgent',
        },
772 773 774 775
        lwpSslOpts => {
            type          => 'keyTextContainer',
            documentation => 'SSL options given to LWP::UserAgent',
        },
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820

        # History
        failedLoginNumber => {
            default       => 5,
            type          => 'int',
            documentation => 'Number of failures stored in login history',
        },
        loginHistoryEnabled => {
            default       => 0,
            type          => 'bool',
            documentation => 'Enable login history',
        },
        portalDisplayLoginHistory => {
            type          => 'boolOrExpr',
            default       => 1,
            documentation => 'Display login history tab in portal',
        },
        successLoginNumber => {
            default       => 5,
            type          => 'int',
            documentation => 'Number of success stored in login history',
        },

        # Other displays
        portalDisplayAppslist => {
            type          => 'boolOrExpr',
            default       => 1,
            documentation => 'Display applications tab in portal',
        },
        portalDisplayChangePassword => {
            type          => 'boolOrExpr',
            default       => '$_auth =~ /^(LDAP|DBI|Demo)$/',
            documentation => 'Display password tab in portal',
        },
        portalDisplayLogout => {
            default       => 1,
            type          => 'boolOrExpr',
            documentation => 'Display logout tab in portal',
        },
        portalDisplayRegister => {
            default       => 1,
            type          => 'bool',
            documentation => 'Display register button in portal',
        },
        portalDisplayResetPassword => {
821
            default       => 0,
822 823 824
            type          => 'bool',
            documentation => 'Display reset password button in portal',
        },
825
        passwordResetAllowedRetries => {
Christophe Maudoux's avatar
Christophe Maudoux committed
826
            default       => 3,
827 828 829
            type          => 'int',
            documentation => 'Maximum number of retries to reset password',
        },
830 831
        portalDisplayOidcConsents => {
            type          => 'boolOrExpr',
832
            default       => '$_oidcConnectedRP',
833 834
            documentation => 'Display OIDC consent tab in portal',
        },
835 836

        # Cookies
Xavier Guimard's avatar
Xavier Guimard committed
837
        cookieExpiration => {
838
            type          => 'int',
Xavier Guimard's avatar
Xavier Guimard committed
839 840 841
            documentation => 'Cookie expiration',
            flags         => 'hp',
        },
Xavier Guimard's avatar
Xavier Guimard committed
842
        cookieName => {
843 844 845 846 847
            type          => 'text',
            test          => qr/^[a-zA-Z][a-zA-Z0-9_-]*$/,
            msgFail       => '__badCookieName__',
            default       => 'lemonldap',
            documentation => 'Name of the main cookie',
Xavier Guimard's avatar
Xavier Guimard committed
848
            flags         => 'hp',
849 850
        },
        domain => {
851 852 853 854
            type          => 'text',
            test          => qr/^(?:$Regexp::Common::URI::RFC2396::hostname)?$/,
            msgFail       => '__badDomainName__',
            default       => 'example.com',
855
            documentation => 'DNS domain',
Xavier Guimard's avatar
Xavier Guimard committed
856
            flags         => 'hp',
857 858 859 860 861
        },
        httpOnly => {
            default       => 1,
            type          => 'bool',
            documentation => 'Enable httpOnly flag in cookie',
Xavier Guimard's avatar
Xavier Guimard committed
862
            flags         => 'hp',
863 864 865 866 867 868 869 870 871 872 873
        },
        securedCookie => {
            type   => 'select',
            select => [
                { k => '0', v => 'unsecuredCookie' },
                { k => '1', v => 'securedCookie' },
                { k => '2', v => 'doubleCookie' },
                { k => '3', v => 'doubleCookieForSingleSession' },
            ],
            default       => 0,
            documentation => 'Cookie securisation method',
Xavier Guimard's avatar
Xavier Guimard committed
874
            flags         => 'hp',
875 876 877
        },

        # Notification
878
        oldNotifFormat => {
Xavier Guimard's avatar
Xavier Guimard committed
879 880
            type          => 'bool',
            default       => 0,
Xavier Guimard's avatar
Xavier Guimard committed
881
            documentation => 'Use old XML format for notifications',
882
        },
883 884 885 886 887
        notificationWildcard => {
            type          => 'text',
            default       => 'allusers',
            documentation => 'Notification string to match all users',
        },
Xavier Guimard's avatar
Xavier Guimard committed
888 889 890 891 892
        notificationXSLTfile => {
            type          => 'text',
            documentation => 'Custom XSLT document for notifications',
        },
        notification => {
893 894 895 896
            default       => 0,
            type          => 'bool',
            documentation => 'Notification activation',
        },
897 898 899 900 901
        notificationServer => {
            default       => 0,
            type          => 'bool',
            documentation => 'Notification server activation',
        },
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
        notificationStorage => {
            type          => 'PerlModule',
            default       => 'File',
            documentation => 'Notification backend',
        },
        notificationStorageOptions => {
            type    => 'keyTextContainer',
            default => { dirName => '/var/lib/lemonldap-ng/notifications', },
            documentation => 'Notification backend options',
        },

        # Captcha
        captcha_login_enabled => {
            default       => 0,
            type          => 'bool',
            documentation => 'Captcha on login page',
        },
        captcha_mail_enabled => {
920
            default       => 1,
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
            type          => 'bool',
            documentation => 'Captcha on password reset page',
        },
        captcha_register_enabled => {
            default       => 1,
            type          => 'bool',
            documentation => 'Captcha on account creation page',
        },
        captcha_size => {
            type          => 'int',
            default       => 6,
            documentation => 'Captcha size',
        },

        # Variables
        exportedVars => {
            type          => 'keyTextContainer',
            help          => 'exportedvars.html',
            keyTest       => qr/^!?[_a-zA-Z][a-zA-Z0-9_]*$/,
940
            keyMsgFail    => '__badVariableName__',
941 942 943 944 945 946 947 948
            test          => qr/^[_a-zA-Z][a-zA-Z0-9_:\-]*$/,
            msgFail       => '__badValue__',
            default       => { 'UA' => 'HTTP_USER_AGENT' },
            documentation => 'Main exported variables',
        },
        groups => {
            type => 'keyTextContainer',
            help =>
949
              'exportedvars.html#extend_variables_using_macros_and_groups',
950 951 952 953 954 955 956
            test          => $perlExpr,
            default       => {},
            documentation => 'Groups',
        },
        macros => {
            type => 'keyTextContainer',
            help =>
957
              'exportedvars.html#extend_variables_using_macros_and_groups',
958
            keyTest       => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
959
            keyMsgFail    => '__badMacroName__',
960 961 962 963 964 965 966 967 968 969
            test          => $perlExpr,
            default       => {},
            documentation => 'Macros',
        },

        # Storage
        globalStorage => {
            type          => 'PerlModule',
            default       => 'Apache::Session::File',
            documentation => 'Session backend module',
Xavier Guimard's avatar
Xavier Guimard committed
970
            flags         => 'hp',
971 972 973 974 975 976 977
        },
        globalStorageOptions => {
            type    => 'keyTextContainer',
            default => {
                'Directory'     => '/var/lib/lemonldap-ng/sessions/',
                'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/',
                'generateModule' =>
978
                  'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
979 980
            },
            documentation => 'Session backend module options',
Xavier Guimard's avatar
Xavier Guimard committed
981
            flags         => 'hp',
982 983
        },
        localSessionStorage => {
Xavier Guimard's avatar
Xavier Guimard committed
984 985
            type          => 'PerlModule',
            default       => 'Cache::FileCache',
Xavier Guimard's avatar
Xavier Guimard committed
986
            documentation => 'Local sessions cache module',
987 988 989 990 991 992 993 994 995 996 997 998 999 1000
        },
        localSessionStorageOptions => {
            type    => 'keyTextContainer',
            default => {
                'namespace'          => 'lemonldap-ng-sessions',
                'default_expires_in' => 600,
                'directory_umask'    => '007',
                'cache_root'         => '/tmp',
                'cache_depth'        => 3,
            },
            documentation => 'Sessions cache module options',
        },

        # Persistent storage
Xavier Guimard's avatar
Xavier Guimard committed
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
        persistentStorage => {
            type          => 'PerlModule',
            documentation => 'Storage module for persistent sessions'
        },
        persistentStorageOptions => {
            type          => 'keyTextContainer',
            documentation => 'Options for persistent sessions storage module'
        },
        sessionDataToRemember => {
            type          => 'keyTextContainer',
            keyTest       => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
            keyMsgFail    => '__invalidSessionData__',
            documentation => 'Data to remember in login history',
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
        },

        # SAML issuer
        issuerDBSAMLActivation => {
            default       => 0,
            type          => 'bool',
            documentation => 'SAML IDP activation',
        },
        issuerDBSAMLPath => {
            type          => 'pcre',
            default       => '^/saml/',
            documentation => 'SAML IDP request path',
        },
        issuerDBSAMLRule => {
            type          => 'boolOrExpr',
            default       => 1,
            documentation => 'SAML IDP rule',
        },

        # OpenID-Connect issuer
        issuerDBOpenIDConnectActivation => {
            type          => 'bool',
1036
            default       => 0,
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
            documentation => 'OpenID Connect server activation',
        },
        issuerDBOpenIDConnectPath => {
            type          => 'text',
            default       => '^/oauth2/',
            documentation => 'OpenID Connect server request path',
        },
        issuerDBOpenIDConnectRule => {
            type          => 'boolOrExpr',
            default       => 1,
            documentation => 'OpenID Connect server rule',
        },

1050 1051 1052
        # GET issuer
        issuerDBGetActivation => {
            type          => 'bool',
1053
            default       => 0,
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
            documentation => 'Get issuer activation',
        },
        issuerDBGetPath => {
            type          => 'text',
            default       => '^/get/',
            documentation => 'Get issuer request path',
        },
        issuerDBGetRule => {
            type          => 'boolOrExpr',
            default       => 1,
            documentation => 'Get issuer rule',
        },
        issuerDBGetParameters => {
            type       => 'doubleHash',
            default    => {},
            keyTest    => qr/^$Regexp::Common::URI::RFC2396::hostname$/,
            keyMsgFail => '__badHostname__',
            test       => {
                keyTest    => qr/^(?=[^\-])[\w\-]+(?<=[^-])$/,
                keyMsgFail => '__badKeyName__',
                test       => sub {
                    my ( $val, $conf ) = @_;
1076
                    return 1
1077
                      if ( defined $conf->{macros}->{$val}
1078 1079 1080
                        or $val eq '_timezone' );
                    foreach ( keys %$conf ) {
                        return 1
1081
                          if ( $_ =~ /exportedvars$/i
1082
                            and defined $conf->{$_}->{$val} );
1083
                    }
1084
                    return ( 1, "__unknownAttrOrMacro__: $val" );
1085 1086 1087 1088 1089
                },
            },
            documentation => 'List of virtualHosts with their get parameters',
        },

1090 1091 1092 1093 1094 1095 1096
        # Password
        mailOnPasswordChange => {
            default       => 0,
            type          => 'bool',
            documentation => 'Send a mail when password is changed',
        },
        portalRequireOldPassword => {
1097 1098 1099
            default       => 1,
            type          => 'bool',
            documentation => 'Old password is required to change the password',
1100 1101 1102 1103 1104 1105 1106
        },
        hideOldPassword => {
            default       => 0,
            type          => 'bool',
            documentation => 'Hide old password in portal',
        },

1107
        # SMTP server
1108
        SMTPServer => {
1109 1110
            type    => 'text',
            default => '',
1111
            test    => qr/^(?:$Regexp::Common::URI::RFC2396::host(?::\d+)?)?$/,
1112 1113
            documentation => 'SMTP Server',
        },
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
        SMTPPort => {
            type          => 'int',
            documentation => 'Fix SMTP port',
        },
        SMTPTLS => {
            type    => 'select',
            default => '',
            select  => [
                { k => '',         v => 'none' },
                { k => 'starttls', v => 'SMTP + STARTTLS' },
                { k => 'ssl',      v => 'SMTPS' },
            ],
            documentation => 'TLS protocol to use with SMTP',
        },
        SMTPTLSOpts => {
            type          => 'keyTextContainer',
            documentation => 'TLS/SSL options for SMTP',
        },
Xavier Guimard's avatar
Xavier Guimard committed
1132 1133 1134 1135 1136 1137 1138 1139
        SMTPAuthUser => {
            type          => 'text',
            documentation => 'Login to use to send mails',
        },
        SMTPAuthPass => {
            type          => 'password',
            documentation => 'Password to use to send mails',
        },
1140

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
        # Mails
        mailCharset => {
            type          => 'text',
            default       => 'utf-8',
            documentation => 'Mail charset',
        },
        mailFrom => {
            type          => 'text',
            default       => 'noreply@example.com',
            documentation => 'Sender email',
        },
        mailSessionKey => {
            type          => 'text',
            default       => 'mail',
            documentation => 'Session parameter where mail is stored',
        },
1157
        mailReplyTo => { type => 'text', documentation => 'Reply-To address' },
1158
        mailTimeout => {
1159 1160
            type          => 'int',
            default       => 0,
1161
            documentation => 'Mail password reset session timeout',
1162 1163 1164
        },

        # Password reset
1165 1166 1167 1168
        mailBody => {
            type          => 'longtext',
            documentation => 'Custom password reset mail body',
        },
1169

1170
        mailConfirmBody => {
1171
            type          => 'longtext',
1172
            documentation => 'Custom confirm password reset mail body',
1173
        },
1174
        mailConfirmSubject => {
1175 1176 1177
            type          => 'text',
            documentation => 'Mail subject for reset confirmation',
        },
1178
        mailSubject => {
1179 1180 1181 1182
            type          => 'text',
            documentation => 'Mail subject for new password email',
        },

1183
        mailUrl => {
1184 1185 1186 1187 1188
            type          => 'url',
            default       => 'http://auth.example.com/resetpwd',
            documentation => 'URL of password reset page',
        },

1189 1190 1191 1192 1193 1194 1195 1196
        # Registration
        registerConfirmSubject => {
            type          => 'text',
            documentation => 'Mail subject for register confirmation',
        },
        registerDB => {
            type   => 'select',
            select => [
1197 1198 1199 1200 1201
                { k => 'AD',     v => 'Active Directory' },
                { k => 'Demo',   v => 'Demonstration' },
                { k => 'LDAP',   v => 'LDAP' },
                { k => 'Null',   v => 'None' },
                { k => 'Custom', v => 'customModule' },
1202
            ],
1203
            default       => 'Null',
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
            documentation => 'Register module',
        },
        registerDoneSubject => {
            type          => 'text',
            documentation => 'Mail subject when register is done',
        },
        registerTimeout => {
            default       => 0,
            type          => 'int',
            documentation => 'Register session timeout',
        },
1215 1216
        registerUrl => {
            type          => 'text',
1217
            default       => 'http://auth.example.com/register',
1218 1219
            documentation => 'URL of register page',
        },
1220

1221 1222 1223
        # Upgrade session
        upgradeSession => {
            type          => 'bool',
1224
            default       => 1,
1225 1226
            documentation => 'Upgrade session activation',
        },
1227

1228 1229 1230 1231
        # 2F
        max2FDevices => {
            default       => 10,
            type          => 'int',
1232
            documentation => 'Maximum registered 2F devices',
1233 1234 1235 1236
        },
        max2FDevicesNameLength => {
            default       => 20,
            type          => 'int',
1237
            documentation => 'Maximum 2F devices name length',
1238
        },
1239

Xavier Guimard's avatar
Xavier Guimard committed
1240 1241
        # U2F
        u2fActivation => {
1242
            type          => 'boolOrExpr',
Xavier Guimard's avatar
Xavier Guimard committed
1243 1244 1245
            default       => 0,
            documentation => 'U2F activation',
        },
1246
        u2fSelfRegistration => {
1247
            type          => 'boolOrExpr',
1248 1249
            default       => 0,
            documentation => 'U2F self registration activation',
Xavier Guimard's avatar
Xavier Guimard committed
1250
        },
1251 1252 1253
        u2fAuthnLevel => {
            type => 'int',
            documentation =>
1254
              'Authentication level for users authentified by password+U2F'
1255
        },
1256 1257 1258 1259 1260
        u2fUserCanRemoveKey => {
            type          => 'bool',
            default       => 1,
            documentation => 'Authorize users to remove existing U2F key',
        },
Xavier Guimard's avatar
Xavier Guimard committed
1261

1262 1263 1264 1265 1266 1267
        # TOTP second factor
        totp2fActivation => {
            type          => 'boolOrExpr',
            default       => 0,
            documentation => 'TOTP activation',
        },
1268
        totp2fSelfRegistration => {
1269
            type          => 'boolOrExpr',
1270 1271 1272 1273 1274 1275
            default       => 0,
            documentation => 'TOTP self registration activation',
        },
        totp2fAuthnLevel => {
            type => 'int',
            documentation =>
1276
              'Authentication level for users authentified by password+TOTP'
1277
        },
Xavier Guimard's avatar
Xavier Guimard committed
1278 1279 1280 1281
        totp2fIssuer => {
            type          => 'text',
            documentation => 'TOTP Issuer',
        },
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
        totp2fInterval => {
            type          => 'int',
            default       => 30,
            documentation => 'TOTP interval',
        },
        totp2fRange => {
            type          => 'int',
            default       => 1,
            documentation => 'TOTP range (number of interval to test)',
        },
Xavier Guimard's avatar
Xavier Guimard committed
1292 1293 1294 1295 1296
        totp2fDigits => {
            type          => 'int',
            default       => 6,
            documentation => 'Number of digits for TOTP code',
        },
1297 1298 1299 1300
        totp2fDisplayExistingSecret => {
            type    => 'bool',
            default => 0,
            documentation =>
1301
              'Display existing TOTP secret in registration form',
1302 1303 1304 1305 1306 1307
        },
        totp2fUserCanChangeKey => {
            type          => 'bool',
            default       => 0,
            documentation => 'Authorize users to change existing TOTP secret',
        },
1308 1309 1310 1311 1312
        totp2fUserCanRemoveKey => {
            type          => 'bool',
            default       => 1,
            documentation => 'Authorize users to remove existing TOTP secret',
        },
1313

1314 1315 1316 1317 1318 1319 1320 1321 1322
        # UTOTP 2F
        utotp2fActivation => {
            type          => 'boolOrExpr',
            default       => 0,
            documentation => 'UTOTP activation (mixed U2F/TOTP module)',
        },
        utotp2fAuthnLevel => {
            type => 'int',
            documentation =>
1323
'Authentication level for users authentified by password+(U2F or TOTP)'
1324 1325
        },

1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
        # Mail second factor
        mail2fActivation => {
            type          => 'boolOrExpr',
            default       => 0,
            documentation => 'Mail second factor activation',
        },
        mail2fSubject => {
            type          => 'text',
            documentation => 'Mail subject for second factor authentication',
        },
        mail2fBody => {
            type          => 'longtext',
            documentation => 'Mail body for second factor authentication',
        },
        mail2fCodeRegex => {
            type          => 'pcre',
1342
            default       => '\d{6}',
1343 1344 1345 1346 1347 1348 1349 1350 1351
            documentation => 'Regular expression to create a mail OTP code',
        },
        mail2fTimeout => {
            type          => 'int',
            documentation => 'Second factor code timeout',
        },
        mail2fAuthnLevel => {
            type => 'int',
            documentation =>
1352
'Authentication level for users authenticated by Mail second factor'
1353 1354 1355 1356 1357 1358
        },
        mail2fLogo => {
            type          => 'text',
            documentation => 'Custom logo for Mail 2F',
        },

1359 1360
        # External second factor
        ext2fActivation => {
1361
            type          => 'boolOrExpr',
1362 1363 1364
            default       => 0,
            documentation => 'External second factor activation',
        },
1365
        ext2fCodeActivation => {
1366
            type          => 'pcre',
1367 1368 1369
            default       => '\d{6}',
            documentation => 'OTP generated by Portal',
        },
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
        ext2FSendCommand => {
            type          => 'text',
            documentation => 'Send command of External second factor',
        },
        ext2FValidateCommand => {
            type          => 'text',
            documentation => 'Validation command of External second factor',
        },
        ext2fAuthnLevel => {
            type => 'int',
            documentation =>
1381
'Authentication level for users authentified by External second factor'
1382
        },
Xavier Guimard's avatar
Xavier Guimard committed
1383 1384 1385 1386
        ext2fLogo => {
            type          => 'text',
            documentation => 'Custom logo for External 2F',
        },
1387

1388 1389 1390 1391 1392 1393 1394
        #  REST External second factor
        rest2fActivation => {
            type          => 'boolOrExpr',
            default       => 0,
            documentation => 'REST second factor activation',
        },
        rest2fInitUrl => {
Xavier Guimard's avatar
Xavier Guimard committed
1395
            type          => 'url',
1396 1397 1398
            documentation => 'REST 2F init URL',
        },
        rest2fInitArgs => {
Xavier Guimard's avatar
Xavier Guimard committed
1399 1400 1401 1402 1403
            type          => 'keyTextContainer',
            keyTest       => qr/^\w+$/,
            keyMsgFail    => '__badKeyName__',
            test          => qr/^\w+$/,
            msgFail       => '__badValue__',
1404 1405 1406
            documentation => 'Args for REST 2F init',
        },
        rest2fVerifyUrl => {
Xavier Guimard's avatar
Xavier Guimard committed
1407 1408 1409 1410 1411
            type          => 'url',
            keyTest       => qr/^\w+$/,
            keyMsgFail    => '__badKeyName__',
            test          => qr/^\w+$/,
            msgFail       => '__badValue__',
1412 1413 1414
            documentation => 'REST 2F init URL',
        },
        rest2fVerifyArgs => {
Xavier Guimard's avatar
Xavier Guimard committed
1415
            type          => 'keyTextContainer',
1416 1417 1418 1419 1420
            documentation => 'Args for REST 2F init',
        },
        rest2fAuthnLevel => {
            type => 'int',
            documentation =>
1421
'Authentication level for users authentified by REST second factor'
1422
        },
Xavier Guimard's avatar
Xavier Guimard committed
1423 1424 1425 1426
        rest2fLogo => {
            type          => 'text',
            documentation => 'Custom logo for REST 2F',