make tidy
This commit is contained in:
parent
f43139321c
commit
c5f9a7f95b
|
@ -541,9 +541,10 @@ sub authChoiceModules {
|
|||
my @res;
|
||||
foreach my $k ( sort keys %$value ) {
|
||||
my $data = [ split /;/, $value->{$k} ];
|
||||
eval {$data->[5] = from_json($data->[5]) if $data->[5] };
|
||||
if($@){
|
||||
$self->logger->error("Bad value in choice over parameters, deleted ($@)");
|
||||
eval { $data->[5] = from_json( $data->[5] ) if $data->[5] };
|
||||
if ($@) {
|
||||
$self->logger->error(
|
||||
"Bad value in choice over parameters, deleted ($@)");
|
||||
}
|
||||
push @res,
|
||||
{
|
||||
|
|
|
@ -107,7 +107,7 @@ sub statusInit {
|
|||
exec $perl_exec, '-MLemonldap::NG::Handler::Lib::Status',
|
||||
|
||||
# Insert @INC in Perl path
|
||||
map( { "-I$_" } @INC ),
|
||||
map( {"-I$_"} @INC ),
|
||||
|
||||
# Command to launch
|
||||
'-e', '&Lemonldap::NG::Handler::Lib::Status::run()',
|
||||
|
|
|
@ -21,7 +21,7 @@ ok(
|
|||
cookieName => 'lemonldap',
|
||||
securedCookie => 0,
|
||||
https => 0,
|
||||
userLogger => 'Lemonldap::NG::Common::Logger::Null',
|
||||
userLogger => 'Lemonldap::NG::Common::Logger::Null',
|
||||
}
|
||||
),
|
||||
'initialization'
|
||||
|
|
|
@ -29,7 +29,7 @@ sub init {
|
|||
cookieName => 'lemonldap',
|
||||
securedCookie => 0,
|
||||
https => 0,
|
||||
logger => 'Lemonldap::NG::Common::Logger::Std',
|
||||
logger => 'Lemonldap::NG::Common::Logger::Std',
|
||||
%$prms
|
||||
);
|
||||
ok(
|
||||
|
|
|
@ -8,17 +8,17 @@ sub types {
|
|||
'array' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'authParamsText' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'blackWhiteList' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'bool' => {
|
||||
'msgFail' => '__notABoolean__',
|
||||
|
@ -36,17 +36,17 @@ sub types {
|
|||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'catAndAppList' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'file' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'hostname' => {
|
||||
'form' => 'text',
|
||||
|
@ -80,48 +80,48 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
if $_ =~ /exportedvars$/i and defined $conf->{$_}{$val};
|
||||
}
|
||||
return 1, "__unknownAttrOrMacro__: $val";
|
||||
}
|
||||
}
|
||||
},
|
||||
'longtext' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'menuApp' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'menuCat' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcmetadatajson' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcmetadatajwks' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcOPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcRPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'password' => {
|
||||
'msgFail' => '__malformedValue__',
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'pcre' => {
|
||||
'form' => 'text',
|
||||
|
@ -132,7 +132,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
}
|
||||
};
|
||||
return $@ ? ( 0, "__badRegexp__: $@" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'PerlModule' => {
|
||||
'form' => 'text',
|
||||
|
@ -142,17 +142,17 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
'portalskin' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'portalskinbackground' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'post' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPrivateKey' => {
|
||||
'test' => sub {
|
||||
|
@ -160,7 +160,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\r\n]*)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPublicKey' => {
|
||||
'test' => sub {
|
||||
|
@ -168,7 +168,7 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\n
|
|||
m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+PUBLIC\s+KEY\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPublicKeyOrCertificate' => {
|
||||
'test' => sub {
|
||||
|
@ -176,37 +176,37 @@ m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\
|
|||
m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'rule' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlAssertion' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlAttribute' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlIDPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlService' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlSPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'select' => {
|
||||
'test' => sub {
|
||||
|
@ -216,19 +216,19 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\
|
|||
return $test
|
||||
? 1
|
||||
: ( 1, "Invalid value '$_[0]' for this select" );
|
||||
}
|
||||
}
|
||||
},
|
||||
'subContainer' => {
|
||||
'keyTest' => qr/\w/,
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'text' => {
|
||||
'msgFail' => '__malformedValue__',
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'trool' => {
|
||||
'msgFail' => '__authorizedValues__: -1, 0, 1',
|
||||
|
@ -1050,7 +1050,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
|
@ -1219,7 +1219,7 @@ qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-
|
|||
and defined $conf->{$_}{$val};
|
||||
}
|
||||
return 1, "__unknownAttrOrMacro__: $val";
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'doubleHash'
|
||||
},
|
||||
|
@ -1502,7 +1502,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'ruleContainer'
|
||||
},
|
||||
|
|
|
@ -29,15 +29,15 @@ sub types {
|
|||
|
||||
# Simple text types
|
||||
text => {
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
msgFail => '__malformedValue__',
|
||||
},
|
||||
password => {
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
msgFail => '__malformedValue__',
|
||||
},
|
||||
longtext => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
url => {
|
||||
form => 'text',
|
||||
|
@ -57,7 +57,7 @@ sub types {
|
|||
pcre => {
|
||||
form => 'text',
|
||||
test => sub {
|
||||
eval {qr/$_[0]/};
|
||||
eval { qr/$_[0]/ };
|
||||
return $@ ? ( 0, "__badRegexp__: $@" ) : (1);
|
||||
},
|
||||
},
|
||||
|
@ -66,11 +66,11 @@ sub types {
|
|||
test => sub {
|
||||
my ( $val, $conf ) = @_;
|
||||
return 1
|
||||
if ( defined $conf->{macros}->{$val}
|
||||
if ( defined $conf->{macros}->{$val}
|
||||
or $val eq '_timezone' );
|
||||
foreach ( keys %$conf ) {
|
||||
return 1
|
||||
if ( $_ =~ /exportedvars$/i
|
||||
if ( $_ =~ /exportedvars$/i
|
||||
and defined $conf->{$_}->{$val} );
|
||||
}
|
||||
return ( 1, "__unknownAttrOrMacro__: $val" );
|
||||
|
@ -102,27 +102,27 @@ sub types {
|
|||
},
|
||||
subContainer => {
|
||||
keyTest => qr/\w/,
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
},
|
||||
select => {
|
||||
test => sub {
|
||||
my $test = grep ( { $_ eq $_[0] }
|
||||
map ( { $_->{k} } @{ $_[2]->{select} } ) );
|
||||
return $test
|
||||
? 1
|
||||
: ( 1, "Invalid value '$_[0]' for this select" );
|
||||
? 1
|
||||
: ( 1, "Invalid value '$_[0]' for this select" );
|
||||
},
|
||||
},
|
||||
|
||||
# Files type (long text)
|
||||
file => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
RSAPublicKey => {
|
||||
test => sub {
|
||||
return (
|
||||
$_[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
|
||||
$_[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
|
||||
? (1)
|
||||
: ( 1, '__badPemEncoding__' )
|
||||
);
|
||||
|
@ -131,8 +131,8 @@ sub types {
|
|||
'RSAPublicKeyOrCertificate' => {
|
||||
'test' => sub {
|
||||
return (
|
||||
$_[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
|
||||
$_[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
|
||||
? (1)
|
||||
: ( 1, '__badPemEncoding__' )
|
||||
);
|
||||
|
@ -141,8 +141,8 @@ sub types {
|
|||
RSAPrivateKey => {
|
||||
test => sub {
|
||||
return (
|
||||
$_[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
|
||||
$_[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
|
||||
? (1)
|
||||
: ( 1, '__badPemEncoding__' )
|
||||
);
|
||||
|
@ -150,13 +150,13 @@ sub types {
|
|||
},
|
||||
|
||||
authParamsText => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
blackWhiteList => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
catAndAppList => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
keyText => {
|
||||
keyTest => qr/^[a-zA-Z0-9_]+$/,
|
||||
|
@ -164,52 +164,52 @@ sub types {
|
|||
msgFail => '__badValue__',
|
||||
},
|
||||
menuApp => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
menuCat => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
oidcOPMetaDataNode => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
oidcRPMetaDataNode => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
oidcmetadatajson => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
oidcmetadatajwks => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
portalskin => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
portalskinbackground => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
post => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
rule => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
samlAssertion => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
samlAttribute => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
samlIDPMetaDataNode => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
samlSPMetaDataNode => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
samlService => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
array => {
|
||||
test => sub {1}
|
||||
test => sub { 1 }
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ sub attributes {
|
|||
checkTime => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Timeout to check new configuration in local cache',
|
||||
'Timeout to check new configuration in local cache',
|
||||
default => 600,
|
||||
flags => 'hp',
|
||||
},
|
||||
|
@ -229,7 +229,7 @@ sub attributes {
|
|||
type => 'array',
|
||||
documentation => 'Alterable session keys by user itself',
|
||||
default =>
|
||||
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
|
||||
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
|
||||
},
|
||||
configStorage => {
|
||||
type => 'text',
|
||||
|
@ -252,14 +252,12 @@ sub attributes {
|
|||
documentation => 'Enable Cross Domain Authentication',
|
||||
},
|
||||
cfgAuthor => {
|
||||
type => 'text',
|
||||
documentation =>
|
||||
'Name of the author of the current configuration',
|
||||
type => 'text',
|
||||
documentation => 'Name of the author of the current configuration',
|
||||
},
|
||||
cfgAuthorIP => {
|
||||
type => 'text',
|
||||
documentation =>
|
||||
'Uploader IP address of the current configuration',
|
||||
type => 'text',
|
||||
documentation => 'Uploader IP address of the current configuration',
|
||||
},
|
||||
cfgDate => {
|
||||
type => 'int',
|
||||
|
@ -281,7 +279,7 @@ sub attributes {
|
|||
confirmFormMethod => {
|
||||
type => "select",
|
||||
select =>
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
default => 'post',
|
||||
documentation => 'HTTP method for confirm page form',
|
||||
},
|
||||
|
@ -301,7 +299,7 @@ sub attributes {
|
|||
infoFormMethod => {
|
||||
type => "select",
|
||||
select =>
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
default => 'get',
|
||||
documentation => 'HTTP method for info page form',
|
||||
},
|
||||
|
@ -316,11 +314,10 @@ sub attributes {
|
|||
documentation => 'Use javascript for redirections',
|
||||
},
|
||||
logoutServices => {
|
||||
type => 'keyTextContainer',
|
||||
help => 'logoutforward.html',
|
||||
default => {},
|
||||
documentation =>
|
||||
'Send logout trough GET request to these services',
|
||||
type => 'keyTextContainer',
|
||||
help => 'logoutforward.html',
|
||||
default => {},
|
||||
documentation => 'Send logout trough GET request to these services',
|
||||
},
|
||||
maintenance => {
|
||||
default => 0,
|
||||
|
@ -358,24 +355,24 @@ sub attributes {
|
|||
default => '_user',
|
||||
help => 'monitoring.html',
|
||||
documentation =>
|
||||
'Session parameter to display connected user in portal',
|
||||
'Session parameter to display connected user in portal',
|
||||
},
|
||||
redirectFormMethod => {
|
||||
type => "select",
|
||||
select =>
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
|
||||
default => 'get',
|
||||
documentation => 'HTTP method for redirect page form',
|
||||
},
|
||||
reloadUrls => {
|
||||
type => 'keyTextContainer',
|
||||
help => 'configlocation.html#configuration_reload',
|
||||
keyTest => qr/^$Regexp::Common::URI::RFC2396::host(?::\d+)?$/,
|
||||
test => $url,
|
||||
msgFail => '__badUrl__',
|
||||
type => 'keyTextContainer',
|
||||
help => 'configlocation.html#configuration_reload',
|
||||
keyTest => qr/^$Regexp::Common::URI::RFC2396::host(?::\d+)?$/,
|
||||
test => $url,
|
||||
msgFail => '__badUrl__',
|
||||
documentation => 'URL to call on reload',
|
||||
},
|
||||
portalMainLogo => {
|
||||
portalMainLogo => {
|
||||
type => 'text',
|
||||
default => 'common/logos/logo_llng_400px.png',
|
||||
documentation => 'Portal main logo path',
|
||||
|
@ -407,7 +404,7 @@ sub attributes {
|
|||
skipRenewConfirmation => {
|
||||
type => 'bool',
|
||||
documentation =>
|
||||
'Avoid asking confirmation when an Issuer asks to renew auth',
|
||||
'Avoid asking confirmation when an Issuer asks to renew auth',
|
||||
},
|
||||
|
||||
# Loggers (ini only)
|
||||
|
@ -449,9 +446,9 @@ sub attributes {
|
|||
|
||||
# Manager or PSGI protected apps
|
||||
protection => {
|
||||
type => 'text',
|
||||
test => qr/^(?:none|authenticate|manager|)$/,
|
||||
msgFail => '__authorizedValues__: none authenticate manager',
|
||||
type => 'text',
|
||||
test => qr/^(?:none|authenticate|manager|)$/,
|
||||
msgFail => '__authorizedValues__: none authenticate manager',
|
||||
documentation => 'Manager protection method',
|
||||
flags => 'hm',
|
||||
},
|
||||
|
@ -467,8 +464,7 @@ sub attributes {
|
|||
keyTest => qr/\w/,
|
||||
help => 'portalmenu.html#categories_and_applications',
|
||||
default => {
|
||||
default =>
|
||||
{ catname => 'Default category', type => "category" }
|
||||
default => { catname => 'Default category', type => "category" }
|
||||
},
|
||||
documentation => 'Applications list',
|
||||
},
|
||||
|
@ -481,7 +477,7 @@ sub attributes {
|
|||
type => 'bool',
|
||||
default => 0,
|
||||
documentation =>
|
||||
'Show error if mail is not found in password reset process',
|
||||
'Show error if mail is not found in password reset process',
|
||||
},
|
||||
portalOpenLinkInNewWindow => {
|
||||
type => 'bool',
|
||||
|
@ -504,22 +500,26 @@ sub attributes {
|
|||
documentation => 'Background image of portal skin',
|
||||
select => [
|
||||
{ k => "", v => 'None' },
|
||||
{ k => "1280px-Anse_Source_d'Argent_2-La_Digue.jpg",
|
||||
{
|
||||
k => "1280px-Anse_Source_d'Argent_2-La_Digue.jpg",
|
||||
v => 'Anse'
|
||||
},
|
||||
{ k =>
|
||||
"1280px-Autumn-clear-water-waterfall-landscape_-_Virginia_-_ForestWander.jpg",
|
||||
{
|
||||
k =>
|
||||
"1280px-Autumn-clear-water-waterfall-landscape_-_Virginia_-_ForestWander.jpg",
|
||||
v => 'Waterfall'
|
||||
},
|
||||
{ k => "1280px-BrockenSnowedTrees.jpg", v => 'Snowed Trees' },
|
||||
{ k =>
|
||||
"1280px-Cedar_Breaks_National_Monument_partially.jpg",
|
||||
{
|
||||
k => "1280px-Cedar_Breaks_National_Monument_partially.jpg",
|
||||
v => 'National Monument'
|
||||
},
|
||||
{ k => "1280px-Parry_Peak_from_Winter_Park.jpg",
|
||||
{
|
||||
k => "1280px-Parry_Peak_from_Winter_Park.jpg",
|
||||
v => 'Winter'
|
||||
},
|
||||
{ k => "Aletschgletscher_mit_Pinus_cembra1.jpg",
|
||||
{
|
||||
k => "Aletschgletscher_mit_Pinus_cembra1.jpg",
|
||||
v => 'Pinus'
|
||||
},
|
||||
],
|
||||
|
@ -565,13 +565,13 @@ sub attributes {
|
|||
default => 0,
|
||||
type => 'bool',
|
||||
documentation =>
|
||||
'Enable force to authenticate when displaying portal',
|
||||
'Enable force to authenticate when displaying portal',
|
||||
},
|
||||
portalForceAuthnInterval => {
|
||||
default => 5,
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Maximun interval in seconds since last authentifcation to force reauthentication',
|
||||
'Maximun interval in seconds since last authentifcation to force reauthentication',
|
||||
},
|
||||
bruteForceProtection => {
|
||||
default => 0,
|
||||
|
@ -582,18 +582,18 @@ sub attributes {
|
|||
default => 30,
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Brute force attack protection -> Tempo before try again',
|
||||
'Brute force attack protection -> Tempo before try again',
|
||||
},
|
||||
bruteForceProtectionMaxAge => {
|
||||
default => 300,
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Brute force attack protection -> Max age third failed login',
|
||||
'Brute force attack protection -> Max age third failed login',
|
||||
},
|
||||
grantSessionRules => {
|
||||
type => 'grantContainer',
|
||||
keyTest => $perlExpr,
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
documentation => 'Rules to grant sessions',
|
||||
},
|
||||
hiddenAttributes => {
|
||||
|
@ -629,7 +629,7 @@ sub attributes {
|
|||
type => 'text',
|
||||
default => "'self'",
|
||||
documentation =>
|
||||
'Authorizated Ajax destination for Content-Security-Policy',
|
||||
'Authorizated Ajax destination for Content-Security-Policy',
|
||||
},
|
||||
cspFont => {
|
||||
type => 'text',
|
||||
|
@ -652,7 +652,7 @@ sub attributes {
|
|||
documentation => 'Regular expression to create a random password',
|
||||
},
|
||||
trustedDomains =>
|
||||
{ type => 'text', documentation => 'Trusted domains', },
|
||||
{ type => 'text', documentation => 'Trusted domains', },
|
||||
storePassword => {
|
||||
default => 0,
|
||||
type => 'bool',
|
||||
|
@ -788,10 +788,10 @@ sub attributes {
|
|||
flags => 'hp',
|
||||
},
|
||||
domain => {
|
||||
type => 'text',
|
||||
test => qr/^(?:$Regexp::Common::URI::RFC2396::hostname)?$/,
|
||||
msgFail => '__badDomainName__',
|
||||
default => 'example.com',
|
||||
type => 'text',
|
||||
test => qr/^(?:$Regexp::Common::URI::RFC2396::hostname)?$/,
|
||||
msgFail => '__badDomainName__',
|
||||
default => 'example.com',
|
||||
documentation => 'DNS domain',
|
||||
flags => 'hp',
|
||||
},
|
||||
|
@ -886,7 +886,7 @@ sub attributes {
|
|||
groups => {
|
||||
type => 'keyTextContainer',
|
||||
help =>
|
||||
'exportedvars.html#extend_variables_using_macros_and_groups',
|
||||
'exportedvars.html#extend_variables_using_macros_and_groups',
|
||||
test => $perlExpr,
|
||||
default => {},
|
||||
documentation => 'Groups',
|
||||
|
@ -894,7 +894,7 @@ sub attributes {
|
|||
macros => {
|
||||
type => 'keyTextContainer',
|
||||
help =>
|
||||
'exportedvars.html#extend_variables_using_macros_and_groups',
|
||||
'exportedvars.html#extend_variables_using_macros_and_groups',
|
||||
keyTest => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
|
||||
keyMsgFail => '__badMacroName__',
|
||||
test => $perlExpr,
|
||||
|
@ -915,7 +915,7 @@ sub attributes {
|
|||
'Directory' => '/var/lib/lemonldap-ng/sessions/',
|
||||
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/',
|
||||
'generateModule' =>
|
||||
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
|
||||
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
|
||||
},
|
||||
documentation => 'Session backend module options',
|
||||
flags => 'hp',
|
||||
|
@ -1014,11 +1014,11 @@ sub attributes {
|
|||
test => sub {
|
||||
my ( $val, $conf ) = @_;
|
||||
return 1
|
||||
if ( defined $conf->{macros}->{$val}
|
||||
if ( defined $conf->{macros}->{$val}
|
||||
or $val eq '_timezone' );
|
||||
foreach ( keys %$conf ) {
|
||||
return 1
|
||||
if ( $_ =~ /exportedvars$/i
|
||||
if ( $_ =~ /exportedvars$/i
|
||||
and defined $conf->{$_}->{$val} );
|
||||
}
|
||||
return ( 1, "__unknownAttrOrMacro__: $val" );
|
||||
|
@ -1034,10 +1034,9 @@ sub attributes {
|
|||
documentation => 'Send a mail when password is changed',
|
||||
},
|
||||
portalRequireOldPassword => {
|
||||
default => 1,
|
||||
type => 'bool',
|
||||
documentation =>
|
||||
'Old password is required to change the password',
|
||||
default => 1,
|
||||
type => 'bool',
|
||||
documentation => 'Old password is required to change the password',
|
||||
},
|
||||
hideOldPassword => {
|
||||
default => 0,
|
||||
|
@ -1047,7 +1046,7 @@ sub attributes {
|
|||
|
||||
# Mails
|
||||
mailBody =>
|
||||
{ type => 'longtext', documentation => 'Custom mail body', },
|
||||
{ type => 'longtext', documentation => 'Custom mail body', },
|
||||
mailCharset => {
|
||||
type => 'text',
|
||||
default => 'utf-8',
|
||||
|
@ -1066,8 +1065,7 @@ sub attributes {
|
|||
default => 'noreply@example.com',
|
||||
documentation => 'Sender email',
|
||||
},
|
||||
mailReplyTo =>
|
||||
{ type => 'text', documentation => 'Reply-To address' },
|
||||
mailReplyTo => { type => 'text', documentation => 'Reply-To address' },
|
||||
mailSessionKey => {
|
||||
type => 'text',
|
||||
default => 'mail',
|
||||
|
@ -1090,7 +1088,7 @@ sub attributes {
|
|||
SMTPServer => {
|
||||
type => 'text',
|
||||
default => '',
|
||||
test => qr/^(?:$Regexp::Common::URI::RFC2396::host(?::\d+)?)?$/,
|
||||
test => qr/^(?:$Regexp::Common::URI::RFC2396::host(?::\d+)?)?$/,
|
||||
documentation => 'SMTP Server',
|
||||
},
|
||||
SMTPPort => {
|
||||
|
@ -1185,7 +1183,7 @@ sub attributes {
|
|||
u2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by password+U2F'
|
||||
'Authentication level for users authentified by password+U2F'
|
||||
},
|
||||
u2fUserCanRemoveKey => {
|
||||
type => 'bool',
|
||||
|
@ -1207,7 +1205,7 @@ sub attributes {
|
|||
totp2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by password+TOTP'
|
||||
'Authentication level for users authentified by password+TOTP'
|
||||
},
|
||||
totp2fIssuer => {
|
||||
type => 'text',
|
||||
|
@ -1232,7 +1230,7 @@ sub attributes {
|
|||
type => 'bool',
|
||||
default => 0,
|
||||
documentation =>
|
||||
'Display existing TOTP secret in registration form',
|
||||
'Display existing TOTP secret in registration form',
|
||||
},
|
||||
totp2fUserCanChangeKey => {
|
||||
type => 'bool',
|
||||
|
@ -1254,7 +1252,7 @@ sub attributes {
|
|||
utotp2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by password+(U2F or TOTP)'
|
||||
'Authentication level for users authentified by password+(U2F or TOTP)'
|
||||
},
|
||||
|
||||
# External second factor
|
||||
|
@ -1274,7 +1272,7 @@ sub attributes {
|
|||
ext2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by External second factor'
|
||||
'Authentication level for users authentified by External second factor'
|
||||
},
|
||||
ext2fLogo => {
|
||||
type => 'text',
|
||||
|
@ -1314,7 +1312,7 @@ sub attributes {
|
|||
rest2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by REST second factor'
|
||||
'Authentication level for users authentified by REST second factor'
|
||||
},
|
||||
rest2fLogo => {
|
||||
type => 'text',
|
||||
|
@ -1335,7 +1333,7 @@ sub attributes {
|
|||
yubikey2fAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation =>
|
||||
'Authentication level for users authentified by Yubikey second factor'
|
||||
'Authentication level for users authentified by Yubikey second factor'
|
||||
},
|
||||
yubikey2fClientID => {
|
||||
type => 'text',
|
||||
|
@ -1424,7 +1422,7 @@ sub attributes {
|
|||
exportedAttr => {
|
||||
type => 'text',
|
||||
documentation =>
|
||||
'List of attributes to export by SOAP or REST servers',
|
||||
'List of attributes to export by SOAP or REST servers',
|
||||
},
|
||||
wsdlServer => {
|
||||
type => 'bool',
|
||||
|
@ -1452,7 +1450,7 @@ sub attributes {
|
|||
help => 'writingrulesand_headers.html#rules',
|
||||
test => {
|
||||
keyTest => sub {
|
||||
eval {qr/$_[0]/};
|
||||
eval { qr/$_[0]/ };
|
||||
return $@ ? 0 : 1;
|
||||
},
|
||||
keyMsgFail => '__badRegexp__',
|
||||
|
@ -1461,15 +1459,15 @@ sub attributes {
|
|||
my $s = $val;
|
||||
if ( $s =~ s/^logout(?:_(?:sso|app(?:_sso)?))?\s*// ) {
|
||||
return $s =~ m{^(?:https?://.*)?$}
|
||||
? (1)
|
||||
: ( 0, '__badUrl__' );
|
||||
? (1)
|
||||
: ( 0, '__badUrl__' );
|
||||
}
|
||||
$s =~ s/\b(accept|deny|unprotect|skip)\b/1/g;
|
||||
no warnings( 'redefine', 'uninitialized' );
|
||||
eval $s;
|
||||
my $err = join( '',
|
||||
grep { $_ =~ /Undefined subroutine/ ? () : $_ }
|
||||
split( /\n/, $@ ) );
|
||||
split( /\n/, $@ ) );
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : (1);
|
||||
},
|
||||
msgFail => '__badExpression__',
|
||||
|
@ -1495,9 +1493,9 @@ sub attributes {
|
|||
eval $s;
|
||||
my $err = join( '',
|
||||
grep { $_ =~ /Undefined subroutine/ ? () : $_ }
|
||||
split( /\n/, $@ ) );
|
||||
split( /\n/, $@ ) );
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : (1);
|
||||
}
|
||||
}
|
||||
},
|
||||
documentation => 'Virtualhost headers',
|
||||
flags => 'h',
|
||||
|
@ -1505,7 +1503,7 @@ sub attributes {
|
|||
post => {
|
||||
type => 'postContainer',
|
||||
help => 'formreplay.html',
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
keyTest => qr/^(?:\*\.)?$Regexp::Common::URI::RFC2396::hostname$/,
|
||||
keyMsgFail => '__badHostname__',
|
||||
documentation => 'Virtualhost urls/Data to post',
|
||||
|
@ -1603,7 +1601,7 @@ sub attributes {
|
|||
|
||||
# CAS IDP
|
||||
casAttr =>
|
||||
{ type => 'text', documentation => 'Pivot attribute for CAS', },
|
||||
{ type => 'text', documentation => 'Pivot attribute for CAS', },
|
||||
casAttributes => {
|
||||
type => 'keyTextContainer',
|
||||
documentation => 'CAS exported attributes',
|
||||
|
@ -1762,7 +1760,7 @@ sub attributes {
|
|||
samlAttributeAuthorityDescriptorAttributeServiceSOAP => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP;'
|
||||
. '#PORTAL#/saml/AA/SOAP;',
|
||||
. '#PORTAL#/saml/AA/SOAP;',
|
||||
documentation => 'SAML Attribute Authority SOAP',
|
||||
},
|
||||
samlServicePrivateKeySig => {
|
||||
|
@ -1803,7 +1801,7 @@ sub attributes {
|
|||
type => 'bool',
|
||||
default => 0,
|
||||
documentation =>
|
||||
'Use certificate instead of public key in SAML responses',
|
||||
'Use certificate instead of public key in SAML responses',
|
||||
},
|
||||
samlIdPResolveCookie => {
|
||||
type => 'text',
|
||||
|
@ -1832,7 +1830,7 @@ sub attributes {
|
|||
type => 'int',
|
||||
default => 3,
|
||||
documentation =>
|
||||
'SAML authn context password protected transport level',
|
||||
'SAML authn context password protected transport level',
|
||||
},
|
||||
samlAuthnContextMapTLSClient => {
|
||||
type => 'int',
|
||||
|
@ -1901,45 +1899,45 @@ sub attributes {
|
|||
samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect;'
|
||||
. '#PORTAL#/saml/singleSignOn;',
|
||||
. '#PORTAL#/saml/singleSignOn;',
|
||||
documentation => 'SAML IDP SSO HTTP Redirect',
|
||||
},
|
||||
samlIDPSSODescriptorSingleSignOnServiceHTTPPost => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;'
|
||||
. '#PORTAL#/saml/singleSignOn;',
|
||||
. '#PORTAL#/saml/singleSignOn;',
|
||||
documentation => 'SAML IDP SSO HTTP POST',
|
||||
},
|
||||
samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;'
|
||||
. '#PORTAL#/saml/singleSignOnArtifact;',
|
||||
. '#PORTAL#/saml/singleSignOnArtifact;',
|
||||
documentation => 'SAML IDP SSO HTTP Artifact',
|
||||
},
|
||||
samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect;'
|
||||
. '#PORTAL#/saml/singleLogout;'
|
||||
. '#PORTAL#/saml/singleLogoutReturn',
|
||||
. '#PORTAL#/saml/singleLogout;'
|
||||
. '#PORTAL#/saml/singleLogoutReturn',
|
||||
documentation => 'SAML IDP SLO HTTP Redirect',
|
||||
},
|
||||
samlIDPSSODescriptorSingleLogoutServiceHTTPPost => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;'
|
||||
. '#PORTAL#/saml/singleLogout;'
|
||||
. '#PORTAL#/saml/singleLogoutReturn',
|
||||
. '#PORTAL#/saml/singleLogout;'
|
||||
. '#PORTAL#/saml/singleLogoutReturn',
|
||||
documentation => 'SAML IDP SLO HTTP POST',
|
||||
},
|
||||
samlIDPSSODescriptorSingleLogoutServiceSOAP => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP;'
|
||||
. '#PORTAL#/saml/singleLogoutSOAP;',
|
||||
. '#PORTAL#/saml/singleLogoutSOAP;',
|
||||
documentation => 'SAML IDP SLO SOAP',
|
||||
},
|
||||
samlIDPSSODescriptorArtifactResolutionServiceArtifact => {
|
||||
type => 'samlAssertion',
|
||||
default => '1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;'
|
||||
. '#PORTAL#/saml/artifact',
|
||||
. '#PORTAL#/saml/artifact',
|
||||
documentation => 'SAML IDP artifact resolution service',
|
||||
},
|
||||
|
||||
|
@ -1979,8 +1977,8 @@ sub attributes {
|
|||
my $res = 1;
|
||||
my %entityIds;
|
||||
foreach my $idpId ( keys %$v ) {
|
||||
unless ( $v->{$idpId}->{samlIDPMetaDataXML}
|
||||
=~ /entityID="(.+?)"/si )
|
||||
unless ( $v->{$idpId}->{samlIDPMetaDataXML} =~
|
||||
/entityID="(.+?)"/si )
|
||||
{
|
||||
push @msg, "$idpId SAML metadata has ne EntityID";
|
||||
$res = 0;
|
||||
|
@ -1989,7 +1987,7 @@ sub attributes {
|
|||
my $eid = $1;
|
||||
if ( defined $entityIds{$eid} ) {
|
||||
push @msg,
|
||||
"$idpId and $entityIds{$eid} have the same SAML EntityID";
|
||||
"$idpId and $entityIds{$eid} have the same SAML EntityID";
|
||||
$res = 0;
|
||||
next;
|
||||
}
|
||||
|
@ -2040,7 +2038,8 @@ sub attributes {
|
|||
select => [
|
||||
{ k => '', v => '' },
|
||||
{ k => 'kerberos', v => 'Kerberos' },
|
||||
{ k => 'password-protected-transport',
|
||||
{
|
||||
k => 'password-protected-transport',
|
||||
v => 'Password protected transport'
|
||||
},
|
||||
{ k => 'password', v => 'Password' },
|
||||
|
@ -2152,40 +2151,40 @@ sub attributes {
|
|||
samlSPSSODescriptorSingleLogoutServiceHTTPRedirect => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect;'
|
||||
. '#PORTAL#/saml/proxySingleLogout;'
|
||||
. '#PORTAL#/saml/proxySingleLogoutReturn',
|
||||
. '#PORTAL#/saml/proxySingleLogout;'
|
||||
. '#PORTAL#/saml/proxySingleLogoutReturn',
|
||||
documentation => 'SAML SP SLO HTTP Redirect',
|
||||
},
|
||||
samlSPSSODescriptorSingleLogoutServiceHTTPPost => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;'
|
||||
. '#PORTAL#/saml/proxySingleLogout;'
|
||||
. '#PORTAL#/saml/proxySingleLogoutReturn',
|
||||
. '#PORTAL#/saml/proxySingleLogout;'
|
||||
. '#PORTAL#/saml/proxySingleLogoutReturn',
|
||||
documentation => 'SAML SP SLO HTTP POST',
|
||||
},
|
||||
samlSPSSODescriptorSingleLogoutServiceSOAP => {
|
||||
type => 'samlService',
|
||||
default => 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP;'
|
||||
. '#PORTAL#/saml/proxySingleLogoutSOAP;',
|
||||
. '#PORTAL#/saml/proxySingleLogoutSOAP;',
|
||||
documentation => 'SAML SP SLO SOAP',
|
||||
},
|
||||
samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact => {
|
||||
type => 'samlAssertion',
|
||||
default =>
|
||||
'1;0;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;'
|
||||
. '#PORTAL#/saml/proxySingleSignOnArtifact',
|
||||
'1;0;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;'
|
||||
. '#PORTAL#/saml/proxySingleSignOnArtifact',
|
||||
documentation => 'SAML SP ACS HTTP artifact',
|
||||
},
|
||||
samlSPSSODescriptorAssertionConsumerServiceHTTPPost => {
|
||||
type => 'samlAssertion',
|
||||
default => '0;1;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;'
|
||||
. '#PORTAL#/saml/proxySingleSignOnPost',
|
||||
. '#PORTAL#/saml/proxySingleSignOnPost',
|
||||
documentation => 'SAML SP ACS HTTP POST',
|
||||
},
|
||||
samlSPSSODescriptorArtifactResolutionServiceArtifact => {
|
||||
type => 'samlAssertion',
|
||||
default => '1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;'
|
||||
. '#PORTAL#/saml/artifact',
|
||||
. '#PORTAL#/saml/artifact',
|
||||
documentation => 'SAML SP artifact resolution service ',
|
||||
},
|
||||
samlSPMetaDataOptionsNameIDFormat => {
|
||||
|
@ -2277,8 +2276,8 @@ sub attributes {
|
|||
{ k => 'Demo', v => 'Demonstration' },
|
||||
{ k => 'Choice', v => 'authChoice' },
|
||||
{ k => 'Combination', v => 'combineMods' },
|
||||
{ k => 'CAS', v => 'Central Authentication Service (CAS)' },
|
||||
{ k => 'OpenID', v => 'OpenID' },
|
||||
{ k => 'CAS', v => 'Central Authentication Service (CAS)' },
|
||||
{ k => 'OpenID', v => 'OpenID' },
|
||||
{ k => 'OpenIDConnect', v => 'OpenID Connect' },
|
||||
{ k => 'SAML', v => 'SAML v2' },
|
||||
{ k => 'Proxy', v => 'Proxy' },
|
||||
|
@ -2341,7 +2340,7 @@ sub attributes {
|
|||
type => 'text',
|
||||
default => 'TOTP,U2F,Yubikey',
|
||||
documentation =>
|
||||
'Available self-registration modules for second factor',
|
||||
'Available self-registration modules for second factor',
|
||||
},
|
||||
|
||||
# DEMO
|
||||
|
@ -2414,9 +2413,9 @@ sub attributes {
|
|||
my $l = shift;
|
||||
my (@s) = split( /[\s,]+/, $l );
|
||||
foreach my $s (@s) {
|
||||
$s
|
||||
=~ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?::\d{1,5})?/?.*)$}o
|
||||
or return ( 0, "__badLdapUri__: \"$s\"" );
|
||||
$s =~
|
||||
m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?::\d{1,5})?/?.*)$}o
|
||||
or return ( 0, "__badLdapUri__: \"$s\"" );
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
|
@ -2474,7 +2473,7 @@ sub attributes {
|
|||
documentation => 'LDAP filter for mail search'
|
||||
},
|
||||
LDAPFilter =>
|
||||
{ type => 'text', documentation => 'Default LDAP filter' },
|
||||
{ type => 'text', documentation => 'Default LDAP filter' },
|
||||
AuthLDAPFilter => {
|
||||
type => 'text',
|
||||
documentation => 'LDAP filter for auth search'
|
||||
|
@ -2499,7 +2498,7 @@ sub attributes {
|
|||
type => 'text',
|
||||
default => 'dn',
|
||||
documentation =>
|
||||
'LDAP attribute name in user entry referenced as member in groups',
|
||||
'LDAP attribute name in user entry referenced as member in groups',
|
||||
},
|
||||
ldapGroupAttributeNameSearch => {
|
||||
type => 'text',
|
||||
|
@ -2510,7 +2509,7 @@ sub attributes {
|
|||
type => 'text',
|
||||
default => 'dn',
|
||||
documentation =>
|
||||
'LDAP attribute name in group entry referenced as member in groups',
|
||||
'LDAP attribute name in group entry referenced as member in groups',
|
||||
},
|
||||
ldapTimeout => {
|
||||
type => 'int',
|
||||
|
@ -2637,7 +2636,7 @@ sub attributes {
|
|||
default => {
|
||||
proxy => 'http://auth.example.com/sessions',
|
||||
ns =>
|
||||
'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService',
|
||||
'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService',
|
||||
},
|
||||
documentation => 'Apache::Session module parameters',
|
||||
},
|
||||
|
@ -2719,7 +2718,7 @@ sub attributes {
|
|||
},
|
||||
linkedInUserField => { type => 'text', default => 'emailAddress' },
|
||||
linkedInScope =>
|
||||
{ type => 'text', default => 'r_basicprofile r_emailaddress' },
|
||||
{ type => 'text', default => 'r_basicprofile r_emailaddress' },
|
||||
|
||||
# WebID
|
||||
webIDAuthnLevel => {
|
||||
|
@ -2759,15 +2758,15 @@ sub attributes {
|
|||
dbiPasswordMailCol => { type => 'text', },
|
||||
userPivot => { type => 'text', },
|
||||
dbiAuthPasswordHash =>
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
dbiDynamicHashEnabled =>
|
||||
{ type => 'bool', help => 'authdbi.html#password', },
|
||||
{ type => 'bool', help => 'authdbi.html#password', },
|
||||
dbiDynamicHashValidSchemes =>
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
dbiDynamicHashValidSaltedSchemes =>
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
dbiDynamicHashNewPasswordScheme =>
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
{ type => 'text', help => 'authdbi.html#password', },
|
||||
dbiExportedVars => {
|
||||
type => 'keyTextContainer',
|
||||
keyTest => qr/^!?[a-zA-Z][a-zA-Z0-9_-]*$/,
|
||||
|
@ -2847,11 +2846,13 @@ sub attributes {
|
|||
type => 'authChoiceContainer',
|
||||
keyTest => qr/^(\d*)?[a-zA-Z0-9_]+$/,
|
||||
keyMsgFail => '__badChoiceKey__',
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
select => [
|
||||
[ { k => 'Apache', v => 'Apache' },
|
||||
[
|
||||
{ k => 'Apache', v => 'Apache' },
|
||||
{ k => 'AD', v => 'Active Directory' },
|
||||
{ k => 'CAS',
|
||||
{
|
||||
k => 'CAS',
|
||||
v => 'Central Authentication Service (CAS)'
|
||||
},
|
||||
{ k => 'DBI', v => 'Database (DBI)' },
|
||||
|
@ -2876,8 +2877,10 @@ sub attributes {
|
|||
{ k => 'WebID', v => 'WebID' },
|
||||
{ k => 'Custom', v => 'customModule' },
|
||||
],
|
||||
[ { k => 'AD', v => 'Active Directory' },
|
||||
{ k => 'CAS',
|
||||
[
|
||||
{ k => 'AD', v => 'Active Directory' },
|
||||
{
|
||||
k => 'CAS',
|
||||
v => 'Central Authentication Service (CAS)'
|
||||
},
|
||||
{ k => 'DBI', v => 'Database (DBI)' },
|
||||
|
@ -2896,7 +2899,8 @@ sub attributes {
|
|||
{ k => 'WebID', v => 'WebID' },
|
||||
{ k => 'Custom', v => 'customModule' },
|
||||
],
|
||||
[ { k => 'AD', v => 'Active Directory' },
|
||||
[
|
||||
{ k => 'AD', v => 'Active Directory' },
|
||||
{ k => 'DBI', v => 'Database (DBI)' },
|
||||
{ k => 'Demo', v => 'Demo' },
|
||||
{ k => 'LDAP', v => 'LDAP' },
|
||||
|
@ -2916,7 +2920,7 @@ sub attributes {
|
|||
combModules => {
|
||||
type => 'cmbModuleContainer',
|
||||
keyTest => qr/^\w+$/,
|
||||
test => sub {1},
|
||||
test => sub { 1 },
|
||||
documentation => 'Combination module description',
|
||||
select => [
|
||||
{ k => 'Apache', v => 'Apache' },
|
||||
|
@ -2933,8 +2937,8 @@ sub attributes {
|
|||
{ k => 'Twitter', v => 'Twitter' },
|
||||
{ k => 'WebID', v => 'WebID' },
|
||||
{ k => 'Demo', v => 'Demonstration' },
|
||||
{ k => 'CAS', v => 'Central Authentication Service (CAS)' },
|
||||
{ k => 'OpenID', v => 'OpenID' },
|
||||
{ k => 'CAS', v => 'Central Authentication Service (CAS)' },
|
||||
{ k => 'OpenID', v => 'OpenID' },
|
||||
{ k => 'OpenIDConnect', v => 'OpenID Connect' },
|
||||
{ k => 'SAML', v => 'SAML v2' },
|
||||
{ k => 'Proxy', v => 'Proxy' },
|
||||
|
@ -3045,8 +3049,7 @@ sub attributes {
|
|||
'loa-4' => 4,
|
||||
'loa-5' => 5,
|
||||
},
|
||||
documentation =>
|
||||
'OpenID Connect Authentication Context Class Ref',
|
||||
documentation => 'OpenID Connect Authentication Context Class Ref',
|
||||
},
|
||||
oidcServicePrivateKeySig => { type => 'RSAPrivateKey', },
|
||||
oidcServicePublicKeySig => { type => 'RSAPublicKey', },
|
||||
|
@ -3055,10 +3058,9 @@ sub attributes {
|
|||
documentation => 'OpenID Connect Signature Key ID',
|
||||
},
|
||||
oidcServiceAllowDynamicRegistration => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation =>
|
||||
'OpenID Connect allow dynamic client registration',
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation => 'OpenID Connect allow dynamic client registration',
|
||||
},
|
||||
oidcServiceAllowAuthorizationCodeFlow => {
|
||||
type => 'bool',
|
||||
|
@ -3088,12 +3090,12 @@ sub attributes {
|
|||
oidcOPMetaDataNodes => {
|
||||
type => 'oidcOPMetaDataNodeContainer',
|
||||
help =>
|
||||
'authopenidconnect.html#declare_the_openid_connect_provider_in_llng',
|
||||
'authopenidconnect.html#declare_the_openid_connect_provider_in_llng',
|
||||
},
|
||||
oidcRPMetaDataNodes => {
|
||||
type => 'oidcRPMetaDataNodeContainer',
|
||||
help =>
|
||||
'idpopenidconnect.html#configuration_of_relying_party_in_llng',
|
||||
'idpopenidconnect.html#configuration_of_relying_party_in_llng',
|
||||
},
|
||||
oidcOPMetaDataOptions => { type => 'subContainer', },
|
||||
oidcRPMetaDataOptions => { type => 'subContainer', },
|
||||
|
@ -3115,7 +3117,7 @@ sub attributes {
|
|||
oidcOPMetaDataOptionsClientID => { type => 'text', },
|
||||
oidcOPMetaDataOptionsClientSecret => { type => 'password', },
|
||||
oidcOPMetaDataOptionsScope =>
|
||||
{ type => 'text', default => 'openid profile' },
|
||||
{ type => 'text', default => 'openid profile' },
|
||||
oidcOPMetaDataOptionsDisplay => {
|
||||
type => 'select',
|
||||
select => [
|
||||
|
@ -3140,10 +3142,9 @@ sub attributes {
|
|||
default => 'client_secret_post',
|
||||
},
|
||||
oidcOPMetaDataOptionsCheckJWTSignature =>
|
||||
{ type => 'bool', default => 1 },
|
||||
oidcOPMetaDataOptionsIDTokenMaxAge =>
|
||||
{ type => 'int', default => 30 },
|
||||
oidcOPMetaDataOptionsUseNonce => { type => 'bool', default => 1 },
|
||||
{ type => 'bool', default => 1 },
|
||||
oidcOPMetaDataOptionsIDTokenMaxAge => { type => 'int', default => 30 },
|
||||
oidcOPMetaDataOptionsUseNonce => { type => 'bool', default => 1 },
|
||||
oidcOPMetaDataOptionsDisplayName => { type => 'text', },
|
||||
oidcOPMetaDataOptionsIcon => { type => 'text', },
|
||||
oidcOPMetaDataOptionsStoreIDToken => { type => 'bool', default => 0 },
|
||||
|
@ -3182,12 +3183,12 @@ sub attributes {
|
|||
default => 'HS512',
|
||||
},
|
||||
oidcRPMetaDataOptionsIDTokenExpiration =>
|
||||
{ type => 'int', default => 3600 },
|
||||
{ type => 'int', default => 3600 },
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration =>
|
||||
{ type => 'int', default => 3600 },
|
||||
{ type => 'int', default => 3600 },
|
||||
oidcRPMetaDataOptionsRedirectUris => { type => 'text', },
|
||||
oidcRPMetaDataOptionsExtraClaims =>
|
||||
{ type => 'keyTextContainer', default => {} },
|
||||
{ type => 'keyTextContainer', default => {} },
|
||||
oidcRPMetaDataOptionsBypassConsent => {
|
||||
type => 'bool',
|
||||
help => 'openidconnectclaims.html',
|
||||
|
|
|
@ -69,7 +69,7 @@ has confChanged => (
|
|||
);
|
||||
|
||||
# Properties required during build
|
||||
has refConf => ( is => 'ro', isa => 'HashRef', required => 1 );
|
||||
has refConf => ( is => 'ro', isa => 'HashRef', required => 1 );
|
||||
has req => ( is => 'ro', required => 1 );
|
||||
has newConf => ( is => 'rw', isa => 'HashRef' );
|
||||
has tree => ( is => 'rw', isa => 'ArrayRef' );
|
||||
|
@ -158,7 +158,7 @@ sub _scanNodes {
|
|||
hdebug("Looking to $name");
|
||||
|
||||
# subnode
|
||||
my $subNodes = $leaf->{nodes} // $leaf->{_nodes};
|
||||
my $subNodes = $leaf->{nodes} // $leaf->{_nodes};
|
||||
my $subNodesCond = $leaf->{nodes_cond} // $leaf->{_nodes_cond};
|
||||
|
||||
##################################
|
||||
|
@ -763,11 +763,12 @@ sub _scanNodes {
|
|||
foreach my $n (@$subNodes) {
|
||||
hdebug(" looking at $n subnode");
|
||||
if ( ref $n->{data} and ref $n->{data} eq 'ARRAY' ) {
|
||||
|
||||
# authChoiceModules
|
||||
if ( $name eq 'authChoiceModules' ) {
|
||||
hdebug(' combModules');
|
||||
$n->{data}->[5] ||= {};
|
||||
$n->{data}->[5] = to_json($n->{data}->[5]);
|
||||
$n->{data}->[5] = to_json( $n->{data}->[5] );
|
||||
}
|
||||
|
||||
$n->{data} = join ';', @{ $n->{data} };
|
||||
|
@ -1059,15 +1060,15 @@ sub _unitTest {
|
|||
or $attr->{type} =~ /Container$/ )
|
||||
{
|
||||
my $keyMsg = $attr->{keyMsgFail} // $type->{keyMsgFail};
|
||||
my $msg = $attr->{msgFail} // $type->{msgFail};
|
||||
my $msg = $attr->{msgFail} // $type->{msgFail};
|
||||
$res = 0
|
||||
unless (
|
||||
$self->_execTest(
|
||||
{
|
||||
keyTest => $attr->{keyTest} // $type->{keyTest},
|
||||
keyTest => $attr->{keyTest} // $type->{keyTest},
|
||||
keyMsgFail => $attr->{keyMsgFail}
|
||||
// $type->{keyMsgFail},
|
||||
test => $attr->{test} // $type->{test},
|
||||
test => $attr->{test} // $type->{test},
|
||||
msgFail => $attr->{msgFail} // $type->{msgFail},
|
||||
},
|
||||
$conf->{$key},
|
||||
|
|
|
@ -30,7 +30,8 @@ sub tests {
|
|||
portalIsInDomain => sub {
|
||||
return (
|
||||
1,
|
||||
( index( $conf->{portal}, $conf->{domain} ) > 0
|
||||
(
|
||||
index( $conf->{portal}, $conf->{domain} ) > 0
|
||||
? ''
|
||||
: "Portal seems not to be in the domain $conf->{domain}"
|
||||
)
|
||||
|
@ -42,7 +43,7 @@ sub tests {
|
|||
|
||||
# Checking for ending slash
|
||||
$conf->{portal} .= '/'
|
||||
unless ( $conf->{portal} =~ qr#/$# );
|
||||
unless ( $conf->{portal} =~ qr#/$# );
|
||||
|
||||
# Deleting trailing ending slash
|
||||
my $regex = qr#/+$#;
|
||||
|
@ -60,10 +61,11 @@ sub tests {
|
|||
}
|
||||
return (
|
||||
1,
|
||||
( @pb
|
||||
(
|
||||
@pb
|
||||
? 'Virtual hosts '
|
||||
. join( ', ', @pb )
|
||||
. " are not in $conf->{domain} and cross-domain-authentication is not set"
|
||||
. join( ', ', @pb )
|
||||
. " are not in $conf->{domain} and cross-domain-authentication is not set"
|
||||
: undef
|
||||
)
|
||||
);
|
||||
|
@ -77,9 +79,9 @@ sub tests {
|
|||
}
|
||||
if (@pb) {
|
||||
return ( 0,
|
||||
'Virtual hosts '
|
||||
. join( ', ', @pb )
|
||||
. " contain a port, this is not allowed" );
|
||||
'Virtual hosts '
|
||||
. join( ', ', @pb )
|
||||
. " contain a port, this is not allowed" );
|
||||
}
|
||||
else { return 1; }
|
||||
},
|
||||
|
@ -92,9 +94,9 @@ sub tests {
|
|||
}
|
||||
if (@pb) {
|
||||
return ( 0,
|
||||
'Virtual hosts '
|
||||
. join( ', ', @pb )
|
||||
. " must be in lower case" );
|
||||
'Virtual hosts '
|
||||
. join( ', ', @pb )
|
||||
. " must be in lower case" );
|
||||
}
|
||||
else { return 1; }
|
||||
},
|
||||
|
@ -102,12 +104,12 @@ sub tests {
|
|||
# Check if "userDB" and "authentication" are consistent
|
||||
authAndUserDBConsistency => sub {
|
||||
foreach
|
||||
my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID))
|
||||
my $type (qw(Facebook Google OpenID OpenIDConnect SAML WebID))
|
||||
{
|
||||
return ( 0,
|
||||
"\"$type\" can not be used as user database without using \"$type\" for authentication"
|
||||
)
|
||||
if ($conf->{userDB} =~ /$type/
|
||||
"\"$type\" can not be used as user database without using \"$type\" for authentication"
|
||||
)
|
||||
if ( $conf->{userDB} =~ /$type/
|
||||
and $conf->{authentication} !~ /$type/ );
|
||||
}
|
||||
return 1;
|
||||
|
@ -117,29 +119,30 @@ sub tests {
|
|||
checkAttrAndMacros => sub {
|
||||
my @tmp;
|
||||
foreach my $k ( keys %$conf ) {
|
||||
if ( $k
|
||||
=~ /^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/
|
||||
)
|
||||
if ( $k =~
|
||||
/^(?:openIdSreg_(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)|whatToTrace)$/
|
||||
)
|
||||
{
|
||||
my $v = $conf->{$k};
|
||||
$v =~ s/^$//;
|
||||
next if ( $v =~ /^_/ );
|
||||
push @tmp,
|
||||
$k
|
||||
unless (
|
||||
$k
|
||||
unless (
|
||||
defined(
|
||||
$conf->{exportedVars}->{$v}
|
||||
or defined( $conf->{macros}->{$v} )
|
||||
or defined( $conf->{macros}->{$v} )
|
||||
)
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
1,
|
||||
( @tmp
|
||||
(
|
||||
@tmp
|
||||
? 'Values of parameter(s) "'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not defined in exported attributes or macros'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not defined in exported attributes or macros'
|
||||
: ''
|
||||
)
|
||||
);
|
||||
|
@ -151,18 +154,18 @@ sub tests {
|
|||
if ( $conf->{userDB} =~ /^Google$/ ) {
|
||||
foreach my $k ( keys %{ $conf->{exportedVars} } ) {
|
||||
my $v = $conf->{exportedVars}->{$k};
|
||||
if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() )
|
||||
{
|
||||
if ( $v !~ Lemonldap::NG::Common::Regexp::GOOGLEAXATTR() ) {
|
||||
push @tmp, $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
1,
|
||||
( @tmp
|
||||
(
|
||||
@tmp
|
||||
? 'Values of parameter(s) "'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not exported by Google'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not exported by Google'
|
||||
: ''
|
||||
)
|
||||
);
|
||||
|
@ -174,8 +177,7 @@ sub tests {
|
|||
if ( $conf->{userDB} =~ /^OpenID$/ ) {
|
||||
foreach my $k ( keys %{ $conf->{exportedVars} } ) {
|
||||
my $v = $conf->{exportedVars}->{$k};
|
||||
if ( $v
|
||||
!~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
|
||||
if ( $v !~ Lemonldap::NG::Common::Regexp::OPENIDSREGATTR() )
|
||||
{
|
||||
push @tmp, $v;
|
||||
}
|
||||
|
@ -183,10 +185,11 @@ sub tests {
|
|||
}
|
||||
return (
|
||||
1,
|
||||
( @tmp
|
||||
(
|
||||
@tmp
|
||||
? 'Values of parameter(s) "'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not exported by OpenID SREG'
|
||||
. join( ', ', @tmp )
|
||||
. '" are not exported by OpenID SREG'
|
||||
: ''
|
||||
)
|
||||
);
|
||||
|
@ -195,40 +198,40 @@ sub tests {
|
|||
# Try to use Apache::Session module
|
||||
testApacheSession => sub {
|
||||
my ( $id, %h );
|
||||
my $gc = $Lemonldap::NG::Handler::PSGI::Main::tsv
|
||||
->{sessionStorageModule};
|
||||
my $gc =
|
||||
$Lemonldap::NG::Handler::PSGI::Main::tsv->{sessionStorageModule};
|
||||
return 1
|
||||
if ( ( $gc and $gc eq $conf->{globalStorage} )
|
||||
or $conf->{globalStorage}
|
||||
=~ /^Lemonldap::NG::Common::Apache::Session::/ );
|
||||
if ( ( $gc and $gc eq $conf->{globalStorage} )
|
||||
or $conf->{globalStorage} =~
|
||||
/^Lemonldap::NG::Common::Apache::Session::/ );
|
||||
eval "use $conf->{globalStorage}";
|
||||
return ( -1, "Unknown package $conf->{globalStorage}" ) if ($@);
|
||||
eval {
|
||||
tie %h, 'Lemonldap::NG::Common::Apache::Session', undef,
|
||||
{
|
||||
{
|
||||
%{ $conf->{globalStorageOptions} },
|
||||
backend => $conf->{globalStorage}
|
||||
};
|
||||
};
|
||||
};
|
||||
return ( -1, "Unable to create a session ($@)" )
|
||||
if ( $@ or not tied(%h) );
|
||||
if ( $@ or not tied(%h) );
|
||||
eval {
|
||||
$h{a} = 1;
|
||||
$id = $h{_session_id} or return ( -1, 'No _session_id' );
|
||||
untie(%h);
|
||||
tie %h, 'Lemonldap::NG::Common::Apache::Session', $id,
|
||||
{
|
||||
{
|
||||
%{ $conf->{globalStorageOptions} },
|
||||
backend => $conf->{globalStorage}
|
||||
};
|
||||
};
|
||||
};
|
||||
return ( -1, "Unable to insert data ($@)" ) if ($@);
|
||||
return ( -1, "Unable to recover data stored" )
|
||||
unless ( $h{a} == 1 );
|
||||
unless ( $h{a} == 1 );
|
||||
eval { tied(%h)->delete; };
|
||||
return ( -1, "Unable to delete session ($@)" ) if ($@);
|
||||
return ( -1,
|
||||
'All sessions may be lost and you must restart all your Apache servers'
|
||||
'All sessions may be lost and you must restart all your Apache servers'
|
||||
) if ( $gc and $conf->{globalStorage} ne $gc );
|
||||
return 1;
|
||||
},
|
||||
|
@ -238,8 +241,9 @@ sub tests {
|
|||
my $cn = $Lemonldap::NG::Handler::PSGI::API::tsv->{cookieName};
|
||||
return (
|
||||
1,
|
||||
( $cn
|
||||
and $cn ne $conf->{cookieName}
|
||||
(
|
||||
$cn
|
||||
and $cn ne $conf->{cookieName}
|
||||
? 'Cookie name has changed, you must restart all your web servers'
|
||||
: ()
|
||||
)
|
||||
|
@ -250,9 +254,9 @@ sub tests {
|
|||
cookieTTL => sub {
|
||||
return 1 unless ( defined $conf->{cookieExpiration} );
|
||||
return ( 0, "Cookie TTL must be higher than one minute" )
|
||||
unless ( $conf->{cookieExpiration} > 60 );
|
||||
unless ( $conf->{cookieExpiration} > 60 );
|
||||
return ( 1, "Cookie TTL should be higher or equal than one hour" )
|
||||
unless ( $conf->{cookieExpiration} >= 3600
|
||||
unless ( $conf->{cookieExpiration} >= 3600
|
||||
|| $conf->{cookieExpiration} == 0 );
|
||||
|
||||
# Return
|
||||
|
@ -263,7 +267,8 @@ sub tests {
|
|||
managerProtection => sub {
|
||||
return (
|
||||
1,
|
||||
( $conf->{cfgAuthor} eq 'anonymous'
|
||||
(
|
||||
$conf->{cfgAuthor} eq 'anonymous'
|
||||
? 'Your manager seems to be unprotected'
|
||||
: ''
|
||||
)
|
||||
|
@ -279,21 +284,21 @@ sub tests {
|
|||
# Use SMTP
|
||||
eval "use Net::SMTP";
|
||||
return ( 1, "Net::SMTP module is required to use SMTP server" )
|
||||
if ($@);
|
||||
if ($@);
|
||||
|
||||
# Create SMTP object
|
||||
my $smtp = Net::SMTP->new( $conf->{SMTPServer}, Timeout => 5 );
|
||||
return ( 1,
|
||||
"SMTP connection to " . $conf->{SMTPServer} . " failed" )
|
||||
unless ($smtp);
|
||||
unless ($smtp);
|
||||
|
||||
# Skip other tests if no authentication
|
||||
return 1
|
||||
unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
|
||||
unless ( $conf->{SMTPAuthUser} and $conf->{SMTPAuthPass} );
|
||||
|
||||
# Try authentication
|
||||
return ( 1, "SMTP authentication failed" )
|
||||
unless $smtp->auth( $conf->{SMTPAuthUser},
|
||||
unless $smtp->auth( $conf->{SMTPAuthUser},
|
||||
$conf->{SMTPAuthPass} );
|
||||
|
||||
# Return
|
||||
|
@ -303,14 +308,15 @@ sub tests {
|
|||
# SAML entity ID must be uniq
|
||||
samlIDPEntityIdUniqueness => sub {
|
||||
return 1
|
||||
unless ( $conf->{samlIDPMetaDataXML}
|
||||
unless ( $conf->{samlIDPMetaDataXML}
|
||||
and %{ $conf->{samlIDPMetaDataXML} } );
|
||||
my @msg;
|
||||
my $res = 1;
|
||||
my %entityIds;
|
||||
foreach my $idpId ( keys %{ $conf->{samlIDPMetaDataXML} } ) {
|
||||
unless ( $conf->{samlIDPMetaDataXML}->{$idpId}
|
||||
->{samlIDPMetaDataXML} =~ /entityID=(['"])(.+?)\1/si )
|
||||
unless (
|
||||
$conf->{samlIDPMetaDataXML}->{$idpId}->{samlIDPMetaDataXML}
|
||||
=~ /entityID=(['"])(.+?)\1/si )
|
||||
{
|
||||
push @msg, "$idpId SAML metadata has no EntityID";
|
||||
$res = 0;
|
||||
|
@ -319,7 +325,7 @@ sub tests {
|
|||
my $eid = $2;
|
||||
if ( defined $entityIds{$eid} ) {
|
||||
push @msg,
|
||||
"$idpId and $entityIds{$eid} have the same SAML EntityID";
|
||||
"$idpId and $entityIds{$eid} have the same SAML EntityID";
|
||||
$res = 0;
|
||||
next;
|
||||
}
|
||||
|
@ -329,15 +335,15 @@ sub tests {
|
|||
},
|
||||
samlSPEntityIdUniqueness => sub {
|
||||
return 1
|
||||
unless ( $conf->{samlSPMetaDataXML}
|
||||
unless ( $conf->{samlSPMetaDataXML}
|
||||
and %{ $conf->{samlSPMetaDataXML} } );
|
||||
my @msg;
|
||||
my $res = 1;
|
||||
my %entityIds;
|
||||
foreach my $spId ( keys %{ $conf->{samlSPMetaDataXML} } ) {
|
||||
unless (
|
||||
$conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML}
|
||||
=~ /entityID=(['"])(.+?)\1/si )
|
||||
$conf->{samlSPMetaDataXML}->{$spId}->{samlSPMetaDataXML} =~
|
||||
/entityID=(['"])(.+?)\1/si )
|
||||
{
|
||||
push @msg, "$spId SAML metadata has no EntityID";
|
||||
$res = 0;
|
||||
|
@ -346,7 +352,7 @@ sub tests {
|
|||
my $eid = $2;
|
||||
if ( defined $entityIds{$eid} ) {
|
||||
push @msg,
|
||||
"$spId and $entityIds{$eid} have the same SAML EntityID";
|
||||
"$spId and $entityIds{$eid} have the same SAML EntityID";
|
||||
$res = 0;
|
||||
next;
|
||||
}
|
||||
|
@ -360,7 +366,7 @@ sub tests {
|
|||
return 1 unless ( $conf->{authentication} eq 'Combination' );
|
||||
require Lemonldap::NG::Common::Combination::Parser;
|
||||
return ( 0, 'No module declared for combination' )
|
||||
unless ( $conf->{combModules} and %{ $conf->{combModules} } );
|
||||
unless ( $conf->{combModules} and %{ $conf->{combModules} } );
|
||||
my $moduleList;
|
||||
foreach my $md ( keys %{ $conf->{combModules} } ) {
|
||||
my $entry = $conf->{combModules}->{$md};
|
||||
|
@ -371,8 +377,8 @@ sub tests {
|
|||
);
|
||||
}
|
||||
eval {
|
||||
Lemonldap::NG::Common::Combination::Parser->parse(
|
||||
$moduleList, $conf->{combination} );
|
||||
Lemonldap::NG::Common::Combination::Parser->parse( $moduleList,
|
||||
$conf->{combination} );
|
||||
};
|
||||
return ( 0, $@ ) if ($@);
|
||||
|
||||
|
@ -397,7 +403,7 @@ sub tests {
|
|||
eval "use Convert::Base32";
|
||||
return ( 1,
|
||||
"Convert::Base32 module is required to enable TOTP" )
|
||||
if ($@);
|
||||
if ($@);
|
||||
}
|
||||
|
||||
# Use U2F
|
||||
|
@ -406,7 +412,7 @@ sub tests {
|
|||
{
|
||||
eval "use Crypt::U2F::Server::Simple";
|
||||
return ( 1,
|
||||
"Crypt::U2F::Server::Simple module is required to enable U2F"
|
||||
"Crypt::U2F::Server::Simple module is required to enable U2F"
|
||||
) if ($@);
|
||||
}
|
||||
|
||||
|
@ -414,7 +420,7 @@ sub tests {
|
|||
if ( $conf->{yubikey2fActivation} ) {
|
||||
eval "use Auth::Yubikey_WebClient";
|
||||
return ( 1,
|
||||
"Auth::Yubikey_WebClient module is required to enable Yubikey"
|
||||
"Auth::Yubikey_WebClient module is required to enable Yubikey"
|
||||
) if ($@);
|
||||
}
|
||||
|
||||
|
@ -428,7 +434,7 @@ sub tests {
|
|||
my $w = "";
|
||||
foreach ( 'totp', 'u' ) {
|
||||
$w .= uc($_) . "2F is activated twice \n"
|
||||
if ( $conf->{ $_ . '2fActivation' } eq '1' );
|
||||
if ( $conf->{ $_ . '2fActivation' } eq '1' );
|
||||
}
|
||||
return ( 1, ( $w ? $w : () ) );
|
||||
},
|
||||
|
@ -439,8 +445,10 @@ sub tests {
|
|||
return 1 unless ( defined $conf->{totp2fDigits} );
|
||||
return (
|
||||
1,
|
||||
( ( $conf->{totp2fDigits} == 6
|
||||
or $conf->{totp2fDigits} == 8
|
||||
(
|
||||
(
|
||||
$conf->{totp2fDigits} == 6
|
||||
or $conf->{totp2fDigits} == 8
|
||||
)
|
||||
? ''
|
||||
: 'TOTP should be 6 or 8 digits long'
|
||||
|
@ -452,9 +460,9 @@ sub tests {
|
|||
totp2fParams => sub {
|
||||
return 1 unless ( $conf->{totp2fActivation} );
|
||||
return ( 0, 'TOTP range must be defined' )
|
||||
unless ( $conf->{totp2fRange} );
|
||||
unless ( $conf->{totp2fRange} );
|
||||
return ( 1, "TOTP interval should be higher than 10s" )
|
||||
unless ( $conf->{totp2fInterval} > 10 );
|
||||
unless ( $conf->{totp2fInterval} > 10 );
|
||||
|
||||
# Return
|
||||
return 1;
|
||||
|
@ -465,11 +473,12 @@ sub tests {
|
|||
yubikey2fParams => sub {
|
||||
return 1 unless ( $conf->{yubikey2fActivation} );
|
||||
return ( 0, "Yubikey client ID and secret key must be set" )
|
||||
unless ( defined $conf->{yubikey2fSecretKey}
|
||||
unless ( defined $conf->{yubikey2fSecretKey}
|
||||
&& defined $conf->{yubikey2fClientID} );
|
||||
return (
|
||||
1,
|
||||
( ( $conf->{yubikey2fPublicIDSize} == 12 )
|
||||
(
|
||||
( $conf->{yubikey2fPublicIDSize} == 12 )
|
||||
? ''
|
||||
: 'Yubikey public ID size should be 12 digits long'
|
||||
)
|
||||
|
@ -480,7 +489,7 @@ sub tests {
|
|||
rest2fVerifyUrl => sub {
|
||||
return 1 unless ( $conf->{rest2fActivation} );
|
||||
return ( 0, "REST 2F Verify URL must be set" )
|
||||
unless ( defined $conf->{rest2fVerifyUrl} );
|
||||
unless ( defined $conf->{rest2fVerifyUrl} );
|
||||
|
||||
# Return
|
||||
return 1;
|
||||
|
@ -494,16 +503,15 @@ sub tests {
|
|||
my $ok = 0;
|
||||
foreach (qw(u totp yubikey)) {
|
||||
$ok ||= $conf->{ $_ . '2fActivation' }
|
||||
&& $conf->{ $_ . '2fSelfRegistration' };
|
||||
&& $conf->{ $_ . '2fSelfRegistration' };
|
||||
last if ($ok);
|
||||
}
|
||||
|
||||
$ok ||= $conf->{'utotp2fActivation'}
|
||||
&& ( $conf->{'u2fSelfRegistration'}
|
||||
&& ( $conf->{'u2fSelfRegistration'}
|
||||
|| $conf->{'totp2fSelfRegistration'} );
|
||||
$msg
|
||||
= "A self registrable module should be enabled to require 2FA"
|
||||
unless ($ok);
|
||||
$msg = "A self registrable module should be enabled to require 2FA"
|
||||
unless ($ok);
|
||||
|
||||
return ( 1, $msg );
|
||||
},
|
||||
|
@ -512,7 +520,7 @@ sub tests {
|
|||
ext2fCommands => sub {
|
||||
return 1 unless ( $conf->{ext2fActivation} );
|
||||
return ( 0, "External 2F Send or Validate command must be set" )
|
||||
unless ( defined $conf->{ext2FSendCommand}
|
||||
unless ( defined $conf->{ext2FSendCommand}
|
||||
&& defined $conf->{ext2FValidateCommand} );
|
||||
|
||||
# Return
|
||||
|
@ -523,9 +531,9 @@ sub tests {
|
|||
formTimeout => sub {
|
||||
return 1 unless ( defined $conf->{formTimeout} );
|
||||
return ( 0, "XSRF form token TTL must be higher than 30s" )
|
||||
unless ( $conf->{formTimeout} > 30 );
|
||||
unless ( $conf->{formTimeout} > 30 );
|
||||
return ( 1, "XSRF form token TTL should not be higher than 2mn" )
|
||||
if ( $conf->{formTimeout} > 120 );
|
||||
if ( $conf->{formTimeout} > 120 );
|
||||
|
||||
# Return
|
||||
return 1;
|
||||
|
|
|
@ -24,7 +24,7 @@ delete $forms{restore};
|
|||
my ( @types, $attr, $tree, $ctrees );
|
||||
ok( $tree = Lemonldap::NG::Manager::Build::Tree::tree(), 'Get tree' );
|
||||
ok( $ctrees = Lemonldap::NG::Manager::Build::CTrees::cTrees(), 'Get cTrees' );
|
||||
ok( $attr = Lemonldap::NG::Manager::Build::Attributes::attributes(),
|
||||
ok( $attr = Lemonldap::NG::Manager::Build::Attributes::attributes(),
|
||||
'Get attributes' );
|
||||
$count += 4;
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ use strict;
|
|||
use Mouse;
|
||||
use JSON qw(from_json to_json);
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_ERROR
|
||||
PE_NOTOKEN
|
||||
PE_OK
|
||||
PE_SENDRESPONSE
|
||||
PE_TOKENEXPIRED
|
||||
PE_ERROR
|
||||
PE_NOTOKEN
|
||||
PE_OK
|
||||
PE_SENDRESPONSE
|
||||
PE_TOKENEXPIRED
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
@ -35,8 +35,8 @@ has sfReq => ( is => 'rw' );
|
|||
has ott => (
|
||||
is => 'rw',
|
||||
default => sub {
|
||||
my $ott = $_[0]->{p}
|
||||
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||
my $ott =
|
||||
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||
$ott->timeout( $_[0]->{conf}->{formTimeout} );
|
||||
return $ott;
|
||||
}
|
||||
|
@ -49,10 +49,12 @@ sub init {
|
|||
for my $i ( 0 .. 1 ) {
|
||||
foreach (
|
||||
split /,\s*/,
|
||||
$self->conf->{ $i
|
||||
$self->conf->{
|
||||
$i
|
||||
? 'available2FSelfRegistration'
|
||||
: 'available2F' }
|
||||
)
|
||||
: 'available2F'
|
||||
}
|
||||
)
|
||||
{
|
||||
my $prefix = lc($_);
|
||||
$prefix =~ s/2f$//i;
|
||||
|
@ -64,10 +66,9 @@ sub init {
|
|||
# Unless $rule, skip loading
|
||||
if ( $self->conf->{$ap} ) {
|
||||
$self->logger->debug("Trying to load $_ 2F");
|
||||
my $m
|
||||
= $self->p->loadPlugin(
|
||||
$i ? "::2F::Register::$_" : "::2F::$_" )
|
||||
or return 0;
|
||||
my $m =
|
||||
$self->p->loadPlugin( $i ? "::2F::Register::$_" : "::2F::$_" )
|
||||
or return 0;
|
||||
|
||||
# Rule and prefix may be modified by 2F module, reread them
|
||||
my $rule = $self->conf->{$ap};
|
||||
|
@ -77,13 +78,13 @@ sub init {
|
|||
$rule = $self->p->HANDLER->substitute($rule);
|
||||
unless ( $rule = $self->p->HANDLER->buildSub($rule) ) {
|
||||
$self->error( 'External 2F rule error: '
|
||||
. $self->p->HANDLER->tsv->{jail}->error );
|
||||
. $self->p->HANDLER->tsv->{jail}->error );
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Store module
|
||||
push @{ $self->{ $i ? 'sfRModules' : 'sfModules' } },
|
||||
{ p => $prefix, m => $m, r => $rule };
|
||||
{ p => $prefix, m => $m, r => $rule };
|
||||
}
|
||||
else {
|
||||
$self->logger->debug(' -> not enabled');
|
||||
|
@ -97,10 +98,10 @@ sub init {
|
|||
$self->p->HANDLER->substitute( $self->conf->{sfRequired} )
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
$self->error( 'Error in sfRequired rule'
|
||||
. $self->p->HANDLER->tsv->{jail}->error );
|
||||
. $self->p->HANDLER->tsv->{jail}->error );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -161,14 +162,14 @@ sub run {
|
|||
if ( $self->sfReq->( $req, $req->sessionInfo ) ) {
|
||||
$self->logger->debug("2F is required...");
|
||||
$self->logger->debug(" -> Register 2F");
|
||||
$req->pdata->{sfRegToken}
|
||||
= $self->ott->createToken( $req->sessionInfo );
|
||||
$req->pdata->{sfRegToken} =
|
||||
$self->ott->createToken( $req->sessionInfo );
|
||||
$self->logger->debug("Just one 2F is enabled");
|
||||
$self->logger->debug(" -> Redirect to /2fregisters/");
|
||||
$req->response(
|
||||
[ 302,
|
||||
[ Location => $self->conf->{portal} . '/2fregisters/' ],
|
||||
[]
|
||||
[
|
||||
302,
|
||||
[ Location => $self->conf->{portal} . '/2fregisters/' ], []
|
||||
]
|
||||
);
|
||||
return PE_SENDRESPONSE;
|
||||
|
@ -179,7 +180,7 @@ sub run {
|
|||
}
|
||||
|
||||
$self->userLogger->info( 'Second factor required for '
|
||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
|
||||
# Store user data in a token
|
||||
$req->sessionInfo->{_2fRealSession} = $req->id;
|
||||
|
@ -201,10 +202,9 @@ sub run {
|
|||
$req,
|
||||
'2fchoice',
|
||||
params => {
|
||||
SKIN => $self->conf->{portalSkin},
|
||||
TOKEN => $token,
|
||||
MODULES =>
|
||||
[ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
|
||||
SKIN => $self->conf->{portalSkin},
|
||||
TOKEN => $token,
|
||||
MODULES => [ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
|
||||
CHECKLOGINS => $checkLogins
|
||||
}
|
||||
);
|
||||
|
@ -230,16 +230,15 @@ sub _choice {
|
|||
|
||||
# Restore session
|
||||
unless ( $token = $req->param('token') ) {
|
||||
$self->userLogger->error(
|
||||
$self->prefix . ' 2F access without token' );
|
||||
$self->userLogger->error( $self->prefix . ' 2F access without token' );
|
||||
$req->mustRedirect(1);
|
||||
return $self->p->do( $req, [ sub {PE_NOTOKEN} ] );
|
||||
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
|
||||
}
|
||||
|
||||
my $session;
|
||||
unless ( $session = $self->ott->getToken($token) ) {
|
||||
$self->userLogger->info('Token expired');
|
||||
return $self->p->do( $req, [ sub {PE_TOKENEXPIRED} ] );
|
||||
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
|
||||
}
|
||||
|
||||
$req->sessionInfo($session);
|
||||
|
@ -254,7 +253,8 @@ sub _choice {
|
|||
$req->authResult($res);
|
||||
return $self->p->do(
|
||||
$req,
|
||||
[ sub {$res}, 'controlUrl',
|
||||
[
|
||||
sub { $res }, 'controlUrl',
|
||||
'buildCookie', @{ $self->p->endAuth },
|
||||
]
|
||||
);
|
||||
|
@ -269,8 +269,7 @@ sub _redirect {
|
|||
my $arg = $req->env->{QUERY_STRING};
|
||||
$self->logger->debug('Call sfEngine _redirect method');
|
||||
return [
|
||||
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ],
|
||||
[]
|
||||
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ], []
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -300,26 +299,25 @@ sub _displayRegister {
|
|||
'Looking if ' . $m->{m}->prefix . '2F register is available' );
|
||||
if ( $m->{r}->( $req, $req->userData ) ) {
|
||||
push @am,
|
||||
{
|
||||
{
|
||||
CODE => $m->{m}->prefix,
|
||||
URL => '/2fregisters/' . $m->{m}->prefix,
|
||||
LOGO => $m->{m}->logo,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
if ( @am == 1
|
||||
and
|
||||
not( $req->userData->{_2fDevices} or $req->data->{sfRegRequired} ) )
|
||||
and not( $req->userData->{_2fDevices} or $req->data->{sfRegRequired} ) )
|
||||
{
|
||||
return [ 302, [ Location => $self->conf->{portal} . $am[0]->{URL} ],
|
||||
[] ];
|
||||
}
|
||||
|
||||
my $_2fDevices = $req->userData->{_2fDevices}
|
||||
? eval {
|
||||
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
|
||||
}
|
||||
: undef;
|
||||
my $_2fDevices =
|
||||
$req->userData->{_2fDevices}
|
||||
? eval {
|
||||
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } ); }
|
||||
: undef;
|
||||
|
||||
unless ($_2fDevices) {
|
||||
$self->logger->debug("No 2F Device found");
|
||||
|
@ -365,11 +363,11 @@ sub register {
|
|||
$self->logger->debug(' -> OK');
|
||||
my $name = $m->{m}->prefix;
|
||||
push @am,
|
||||
{
|
||||
{
|
||||
name => $name,
|
||||
logo => $m->{m}->logo,
|
||||
url => "/2fregisters/$name"
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
return $self->p->sendJSONresponse( $req, \@am );
|
||||
|
@ -378,12 +376,12 @@ sub register {
|
|||
sub restoreSession {
|
||||
my ( $self, $req, @path ) = @_;
|
||||
my $token = $req->pdata->{sfRegToken}
|
||||
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
|
||||
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
|
||||
$req->userData( $self->ott->getToken( $token, 1 ) );
|
||||
$req->data->{sfRegRequired} = 1;
|
||||
return $req->method eq 'POST'
|
||||
? $self->register( $req, @path )
|
||||
: $self->_displayRegister( $req, @path );
|
||||
? $self->register( $req, @path )
|
||||
: $self->_displayRegister( $req, @path );
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -39,11 +39,12 @@ sub run {
|
|||
$self->logger->debug("Ext2F checkLogins set") if ($checkLogins);
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug('Launching "Send" external 2F command -> ' . $self->conf->{ext2FSendCommand});
|
||||
$self->logger->debug( 'Launching "Send" external 2F command -> '
|
||||
. $self->conf->{ext2FSendCommand} );
|
||||
if ( my $c =
|
||||
$self->launch( $req->sessionInfo, $self->conf->{ext2FSendCommand} ) )
|
||||
{
|
||||
$self->logger->error("External send command failed (code $c)");
|
||||
$self->logger->error("External send command failed (code $c)");
|
||||
return $self->p->do( $req, [ sub { PE_ERROR } ] );
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,8 @@ sub verify {
|
|||
}
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug('Launching "Validate" external 2F command -> ' . $self->conf->{ext2FValidateCommand});
|
||||
$self->logger->debug( 'Launching "Validate" external 2F command -> '
|
||||
. $self->conf->{ext2FValidateCommand} );
|
||||
$self->logger->debug(" code -> $code");
|
||||
if ( my $c =
|
||||
$self->launch( $session, $self->conf->{ext2FValidateCommand}, $code ) )
|
||||
|
|
|
@ -7,7 +7,8 @@ use JSON qw(from_json to_json);
|
|||
|
||||
our $VERSION = '2.0.0';
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin', 'Lemonldap::NG::Portal::Lib::U2F';
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin',
|
||||
'Lemonldap::NG::Portal::Lib::U2F';
|
||||
|
||||
# INITIALIZATION
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ extends 'Lemonldap::NG::Portal::Main::Auth',
|
|||
|
||||
# INTERFACE
|
||||
|
||||
has opList => ( is => 'rw', default => sub { [] } );
|
||||
has opList => ( is => 'rw', default => sub { [] } );
|
||||
has opNumber => ( is => 'rw', default => 0 );
|
||||
has path => ( is => 'rw', default => 'oauth2' );
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ sub send_mail {
|
|||
foreach ( keys %cid ) {
|
||||
$message->attach(
|
||||
Type => "image/" . ( $cid{$_} =~ m/\.(\w+)/ )[0],
|
||||
Id => $_,
|
||||
Id => $_,
|
||||
Path => $self->p->{templateDir} . "/" . $cid{$_},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,17 +16,16 @@ sub displayInit {
|
|||
my ($self) = @_;
|
||||
$self->skinRules( [] );
|
||||
if ( $self->conf->{portalSkinRules} ) {
|
||||
foreach my $skinRule ( sort keys %{ $self->conf->{portalSkinRules} } )
|
||||
{
|
||||
foreach my $skinRule ( sort keys %{ $self->conf->{portalSkinRules} } ) {
|
||||
my $sub = HANDLER->buildSub( HANDLER->substitute($skinRule) );
|
||||
if ($sub) {
|
||||
push @{ $self->skinRules },
|
||||
[ $self->conf->{portalSkinRules}->{$skinRule}, $sub ];
|
||||
[ $self->conf->{portalSkinRules}->{$skinRule}, $sub ];
|
||||
}
|
||||
else {
|
||||
$self->logger->error(
|
||||
qq(Skin rule "$skinRule" returns an error: )
|
||||
. HANDLER->tsv->{jail}->error );
|
||||
. HANDLER->tsv->{jail}->error );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +54,8 @@ sub display {
|
|||
AUTH_URL => $req->{data}->{_url},
|
||||
CHOICE_PARAM => $self->conf->{authChoiceParam},
|
||||
CHOICE_VALUE => $req->data->{_authChoice},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -79,11 +79,12 @@ sub display {
|
|||
CHOICE_PARAM => $self->conf->{authChoiceParam},
|
||||
CHOICE_VALUE => $req->data->{_authChoice},
|
||||
CHECK_LOGINS => $self->conf->{portalCheckLogins}
|
||||
&& $req->data->{login},
|
||||
&& $req->data->{login},
|
||||
ASK_LOGINS => $req->param('checkLogins') || 0,
|
||||
CONFIRMKEY => $self->stamp(),
|
||||
REMEMBER => $req->data->{confirmRemember},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -106,12 +107,13 @@ sub display {
|
|||
CHOICE_PARAM => $self->conf->{authChoiceParam},
|
||||
CHOICE_VALUE => $req->data->{_authChoice},
|
||||
CHECK_LOGINS => $self->conf->{portalCheckLogins}
|
||||
&& $req->data->{login},
|
||||
&& $req->data->{login},
|
||||
ASK_LOGINS => $req->param('checkLogins') || 0,
|
||||
CONFIRMKEY => $self->stamp(),
|
||||
LIST => $req->data->{list} || [],
|
||||
REMEMBER => $req->data->{confirmRemember},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -135,7 +137,8 @@ sub display {
|
|||
FORM_METHOD => $self->conf->{infoFormMethod},
|
||||
CHOICE_PARAM => $self->conf->{authChoiceParam},
|
||||
CHOICE_VALUE => $req->data->{_authChoice},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -150,14 +153,15 @@ sub display {
|
|||
my $p = $self->conf->{portal} . $self->conf->{issuerDBOpenIDPath};
|
||||
$p =~ s#(?<!:)/?\^?/#/#g;
|
||||
my $id = $req->{sessionInfo}
|
||||
->{ $self->conf->{openIdAttr} || $self->conf->{whatToTrace} };
|
||||
->{ $self->conf->{openIdAttr} || $self->conf->{whatToTrace} };
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
AUTH_ERROR => $self->error,
|
||||
AUTH_ERROR_TYPE => $req->error_type,
|
||||
PROVIDERURI => $p,
|
||||
MSG => $req->info(),
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -174,7 +178,8 @@ sub display {
|
|||
URL => $req->{urldc},
|
||||
HIDDEN_INPUTS => $self->buildHiddenForm($req),
|
||||
FORM_METHOD => $req->data->{redirectFormMethod} || 'get',
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -188,16 +193,16 @@ sub display {
|
|||
#utf8::decode($auth_user);
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
AUTH_USER =>
|
||||
$req->{sessionInfo}->{ $self->conf->{portalUserAttr} },
|
||||
NEWWINDOW => $self->conf->{portalOpenLinkInNewWindow},
|
||||
AUTH_USER => $req->{sessionInfo}->{ $self->conf->{portalUserAttr} },
|
||||
NEWWINDOW => $self->conf->{portalOpenLinkInNewWindow},
|
||||
LOGOUT_URL => $self->conf->{portal} . "?logout=1",
|
||||
APPSLIST_ORDER => $req->{sessionInfo}->{'_appsListOrder'},
|
||||
PING => $self->conf->{portalPingInterval},
|
||||
REQUIRE_OLDPASSWORD => $self->conf->{portalRequireOldPassword},
|
||||
HIDE_OLDPASSWORD => 0,
|
||||
$self->menu->params($req),
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -212,7 +217,8 @@ sub display {
|
|||
CONFIRMKEY => $self->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->data->{_url},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -227,7 +233,8 @@ sub display {
|
|||
CONFIRMKEY => $self->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->data->{_url},
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -240,14 +247,15 @@ sub display {
|
|||
or ( not $req->data->{noerror}
|
||||
and $req->userData
|
||||
and %{ $req->userData } )
|
||||
)
|
||||
)
|
||||
{
|
||||
$skinfile = 'error';
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
AUTH_ERROR => $req->error,
|
||||
AUTH_ERROR_TYPE => $req->error_type,
|
||||
( $req->data->{customScript}
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -260,21 +268,21 @@ sub display {
|
|||
my $login = $self->userId($req);
|
||||
$login = '' if ( $login eq 'anonymous' );
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
AUTH_ERROR => $req->error,
|
||||
AUTH_ERROR_TYPE => $req->error_type,
|
||||
AUTH_URL => $req->{data}->{_url},
|
||||
LOGIN => $login,
|
||||
CHECK_LOGINS => $self->conf->{portalCheckLogins},
|
||||
ASK_LOGINS => $req->param('checkLogins') || 0,
|
||||
DISPLAY_RESETPASSWORD =>
|
||||
$self->conf->{portalDisplayResetPassword},
|
||||
DISPLAY_REGISTER => $self->conf->{portalDisplayRegister},
|
||||
MAIL_URL => $self->conf->{mailUrl},
|
||||
REGISTER_URL => $self->conf->{registerUrl},
|
||||
HIDDEN_INPUTS => $self->buildHiddenForm($req),
|
||||
STAYCONNECTED => $self->conf->{stayConnected},
|
||||
( $req->data->{customScript}
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
AUTH_ERROR => $req->error,
|
||||
AUTH_ERROR_TYPE => $req->error_type,
|
||||
AUTH_URL => $req->{data}->{_url},
|
||||
LOGIN => $login,
|
||||
CHECK_LOGINS => $self->conf->{portalCheckLogins},
|
||||
ASK_LOGINS => $req->param('checkLogins') || 0,
|
||||
DISPLAY_RESETPASSWORD => $self->conf->{portalDisplayResetPassword},
|
||||
DISPLAY_REGISTER => $self->conf->{portalDisplayRegister},
|
||||
MAIL_URL => $self->conf->{mailUrl},
|
||||
REGISTER_URL => $self->conf->{registerUrl},
|
||||
HIDDEN_INPUTS => $self->buildHiddenForm($req),
|
||||
STAYCONNECTED => $self->conf->{stayConnected},
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
|
@ -306,12 +314,12 @@ sub display {
|
|||
or $req->{error} == PE_PASSWORDFORMEMPTY
|
||||
or ( $req->{error} == PE_PP_PASSWORD_EXPIRED
|
||||
and $self->conf->{ldapAllowResetExpiredPassword} )
|
||||
)
|
||||
)
|
||||
{
|
||||
%templateParams = (
|
||||
%templateParams,
|
||||
REQUIRE_OLDPASSWORD =>
|
||||
1, # Old password is required to check user credentials
|
||||
1, # Old password is required to check user credentials
|
||||
DISPLAY_FORM => 0,
|
||||
DISPLAY_OPENID_FORM => 0,
|
||||
DISPLAY_YUBIKEY_FORM => 0,
|
||||
|
@ -368,8 +376,8 @@ sub display {
|
|||
# Choose what form to display if not in a loop
|
||||
else {
|
||||
|
||||
my $displayType
|
||||
= eval { $self->_authentication->getDisplayType($req) };
|
||||
my $displayType =
|
||||
eval { $self->_authentication->getDisplayType($req) };
|
||||
|
||||
$self->logger->debug("Display type $displayType ");
|
||||
|
||||
|
@ -377,8 +385,7 @@ sub display {
|
|||
%templateParams,
|
||||
DISPLAY_FORM => $displayType =~ /\bstandardform\b/ ? 1
|
||||
: 0,
|
||||
DISPLAY_OPENID_FORM => $displayType =~ /\bopenidform\b/
|
||||
? 1
|
||||
DISPLAY_OPENID_FORM => $displayType =~ /\bopenidform\b/ ? 1
|
||||
: 0,
|
||||
DISPLAY_YUBIKEY_FORM => $displayType =~ /\byubikeyform\b/
|
||||
? 1
|
||||
|
@ -388,10 +395,9 @@ sub display {
|
|||
module => $displayType eq "logo"
|
||||
? $self->getModule( $req, 'auth' )
|
||||
: "",
|
||||
AUTH_LOOP => [],
|
||||
PORTAL_URL => (
|
||||
$displayType eq "logo" ? $self->conf->{portal} : 0
|
||||
),
|
||||
AUTH_LOOP => [],
|
||||
PORTAL_URL =>
|
||||
( $displayType eq "logo" ? $self->conf->{portal} : 0 ),
|
||||
MSG => $req->info(),
|
||||
);
|
||||
|
||||
|
@ -402,8 +408,8 @@ sub display {
|
|||
}
|
||||
|
||||
# Additional $req param
|
||||
%templateParams
|
||||
= ( %templateParams, %{ $req->{customParameters} // {} }, );
|
||||
%templateParams =
|
||||
( %templateParams, %{ $req->{customParameters} // {} }, );
|
||||
|
||||
$self->logger->debug("Skin returned: $skinfile");
|
||||
return ( $skinfile, \%templateParams );
|
||||
|
@ -419,16 +425,15 @@ sub staticFile {
|
|||
require Plack::Util;
|
||||
require Cwd;
|
||||
require HTTP::Date;
|
||||
open my $fh, '<:raw',
|
||||
$self->conf->{templatesDir}
|
||||
. "/$file"
|
||||
or return $self->sendError( $req,
|
||||
open my $fh, '<:raw', $self->conf->{templatesDir} . "/$file"
|
||||
or return $self->sendError( $req,
|
||||
$self->conf->{templatesDir} . "/$file: $!", 403 );
|
||||
my @stat = stat $file;
|
||||
Plack::Util::set_io_path( $fh, Cwd::realpath($file) );
|
||||
return [
|
||||
200,
|
||||
[ 'Content-Type' => $type,
|
||||
[
|
||||
'Content-Type' => $type,
|
||||
'Content-Length' => $stat[7],
|
||||
'Last-Modified' => HTTP::Date::time2str( $stat[9] )
|
||||
],
|
||||
|
@ -445,12 +450,11 @@ sub buildHiddenForm {
|
|||
|
||||
# Check XSS attacks
|
||||
next
|
||||
if $self->checkXSSAttack( $_,
|
||||
$req->{portalHiddenFormValues}->{$_} );
|
||||
if $self->checkXSSAttack( $_, $req->{portalHiddenFormValues}->{$_} );
|
||||
|
||||
# Build hidden input HTML code
|
||||
$val .= qq{<input type="hidden" name="$_" id="$_" value="}
|
||||
. $req->{portalHiddenFormValues}->{$_} . '" />';
|
||||
. $req->{portalHiddenFormValues}->{$_} . '" />';
|
||||
}
|
||||
|
||||
return $val;
|
||||
|
@ -521,12 +525,13 @@ sub mkSessionArray {
|
|||
displayError => $displayError,
|
||||
fields => [
|
||||
map { { name => $self->conf->{sessionDataToRemember}->{$_} } }
|
||||
@fields
|
||||
@fields
|
||||
],
|
||||
sessions => [
|
||||
map {
|
||||
my $session = $_;
|
||||
{ user => $session->{user},
|
||||
{
|
||||
user => $session->{user},
|
||||
utime => $session->{_utime},
|
||||
ip => $session->{ipAddr},
|
||||
values => [ map { { v => $session->{$_} } } @fields ],
|
||||
|
@ -548,7 +553,7 @@ sub mkOidcConsent {
|
|||
# Set default RP displayname
|
||||
foreach my $oidc ( keys %{ $self->conf->{oidcRPMetaDataOptions} } ) {
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$oidc}
|
||||
->{oidcRPMetaDataOptionsDisplayName} ||= $oidc;
|
||||
->{oidcRPMetaDataOptionsDisplayName} ||= $oidc;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,9 +579,9 @@ sub mkOidcConsent {
|
|||
$self->logger->debug("RP { $rp } Consent found");
|
||||
$consents->{$rp}->{epoch} = $_->{epoch};
|
||||
$consents->{$rp}->{scope} = $_->{scope};
|
||||
$consents->{$rp}->{displayName}
|
||||
= $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsDisplayName};
|
||||
$consents->{$rp}->{displayName} =
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsDisplayName};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,7 +591,8 @@ sub mkOidcConsent {
|
|||
params => {
|
||||
partners => [
|
||||
map {
|
||||
{ name => $_,
|
||||
{
|
||||
name => $_,
|
||||
epoch => $consents->{$_}->{epoch},
|
||||
scope => $consents->{$_}->{scope},
|
||||
displayName => $consents->{$_}->{displayName}
|
||||
|
|
|
@ -84,8 +84,9 @@ sub init {
|
|||
my ( $self, $args ) = @_;
|
||||
$args ||= {};
|
||||
$self->localConfig(
|
||||
{ %{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
|
||||
->getLocalConf('portal')
|
||||
{
|
||||
%{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
|
||||
->getLocalConf('portal')
|
||||
},
|
||||
%$args
|
||||
}
|
||||
|
@ -109,33 +110,33 @@ sub init {
|
|||
# Handle requests (other path may be declared in enabled plugins)
|
||||
$self
|
||||
|
||||
# "/" or undeclared paths
|
||||
->addUnauthRoute( '*' => 'login', ['GET'] )
|
||||
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
|
||||
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
|
||||
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
|
||||
# "/" or undeclared paths
|
||||
->addUnauthRoute( '*' => 'login', ['GET'] )
|
||||
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
|
||||
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
|
||||
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
|
||||
|
||||
# psgi.js
|
||||
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||
# psgi.js
|
||||
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||
|
||||
# portal.css
|
||||
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||
# portal.css
|
||||
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||
|
||||
# lmerror
|
||||
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||
# lmerror
|
||||
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||
|
||||
# Core REST API
|
||||
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
|
||||
->addAuthRoute( ping => 'authenticated', ['GET'] )
|
||||
# Core REST API
|
||||
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
|
||||
->addAuthRoute( ping => 'authenticated', ['GET'] )
|
||||
|
||||
# Refresh session
|
||||
->addAuthRoute( refresh => 'refresh', ['GET'] )
|
||||
# Refresh session
|
||||
->addAuthRoute( refresh => 'refresh', ['GET'] )
|
||||
|
||||
# Logout
|
||||
->addAuthRoute( logout => 'logout', ['GET'] );
|
||||
# Logout
|
||||
->addAuthRoute( logout => 'logout', ['GET'] );
|
||||
|
||||
# Default routes must point to routines declared above
|
||||
$self->defaultAuthRoute('');
|
||||
|
@ -169,8 +170,8 @@ sub reloadConf {
|
|||
$self->csp($csp);
|
||||
|
||||
# Initialize templateDir
|
||||
$self->{templateDir}
|
||||
= $self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
|
||||
$self->{templateDir} =
|
||||
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
|
||||
unless ( -d $self->{templateDir} ) {
|
||||
$self->error("Template dir $self->{templateDir} doesn't exist");
|
||||
return $self->fail;
|
||||
|
@ -190,8 +191,8 @@ sub reloadConf {
|
|||
# Initialize persistent session DB
|
||||
unless ( $self->conf->{persistentStorage} ) {
|
||||
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
|
||||
$self->conf->{persistentStorageOptions}
|
||||
= $self->conf->{globalStorageOptions};
|
||||
$self->conf->{persistentStorageOptions} =
|
||||
$self->conf->{globalStorageOptions};
|
||||
}
|
||||
|
||||
# Initialize cookie domain
|
||||
|
@ -215,19 +216,19 @@ sub reloadConf {
|
|||
return $self->fail;
|
||||
}
|
||||
$mod = $self->conf->{$type}
|
||||
unless ( $self->conf->{$type} eq 'Same' );
|
||||
unless ( $self->conf->{$type} eq 'Same' );
|
||||
my $module = '::' . ucfirst($type) . '::' . $mod;
|
||||
$module =~ s/Authentication/Auth/;
|
||||
|
||||
# Launch and initialize module
|
||||
return $self->fail
|
||||
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
|
||||
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
|
||||
}
|
||||
|
||||
# Load second-factor engine
|
||||
return $self->fail
|
||||
unless $self->{_sfEngine}
|
||||
= $self->loadPlugin( $self->conf->{'sfEngine'} );
|
||||
unless $self->{_sfEngine} =
|
||||
$self->loadPlugin( $self->conf->{'sfEngine'} );
|
||||
|
||||
# Initialize trusted domain regexp
|
||||
if ( $self->conf->{trustedDomains}
|
||||
|
@ -250,8 +251,8 @@ sub reloadConf {
|
|||
# - $domainlabel.$td
|
||||
# $domainlabel is build looking RFC2396
|
||||
# (see Regexp::Common::URI::RFC2396)
|
||||
$_
|
||||
=~ s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
|
||||
$_ =~
|
||||
s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
|
||||
$re->add("$_");
|
||||
}
|
||||
}
|
||||
|
@ -262,8 +263,8 @@ sub reloadConf {
|
|||
$self->logger->debug("Vhost $vhost added in trusted domains");
|
||||
$re->add( quotemeta($vhost) );
|
||||
$self->conf->{vhostOptions} ||= {};
|
||||
if ( my $tmp
|
||||
= $self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
|
||||
if ( my $tmp =
|
||||
$self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
|
||||
{
|
||||
foreach my $alias ( split /\s+/, $tmp ) {
|
||||
$self->logger->debug(
|
||||
|
@ -281,22 +282,22 @@ sub reloadConf {
|
|||
$self->{"_$type"} = {};
|
||||
if ( $self->conf->{$type} ) {
|
||||
for my $name ( sort keys %{ $self->conf->{$type} } ) {
|
||||
my $sub = HANDLER->buildSub(
|
||||
my $sub =
|
||||
HANDLER->buildSub(
|
||||
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
|
||||
if ($sub) {
|
||||
$self->{"_$type"}->{$name} = $sub;
|
||||
}
|
||||
else {
|
||||
$self->logger->error( "$type $name returns an error: "
|
||||
. HANDLER->tsv->{jail}->error );
|
||||
. HANDLER->tsv->{jail}->error );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->{_jsRedirect}
|
||||
= HANDLER->buildSub(
|
||||
HANDLER->substitute( $self->conf->{jsRedirect} ) )
|
||||
or $self->logger->error(
|
||||
$self->{_jsRedirect} =
|
||||
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
|
||||
or $self->logger->error(
|
||||
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
|
||||
|
||||
# Load plugins
|
||||
|
@ -332,7 +333,7 @@ sub loadPlugin {
|
|||
}
|
||||
my $obj;
|
||||
return 0
|
||||
unless ( $obj = $self->loadModule("$plugin") );
|
||||
unless ( $obj = $self->loadModule("$plugin") );
|
||||
return $self->findEP( $plugin, $obj );
|
||||
}
|
||||
|
||||
|
@ -361,7 +362,7 @@ sub findEP {
|
|||
if ( $obj->can('spRules') ) {
|
||||
foreach my $k ( keys %{ $obj->{spRules} } ) {
|
||||
$self->logger->info(
|
||||
"$k is defined more than one time, it can have some bad effect on Menu display"
|
||||
"$k is defined more than one time, it can have some bad effect on Menu display"
|
||||
) if ( $self->spRules->{$k} );
|
||||
$self->spRules->{$k} = $obj->{spRules}->{$k};
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ sub _redirect {
|
|||
# Restore urldc if auth doesn't need to dial with browser
|
||||
$self->restoreRequest( $req, $ir );
|
||||
return $self->run( @_, @path );
|
||||
}
|
||||
}
|
||||
: ()
|
||||
)
|
||||
]
|
||||
|
|
|
@ -17,11 +17,11 @@ use strict;
|
|||
use URI::Escape;
|
||||
|
||||
# List constants
|
||||
sub authProcess {qw(extractFormInfo getUser authenticate)}
|
||||
sub authProcess { qw(extractFormInfo getUser authenticate) }
|
||||
|
||||
sub sessionData {
|
||||
qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo
|
||||
setLocalGroups store secondFactor);
|
||||
setLocalGroups store secondFactor);
|
||||
}
|
||||
|
||||
sub validSession {
|
||||
|
@ -56,9 +56,11 @@ sub handler {
|
|||
if ( $sp or %{ $req->pdata } ) {
|
||||
my %v = (
|
||||
name => $self->conf->{cookieName} . 'pdata',
|
||||
( %{ $req->pdata }
|
||||
(
|
||||
%{ $req->pdata }
|
||||
? ( value => uri_escape( JSON::to_json( $req->pdata ) ) )
|
||||
: ( value => '',
|
||||
: (
|
||||
value => '',
|
||||
expires => 'Wed, 21 Oct 2015 00:00:00 GMT'
|
||||
)
|
||||
)
|
||||
|
@ -92,7 +94,8 @@ sub login {
|
|||
my ( $self, $req ) = @_;
|
||||
return $self->do(
|
||||
$req,
|
||||
[ 'controlUrl', @{ $self->beforeAuth },
|
||||
[
|
||||
'controlUrl', @{ $self->beforeAuth },
|
||||
$self->authProcess, @{ $self->betweenAuthAndData },
|
||||
$self->sessionData, @{ $self->afterData },
|
||||
$self->validSession, @{ $self->endAuth },
|
||||
|
@ -104,7 +107,8 @@ sub postLogin {
|
|||
my ( $self, $req ) = @_;
|
||||
return $self->do(
|
||||
$req,
|
||||
[ 'restoreArgs', 'controlUrl',
|
||||
[
|
||||
'restoreArgs', 'controlUrl',
|
||||
@{ $self->beforeAuth }, $self->authProcess,
|
||||
@{ $self->betweenAuthAndData }, $self->sessionData,
|
||||
@{ $self->afterData }, $self->validSession,
|
||||
|
@ -117,7 +121,8 @@ sub authenticatedRequest {
|
|||
my ( $self, $req ) = @_;
|
||||
return $self->do(
|
||||
$req,
|
||||
[ 'importHandlerData', 'controlUrl',
|
||||
[
|
||||
'importHandlerData', 'controlUrl',
|
||||
'checkLogout', @{ $self->forAuthUser }
|
||||
]
|
||||
);
|
||||
|
@ -127,7 +132,8 @@ sub postAuthenticatedRequest {
|
|||
my ( $self, $req ) = @_;
|
||||
return $self->do(
|
||||
$req,
|
||||
[ 'importHandlerData', 'restoreArgs',
|
||||
[
|
||||
'importHandlerData', 'restoreArgs',
|
||||
'controlUrl', 'checkLogout',
|
||||
@{ $self->forAuthUser }
|
||||
]
|
||||
|
@ -145,7 +151,8 @@ sub refresh {
|
|||
delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ );
|
||||
}
|
||||
$req->steps(
|
||||
[ 'getUser',
|
||||
[
|
||||
'getUser',
|
||||
@{ $self->betweenAuthAndData },
|
||||
'setAuthSessionInfo',
|
||||
'setSessionInfo',
|
||||
|
@ -163,21 +170,21 @@ sub refresh {
|
|||
if ($res) {
|
||||
$req->info(
|
||||
$self->loadTemplate(
|
||||
'simpleInfo',
|
||||
params => { trspan => 'rightsReloadNeedsLogout' }
|
||||
'simpleInfo', params => { trspan => 'rightsReloadNeedsLogout' }
|
||||
)
|
||||
);
|
||||
$req->urldc( $self->conf->{portal} );
|
||||
return $self->do( $req, [ sub {PE_INFO} ] );
|
||||
return $self->do( $req, [ sub { PE_INFO } ] );
|
||||
}
|
||||
return $self->do( $req, [ sub {PE_OK} ] );
|
||||
return $self->do( $req, [ sub { PE_OK } ] );
|
||||
}
|
||||
|
||||
sub logout {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->do(
|
||||
$req,
|
||||
[ 'controlUrl', @{ $self->beforeLogout },
|
||||
[
|
||||
'controlUrl', @{ $self->beforeLogout },
|
||||
'authLogout', 'deleteSession'
|
||||
]
|
||||
);
|
||||
|
@ -194,9 +201,9 @@ sub do {
|
|||
|
||||
# Update status
|
||||
if ( my $p = $self->HANDLER->tsv->{statusPipe} ) {
|
||||
$p->print(( $req->user ? $req->user : $req->address ) . ' => '
|
||||
. $req->uri
|
||||
. " $err\n" );
|
||||
$p->print( ( $req->user ? $req->user : $req->address ) . ' => '
|
||||
. $req->uri
|
||||
. " $err\n" );
|
||||
}
|
||||
|
||||
# Update history
|
||||
|
@ -208,7 +215,8 @@ sub do {
|
|||
if ( ( $err > 0 and !$req->id ) or $err eq PE_SESSIONNOTGRANTED ) {
|
||||
return [
|
||||
401,
|
||||
[ 'WWW-Authenticate' => "SSO " . $self->conf->{portal},
|
||||
[
|
||||
'WWW-Authenticate' => "SSO " . $self->conf->{portal},
|
||||
'Access-Control-Allow-Origin' => '*'
|
||||
],
|
||||
[qq'{"result":0,"error":$err}']
|
||||
|
@ -224,14 +232,16 @@ sub do {
|
|||
else {
|
||||
return $self->sendJSONresponse(
|
||||
$req,
|
||||
{ result => 1,
|
||||
{
|
||||
result => 1,
|
||||
code => $err
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $err
|
||||
if (
|
||||
$err
|
||||
and $err != PE_LOGOUT_OK
|
||||
and (
|
||||
$err != PE_REDIRECT
|
||||
|
@ -240,7 +250,7 @@ sub do {
|
|||
and $req->data->{redirectFormMethod} eq 'post' )
|
||||
or $req->info
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
my ( $tpl, $prms ) = $self->display($req);
|
||||
$self->logger->debug("Calling sendHtml with template $tpl");
|
||||
|
@ -258,20 +268,21 @@ sub do {
|
|||
|
||||
sub getModule {
|
||||
my ( $self, $req, $type ) = @_;
|
||||
if (my $mod = {
|
||||
if (
|
||||
my $mod = {
|
||||
auth => '_authentication',
|
||||
user => '_userDB',
|
||||
password => '_passwordDB'
|
||||
}->{$type}
|
||||
)
|
||||
)
|
||||
{
|
||||
if ( my $sub = $self->$mod->can('name') ) {
|
||||
return $sub->( $self->$mod, $req, $type );
|
||||
}
|
||||
else {
|
||||
my $s = ref( $self->$mod );
|
||||
$s
|
||||
=~ s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//;
|
||||
$s =~
|
||||
s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//;
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +299,7 @@ sub autoRedirect {
|
|||
|
||||
# Set redirection URL if needed
|
||||
$req->{urldc} ||= $self->conf->{portal}
|
||||
if ( $req->mustRedirect and not( $req->info ) );
|
||||
if ( $req->mustRedirect and not( $req->info ) );
|
||||
|
||||
# Redirection should be made if urldc defined
|
||||
if ( $req->{urldc} ) {
|
||||
|
@ -298,9 +309,8 @@ sub autoRedirect {
|
|||
$req->data->{redirectFormMethod} = "get";
|
||||
}
|
||||
else {
|
||||
return [
|
||||
302, [ Location => $req->{urldc}, @{ $req->respHeaders } ], []
|
||||
];
|
||||
return [ 302,
|
||||
[ Location => $req->{urldc}, @{ $req->respHeaders } ], [] ];
|
||||
}
|
||||
}
|
||||
my ( $tpl, $prms ) = $self->display($req);
|
||||
|
@ -322,7 +332,8 @@ sub getApacheSession {
|
|||
}
|
||||
|
||||
my $as = Lemonldap::NG::Common::Session->new(
|
||||
{ storageModule => $self->conf->{globalStorage},
|
||||
{
|
||||
storageModule => $self->conf->{globalStorage},
|
||||
storageModuleOptions => $self->conf->{globalStorageOptions},
|
||||
cacheModule => $self->conf->{localSessionStorage},
|
||||
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
|
||||
|
@ -336,7 +347,8 @@ sub getApacheSession {
|
|||
if ( my $err = $as->error ) {
|
||||
$self->lmLog(
|
||||
$err,
|
||||
( $err =~ /(?:Object does not exist|Invalid session ID)/
|
||||
(
|
||||
$err =~ /(?:Object does not exist|Invalid session ID)/
|
||||
? 'notice'
|
||||
: 'error'
|
||||
)
|
||||
|
@ -350,16 +362,17 @@ sub getApacheSession {
|
|||
}
|
||||
|
||||
my $now = time;
|
||||
if ( $id
|
||||
if (
|
||||
$id
|
||||
and defined $as->data->{_utime}
|
||||
and (
|
||||
$now - $as->data->{_utime} > $self->conf->{timeout}
|
||||
or ( $self->conf->{timeoutActivity}
|
||||
and $as->data->{_lastSeen}
|
||||
and $now - $as->data->{_lastSeen}
|
||||
> $self->conf->{timeoutActivity} )
|
||||
)
|
||||
and $now - $as->data->{_lastSeen} >
|
||||
$self->conf->{timeoutActivity} )
|
||||
)
|
||||
)
|
||||
{
|
||||
$self->logger->debug("Session $args{kind} $id expired");
|
||||
return;
|
||||
|
@ -382,7 +395,8 @@ sub getPersistentSession {
|
|||
$info->{_session_uid} = $uid;
|
||||
|
||||
my $ps = Lemonldap::NG::Common::Session->new(
|
||||
{ storageModule => $self->conf->{persistentStorage},
|
||||
{
|
||||
storageModule => $self->conf->{persistentStorage},
|
||||
storageModuleOptions => $self->conf->{persistentStorageOptions},
|
||||
id => $pid,
|
||||
force => 1,
|
||||
|
@ -423,11 +437,10 @@ sub updatePersistentSession {
|
|||
# Return if no infos to update
|
||||
return () unless ( ref $infos eq 'HASH' and %$infos );
|
||||
$uid ||= $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
||||
|| $req->userData->{ $self->conf->{whatToTrace} };
|
||||
|| $req->userData->{ $self->conf->{whatToTrace} };
|
||||
$self->logger->debug("Found 'whatToTrace' -> $uid");
|
||||
unless ($uid) {
|
||||
$self->logger->debug(
|
||||
'No uid found, skipping updatePersistentSession');
|
||||
$self->logger->debug('No uid found, skipping updatePersistentSession');
|
||||
return ();
|
||||
}
|
||||
$self->logger->debug("Update $uid persistent session");
|
||||
|
@ -469,14 +482,14 @@ sub updateSession {
|
|||
foreach ( keys %$infos ) {
|
||||
$self->logger->debug(
|
||||
"Update sessionInfo $_ with " . $infos->{$_} );
|
||||
$req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_}
|
||||
= $infos->{$_};
|
||||
$req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_} =
|
||||
$infos->{$_};
|
||||
}
|
||||
|
||||
# Update session in global storage with _updateTime
|
||||
$infos->{_updateTime} = strftime( "%Y%m%d%H%M%S", localtime() );
|
||||
if ( my $apacheSession
|
||||
= $self->getApacheSession( $id, info => $infos ) )
|
||||
if ( my $apacheSession =
|
||||
$self->getApacheSession( $id, info => $infos ) )
|
||||
{
|
||||
if ( $apacheSession->error ) {
|
||||
$self->logger->error("Cannot update session $id");
|
||||
|
@ -559,10 +572,10 @@ sub isTrustedUrl {
|
|||
|
||||
sub stamp {
|
||||
my $self = shift;
|
||||
my $res
|
||||
= $self->conf->{cipher}
|
||||
? $self->conf->{cipher}->encrypt( time() )
|
||||
: 1;
|
||||
my $res =
|
||||
$self->conf->{cipher}
|
||||
? $self->conf->{cipher}->encrypt( time() )
|
||||
: 1;
|
||||
$res =~ s/\+/%2B/g;
|
||||
return $res;
|
||||
}
|
||||
|
@ -694,7 +707,7 @@ sub cookie {
|
|||
$h{path} ||= '/';
|
||||
$h{HttpOnly} //= $self->conf->{httpOnly};
|
||||
$h{max_age} //= $self->conf->{cookieExpiration}
|
||||
if ( $self->conf->{cookieExpiration} );
|
||||
if ( $self->conf->{cookieExpiration} );
|
||||
foreach (qw(domain path expires max_age HttpOnly)) {
|
||||
my $f = $_;
|
||||
$f =~ s/_/-/g;
|
||||
|
@ -717,8 +730,8 @@ sub sendHtml {
|
|||
my ( $self, $req, $template, %args ) = @_;
|
||||
my $res = $self->SUPER::sendHtml( $req, $template, %args );
|
||||
push @{ $res->[1] },
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Content-Type-Options' => 'nosniff';
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Content-Type-Options' => 'nosniff';
|
||||
|
||||
# Set authorized URL for POST
|
||||
my $csp = $self->csp . "form-action 'self'";
|
||||
|
@ -732,14 +745,13 @@ sub sendHtml {
|
|||
if ( defined $url ) {
|
||||
$self->logger->debug("Required Params URL : $url");
|
||||
if ( $url =~ s#(https?://[^/]+).*#$1# ) {
|
||||
$self->logger->debug(
|
||||
"Set CSP form-action with Params URL : $url");
|
||||
$self->logger->debug("Set CSP form-action with Params URL : $url");
|
||||
$csp .= " $url";
|
||||
}
|
||||
}
|
||||
if ( defined $req->{cspFormAction} ) {
|
||||
$self->logger->debug( "Set CSP form-action with request URL: "
|
||||
. $req->{cspFormAction} );
|
||||
$self->logger->debug(
|
||||
"Set CSP form-action with request URL: " . $req->{cspFormAction} );
|
||||
$csp .= " " . $req->{cspFormAction};
|
||||
}
|
||||
$csp .= ';';
|
||||
|
@ -754,7 +766,7 @@ sub sendHtml {
|
|||
my @url;
|
||||
if ( $req->info ) {
|
||||
@url = map { s#https?://([^/]+).*#$1#; $_ }
|
||||
( $req->info =~ /<iframe.*?src="(.*?)"/sg );
|
||||
( $req->info =~ /<iframe.*?src="(.*?)"/sg );
|
||||
}
|
||||
if (@url) {
|
||||
$csp .= join( ' ', 'child-src', @url ) . ';';
|
||||
|
@ -768,16 +780,17 @@ sub sendHtml {
|
|||
|
||||
sub sendCss {
|
||||
my ( $self, $req ) = @_;
|
||||
my $s
|
||||
= 'html,body{background:url("'
|
||||
. $self->staticPrefix
|
||||
. '/common/backgrounds/'
|
||||
. $self->conf->{portalSkinBackground}
|
||||
. '") no-repeat center fixed;'
|
||||
. 'background-size:cover;}';
|
||||
my $s =
|
||||
'html,body{background:url("'
|
||||
. $self->staticPrefix
|
||||
. '/common/backgrounds/'
|
||||
. $self->conf->{portalSkinBackground}
|
||||
. '") no-repeat center fixed;'
|
||||
. 'background-size:cover;}';
|
||||
return [
|
||||
200,
|
||||
[ 'Content-Type' => 'text/css',
|
||||
[
|
||||
'Content-Type' => 'text/css',
|
||||
'Content-Length' => length($s),
|
||||
'Cache-Control' => 'public,max-age=3600',
|
||||
],
|
||||
|
@ -799,16 +812,16 @@ sub lmError {
|
|||
|
||||
# Error code
|
||||
$templateParams{"ERROR$_"} = ( $httpError == $_ ? 1 : 0 )
|
||||
foreach ( 403, 404, 500, 502, 503 );
|
||||
foreach ( 403, 404, 500, 502, 503 );
|
||||
return $self->sendHtml( $req, 'error', params => \%templateParams );
|
||||
}
|
||||
|
||||
sub rebuildCookies {
|
||||
my ( $self, $req ) = @_;
|
||||
my @tmp;
|
||||
for ( my $i = 0; $i < @{ $req->{respHeaders} }; $i += 2 ) {
|
||||
for ( my $i = 0 ; $i < @{ $req->{respHeaders} } ; $i += 2 ) {
|
||||
push @tmp, $req->respHeaders->[0], $req->respHeaders->[1]
|
||||
unless ( $req->respHeaders->[0] eq 'Set-Cookie' );
|
||||
unless ( $req->respHeaders->[0] eq 'Set-Cookie' );
|
||||
}
|
||||
$req->{respHeaders} = \@tmp;
|
||||
$self->buildCookie($req);
|
||||
|
@ -831,7 +844,7 @@ sub tplParams {
|
|||
sub registerLogin {
|
||||
my ( $self, $req ) = @_;
|
||||
return
|
||||
unless ( $self->conf->{loginHistoryEnabled}
|
||||
unless ( $self->conf->{loginHistoryEnabled}
|
||||
and defined $req->authResult );
|
||||
my $history = $req->sessionInfo->{_loginHistory} ||= {};
|
||||
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
|
||||
|
@ -841,17 +854,17 @@ sub registerLogin {
|
|||
# Gather current login's parameters
|
||||
my $login = $self->_sumUpSession( $req->{sessionInfo}, 1 );
|
||||
$login->{error} = $self->error( $req->authResult )
|
||||
if ( $req->authResult );
|
||||
if ( $req->authResult );
|
||||
|
||||
$self->logger->debug(" Current login -> " . $login->{error}) if ( $login->{error} );
|
||||
$self->logger->debug( " Current login -> " . $login->{error} )
|
||||
if ( $login->{error} );
|
||||
|
||||
# Add current login into history
|
||||
unshift @{ $history->{$type} }, $login;
|
||||
|
||||
# Forget oldest logins
|
||||
splice @{ $history->{$type} }, $self->conf->{ $type . "Number" }
|
||||
if (
|
||||
scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
||||
if ( scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
||||
|
||||
# Save into persistent session
|
||||
$self->updatePersistentSession( $req, { _loginHistory => $history, } );
|
||||
|
@ -862,12 +875,12 @@ sub registerLogin {
|
|||
# @return hashref
|
||||
sub _sumUpSession {
|
||||
my ( $self, $session, $withoutUser ) = @_;
|
||||
my $res
|
||||
= $withoutUser
|
||||
? {}
|
||||
: { user => $session->{ $self->conf->{whatToTrace} } };
|
||||
my $res =
|
||||
$withoutUser
|
||||
? {}
|
||||
: { user => $session->{ $self->conf->{whatToTrace} } };
|
||||
$res->{$_} = $session->{$_}
|
||||
foreach ( "_utime", "ipAddr",
|
||||
foreach ( "_utime", "ipAddr",
|
||||
keys %{ $self->conf->{sessionDataToRemember} } );
|
||||
return $res;
|
||||
}
|
||||
|
@ -876,12 +889,12 @@ sub _sumUpSession {
|
|||
sub loadTemplate {
|
||||
my ( $self, $name, %prm ) = @_;
|
||||
$name .= '.tpl';
|
||||
my $file
|
||||
= $self->conf->{templateDir} . '/'
|
||||
. $self->conf->{portalSkin} . '/'
|
||||
. $name;
|
||||
my $file =
|
||||
$self->conf->{templateDir} . '/'
|
||||
. $self->conf->{portalSkin} . '/'
|
||||
. $name;
|
||||
$file = $self->conf->{templateDir} . '/common/' . $name
|
||||
unless ( -e $file );
|
||||
unless ( -e $file );
|
||||
unless ( -e $file ) {
|
||||
die "Unable to find $name in $self->conf->{templateDir}";
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ package Lemonldap::NG::Portal::Main::SecondFactor;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_SENDRESPONSE
|
||||
PE_OK
|
||||
PE_NOTOKEN
|
||||
PE_TOKENEXPIRED
|
||||
PE_BADCREDENTIALS
|
||||
PE_SENDRESPONSE
|
||||
PE_OK
|
||||
PE_NOTOKEN
|
||||
PE_TOKENEXPIRED
|
||||
PE_BADCREDENTIALS
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
@ -19,8 +19,8 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|||
has ott => (
|
||||
is => 'rw',
|
||||
default => sub {
|
||||
my $ott = $_[0]->{p}
|
||||
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||
my $ott =
|
||||
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||
$ott->timeout( $_[0]->{conf}->{formTimeout} );
|
||||
return $ott;
|
||||
}
|
||||
|
@ -52,8 +52,7 @@ sub _redirect {
|
|||
my ( $self, $req ) = @_;
|
||||
my $arg = $req->env->{QUERY_STRING};
|
||||
return [
|
||||
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ],
|
||||
[]
|
||||
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ], []
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -66,16 +65,15 @@ sub _verify {
|
|||
# Check token
|
||||
my $token;
|
||||
unless ( $token = $req->param('token') ) {
|
||||
$self->userLogger->error(
|
||||
$self->prefix . ' 2F access without token' );
|
||||
$self->userLogger->error( $self->prefix . ' 2F access without token' );
|
||||
$req->mustRedirect(1);
|
||||
return $self->p->do( $req, [ sub {PE_NOTOKEN} ] );
|
||||
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
|
||||
}
|
||||
|
||||
my $session;
|
||||
unless ( $session = $self->ott->getToken($token) ) {
|
||||
$self->userLogger->info('Token expired');
|
||||
return $self->p->do( $req, [ sub {PE_TOKENEXPIRED} ] );
|
||||
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
|
||||
}
|
||||
|
||||
# Launch second factor verification
|
||||
|
@ -90,7 +88,7 @@ sub _verify {
|
|||
$req->{sessionInfo}->{_utime} = delete $req->{sessionInfo}->{_2fUtime};
|
||||
$req->authResult(PE_BADCREDENTIALS);
|
||||
return $self->p->do( $req,
|
||||
[ $self->p->storeHistory($req), sub {$res} ] );
|
||||
[ $self->p->storeHistory($req), sub { $res } ] );
|
||||
}
|
||||
|
||||
# Else restore session
|
||||
|
@ -101,15 +99,22 @@ sub _verify {
|
|||
$self->p->rebuildCookies($req);
|
||||
$req->mustRedirect(1);
|
||||
$self->userLogger->notice( $self->prefix
|
||||
. '2F verification for '
|
||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
. '2F verification for '
|
||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
|
||||
if ( my $l = $self->conf->{ $self->prefix . '2fAuthnLevel' } ) {
|
||||
$self->p->updateSession( $req, { authenticationLevel => $l } );
|
||||
}
|
||||
$req->authResult(PE_SENDRESPONSE);
|
||||
return $self->p->do( $req,
|
||||
[ @{ $self->p->afterData }, $self->p->validSession, @{ $self->p->endAuth }, sub {PE_OK} ] );
|
||||
return $self->p->do(
|
||||
$req,
|
||||
[
|
||||
@{ $self->p->afterData },
|
||||
$self->p->validSession,
|
||||
@{ $self->p->endAuth },
|
||||
sub { PE_OK }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -40,11 +40,9 @@ sub run {
|
|||
return PE_OK if ( $countFailed < 3 );
|
||||
|
||||
foreach ( 0 .. 2 ) {
|
||||
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] )
|
||||
{
|
||||
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] ) {
|
||||
push @lastFailedLoginEpoch,
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]
|
||||
->{_utime};
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]->{_utime};
|
||||
}
|
||||
}
|
||||
$self->logger->debug("BruteForceProtection enabled");
|
||||
|
@ -53,7 +51,7 @@ sub run {
|
|||
$MaxAge = $lastFailedLoginEpoch[0] - $lastFailedLoginEpoch[2];
|
||||
$self->logger->debug(" -> MaxAge = $MaxAge");
|
||||
return PE_OK
|
||||
if ( $MaxAge > $self->conf->{bruteForceProtectionMaxAge} );
|
||||
if ( $MaxAge > $self->conf->{bruteForceProtectionMaxAge} );
|
||||
|
||||
# Delta between the two last failed logins -> Auth_N - Auth_N-1
|
||||
my $delta = time - $lastFailedLoginEpoch[1];
|
||||
|
@ -61,7 +59,7 @@ sub run {
|
|||
|
||||
# Delta between the two last failed logins < 30s => wait
|
||||
return PE_OK
|
||||
unless ( $delta <= $self->conf->{bruteForceProtectionTempo} );
|
||||
unless ( $delta <= $self->conf->{bruteForceProtectionTempo} );
|
||||
|
||||
# Account locked
|
||||
#shift @{ $req->sessionInfo->{_loginHistory}->{failedLogin} };
|
||||
|
|
|
@ -12,7 +12,7 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|||
|
||||
use constant forAuthUser => 'run';
|
||||
|
||||
sub init {1}
|
||||
sub init { 1 }
|
||||
|
||||
# RUNNING METHOD
|
||||
|
||||
|
@ -25,8 +25,8 @@ sub run {
|
|||
$self->logger->debug( "Delta with last Authn -> " . $delta );
|
||||
|
||||
$delta <= $self->conf->{portalForceAuthnInterval}
|
||||
? return PE_OK
|
||||
: return PE_MUSTAUTHN;
|
||||
? return PE_OK
|
||||
: return PE_MUSTAUTHN;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ package Lemonldap::NG::Portal::Plugins::GrantSession;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_OK
|
||||
PE_SESSIONNOTGRANTED
|
||||
PE_OK
|
||||
PE_SESSIONNOTGRANTED
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
@ -22,11 +22,11 @@ sub init {
|
|||
$self->logger->debug("GrantRule key -> $_");
|
||||
$self->logger->debug(
|
||||
"GrantRule value -> " . $self->conf->{grantSessionRules}->{$_} );
|
||||
my $rule = $hd->buildSub(
|
||||
my $rule =
|
||||
$hd->buildSub(
|
||||
$hd->substitute( $self->conf->{grantSessionRules}->{$_} ) );
|
||||
unless ($rule) {
|
||||
$self->error(
|
||||
"Bad grantSession rule " . $hd->tsv->{jail}->error );
|
||||
$self->error( "Bad grantSession rule " . $hd->tsv->{jail}->error );
|
||||
return 0;
|
||||
}
|
||||
$self->rules->{$_} = $rule;
|
||||
|
@ -45,7 +45,7 @@ sub run {
|
|||
|
||||
foreach ( sort sortByComment keys %{ $self->rules } ) {
|
||||
$self->logger->debug( "Grant session condition -> "
|
||||
. $self->conf->{grantSessionRules}->{$_} );
|
||||
. $self->conf->{grantSessionRules}->{$_} );
|
||||
unless ( $self->rules->{$_}->( $req, $req->sessionInfo ) ) {
|
||||
$req->userData( {} );
|
||||
|
||||
|
@ -54,7 +54,7 @@ sub run {
|
|||
if ($1) {
|
||||
$self->logger->debug("Message -> $1");
|
||||
|
||||
# Message can contain session data as user attributes or macros
|
||||
# Message can contain session data as user attributes or macros
|
||||
my $hd = $self->p->HANDLER;
|
||||
my $msg = $hd->substitute($1);
|
||||
unless ( $msg = $hd->buildSub($msg) ) {
|
||||
|
@ -68,17 +68,17 @@ sub run {
|
|||
)
|
||||
);
|
||||
$self->userLogger->error( 'User '
|
||||
. $req->sessionInfo->{uid}
|
||||
. " was not granted to open session (rule -> $msg)" );
|
||||
. $req->sessionInfo->{uid}
|
||||
. " was not granted to open session (rule -> $msg)" );
|
||||
$req->urldc( $self->conf->{portal} );
|
||||
return $req->authResult(PE_SESSIONNOTGRANTED);
|
||||
}
|
||||
else {
|
||||
$self->userLogger->error( 'User '
|
||||
. $req->sessionInfo->{uid}
|
||||
. " was not granted to open session (rule -> "
|
||||
. $self->conf->{grantSessionRules}->{$_}
|
||||
. ")" );
|
||||
. $req->sessionInfo->{uid}
|
||||
. " was not granted to open session (rule -> "
|
||||
. $self->conf->{grantSessionRules}->{$_}
|
||||
. ")" );
|
||||
$req->urldc( $self->conf->{portal} );
|
||||
return $req->authResult(PE_SESSIONNOTGRANTED);
|
||||
}
|
||||
|
|
|
@ -28,15 +28,15 @@ sub run {
|
|||
$req->sessionInfo->{_loginHistory}->{successLogin},
|
||||
'lastLogins', 0, 0 )
|
||||
: ""
|
||||
)
|
||||
. ("<hr>") .
|
||||
(
|
||||
)
|
||||
. ("<hr>")
|
||||
. (
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin}
|
||||
? $self->p->mkSessionArray(
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin},
|
||||
'lastFailedLogins', 0, 1 )
|
||||
: ""
|
||||
)
|
||||
)
|
||||
);
|
||||
unless ( $req->info ) {
|
||||
$req->info( $self->loadTemplate('noHistory') );
|
||||
|
|
|
@ -32,7 +32,7 @@ sub createUser {
|
|||
givenName => $gn,
|
||||
unicodePwd => utf8(
|
||||
chr(34) . $req->data->{registerInfo}->{password} . chr(34)
|
||||
)->utf16le(),
|
||||
)->utf16le(),
|
||||
mail => $req->data->{registerInfo}->{mail},
|
||||
]
|
||||
);
|
||||
|
|
|
@ -9,7 +9,7 @@ sub new {
|
|||
}
|
||||
|
||||
my $res = $self->{p}->loadModule( $self->{conf}->{customRegister} );
|
||||
unless($res) {
|
||||
unless ($res) {
|
||||
die 'Unable to load register module ' . $self->{conf}->{customRegister};
|
||||
}
|
||||
return $res;
|
||||
|
|
|
@ -20,7 +20,7 @@ SKIP: {
|
|||
);
|
||||
$dbh->prepare(
|
||||
q{INSERT INTO notifications VALUES ('dwho','testref','2016-05-30 00:00:00',?,null,null)}
|
||||
)->execute(
|
||||
)->execute(
|
||||
'[
|
||||
{
|
||||
"uid": "dwho",
|
||||
|
@ -32,7 +32,7 @@ q{INSERT INTO notifications VALUES ('dwho','testref','2016-05-30 00:00:00',?,nul
|
|||
"check": ["Accept test"]
|
||||
}
|
||||
]'
|
||||
);
|
||||
);
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{
|
||||
|
|
|
@ -111,8 +111,8 @@ ok(
|
|||
),
|
||||
'New auth query'
|
||||
);
|
||||
expectAuthenticatedAs($res,'dwho');
|
||||
ok($res->[2]->[0] =~ /yourApp/s, 'Menu displayed');
|
||||
expectAuthenticatedAs( $res, 'dwho' );
|
||||
ok( $res->[2]->[0] =~ /yourApp/s, 'Menu displayed' );
|
||||
count(2);
|
||||
|
||||
clean_sessions();
|
||||
|
|
|
@ -16,7 +16,6 @@ my $mailSend = 0;
|
|||
|
||||
my $mail2 = 0;
|
||||
|
||||
|
||||
SKIP: {
|
||||
eval
|
||||
'require Email::Sender::Simple;use GD::SecurityImage;use Image::Magick;';
|
||||
|
|
|
@ -10,7 +10,8 @@ BEGIN {
|
|||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
|
@ -22,7 +23,8 @@ my $client = LLNG::Manager::Test->new(
|
|||
);
|
||||
|
||||
## First successful connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
|
@ -37,7 +39,8 @@ expectRedirection( $res, 'http://auth.example.com/' );
|
|||
$client->logout($id1);
|
||||
|
||||
## Second successful connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
|
@ -52,7 +55,8 @@ expectRedirection( $res, 'http://auth.example.com/' );
|
|||
$client->logout($id1);
|
||||
|
||||
## First failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -63,7 +67,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Second failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -74,7 +79,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Third failed connection -> rejected
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23,
|
||||
|
@ -89,7 +95,8 @@ count(1);
|
|||
sleep 1;
|
||||
|
||||
## Fourth failed connection -> Rejected
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23,
|
||||
|
@ -104,7 +111,8 @@ count(1);
|
|||
sleep 2;
|
||||
|
||||
## Third successful connection -> Rejected
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
|
@ -119,7 +127,8 @@ count(1);
|
|||
sleep 3;
|
||||
|
||||
## Fourth successful connection -> Accepted
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
@ -131,7 +140,7 @@ count(1);
|
|||
$id1 = expectCookie($res);
|
||||
|
||||
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||
my @cf = ( $res->[2]->[0] =~ /PE5<\/td>/gs );
|
||||
|
|
|
@ -10,7 +10,8 @@ BEGIN {
|
|||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
authentication => 'Demo',
|
||||
userdb => 'Same',
|
||||
portalForceAuthn => 1,
|
||||
|
@ -19,7 +20,8 @@ my $client = LLNG::Manager::Test->new(
|
|||
}
|
||||
);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23
|
||||
|
@ -33,7 +35,8 @@ count(1);
|
|||
|
||||
sleep 3;
|
||||
|
||||
ok( $res = $client->_get(
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
cookie => "lemonldap=$id1",
|
||||
accept => 'text/html',
|
||||
|
@ -41,10 +44,11 @@ ok( $res = $client->_get(
|
|||
'Form ReAuthentification'
|
||||
);
|
||||
ok( $res->[2]->[0] =~ qr%<span trspan="PE87"></span>%, 'Found PE87 code' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
|
@ -56,7 +60,8 @@ expectOK($res);
|
|||
$id1 = expectCookie($res);
|
||||
count(1);
|
||||
|
||||
ok( $res = $client->_get(
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
cookie => "lemonldap=$id1",
|
||||
accept => 'text/html',
|
||||
|
@ -65,7 +70,7 @@ ok( $res = $client->_get(
|
|||
);
|
||||
ok( $res->[2]->[0] =~ qr%<span trspan="yourApps">Your applications</span>%,
|
||||
'Found applications list' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$client->logout($id1);
|
||||
|
|
|
@ -10,7 +10,8 @@ BEGIN {
|
|||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userdb => 'Same',
|
||||
|
@ -23,7 +24,8 @@ my $client = LLNG::Manager::Test->new(
|
|||
}
|
||||
);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
accept => 'text/html',
|
||||
|
@ -33,15 +35,16 @@ ok( $res = $client->_post(
|
|||
);
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ /<h3 trspan="dwho not allowed">dwho not allowed<\/h3>/,
|
||||
'dwho rejected with custom message and session data'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
'dwho rejected with custom message and session data' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ qr%src="/static/common/js/info.(?:min\.)?js"></script>%,
|
||||
'Found INFO js'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
'Found INFO js' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=rtyler&password=rtyler'),
|
||||
length => 27
|
||||
|
@ -52,7 +55,8 @@ count(1);
|
|||
expectOK($res);
|
||||
expectCookie($res);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=mrsmith&password=mrsmith'),
|
||||
accept => 'text/html',
|
||||
|
@ -61,7 +65,8 @@ ok( $res = $client->_post(
|
|||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ /<span trmsg="4"><\/span><\/div>/,
|
||||
ok(
|
||||
$res->[2]->[0] =~ /<span trmsg="4"><\/span><\/div>/,
|
||||
'rtyler rejected with PE_SESSIONNOTGRANTED'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
@ -72,7 +77,8 @@ count(1);
|
|||
|
||||
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
|
||||
$client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
authentication => 'Demo',
|
||||
userdb => 'Same',
|
||||
grantSessionRules => { '' => '$uid eq "dwho"', }
|
||||
|
@ -80,7 +86,8 @@ $client = LLNG::Manager::Test->new(
|
|||
}
|
||||
);
|
||||
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23
|
||||
|
|
|
@ -9,7 +9,8 @@ BEGIN {
|
|||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
|
@ -20,7 +21,8 @@ my $client = LLNG::Manager::Test->new(
|
|||
);
|
||||
|
||||
## First successful connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
@ -32,7 +34,7 @@ count(1);
|
|||
expectOK($res);
|
||||
my $id1 = expectCookie($res);
|
||||
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||
or explain( $res->[2]->[0], 'trspan="noHistory"' );
|
||||
or explain( $res->[2]->[0], 'trspan="noHistory"' );
|
||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||
|
||||
# History with 1 successLogin
|
||||
|
@ -47,7 +49,8 @@ expectOK($res);
|
|||
$client->logout($id1);
|
||||
|
||||
## Second successful connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
@ -69,7 +72,8 @@ count(2);
|
|||
$client->logout($id1);
|
||||
|
||||
## First failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -80,7 +84,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Second failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -91,7 +96,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Third successful connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
|
|
@ -34,13 +34,13 @@ LWP::Protocol::PSGI->register(
|
|||
my $client = LLNG::Manager::Test->new(
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
rest2fActivation => 1,
|
||||
rest2fInitUrl => 'http://auth.example.com/init',
|
||||
rest2fInitArgs => { name => 'uid' },
|
||||
rest2fVerifyUrl => 'http://auth.example.com/vrfy',
|
||||
rest2fVerifyArgs => { code => 'code' },
|
||||
loginHistoryEnabled => 1,
|
||||
logLevel => 'error',
|
||||
rest2fActivation => 1,
|
||||
rest2fInitUrl => 'http://auth.example.com/init',
|
||||
rest2fInitArgs => { name => 'uid' },
|
||||
rest2fVerifyUrl => 'http://auth.example.com/vrfy',
|
||||
rest2fVerifyArgs => { code => 'code' },
|
||||
loginHistoryEnabled => 1,
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
}
|
||||
|
@ -74,9 +74,8 @@ ok(
|
|||
);
|
||||
my $id = expectCookie($res);
|
||||
|
||||
|
||||
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||
ok( @c == 1, 'One entry found' );
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ use_ok('Lemonldap::NG::Common::FormEncode');
|
|||
count(1);
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
ext2fActivation => 1,
|
||||
ext2FSendCommand => 't/sendOTP.pl -uid $uid',
|
||||
|
@ -26,7 +27,8 @@ my $client = LLNG::Manager::Test->new(
|
|||
my $res;
|
||||
|
||||
## First failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -37,7 +39,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Second failed connection
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23
|
||||
|
@ -48,7 +51,8 @@ count(1);
|
|||
expectReject($res);
|
||||
|
||||
## Third failed connection -> rejected
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=ohwd'),
|
||||
length => 23,
|
||||
|
@ -64,7 +68,8 @@ sleep 2;
|
|||
|
||||
# Try to authenticate
|
||||
# -------------------
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
@ -74,18 +79,19 @@ ok( $res = $client->_post(
|
|||
);
|
||||
count(1);
|
||||
|
||||
my ( $host, $url, $query )
|
||||
= expectForm( $res, undef, '/ext2fcheck', 'token', 'code',
|
||||
'checkLogins' );
|
||||
my ( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/ext2fcheck', 'token', 'code', 'checkLogins' );
|
||||
|
||||
ok( $res->[2]->[0]
|
||||
=~ qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$query =~ s/code=/code=123456/;
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/ext2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
|
@ -102,7 +108,8 @@ sleep 4;
|
|||
|
||||
# Try to authenticate again
|
||||
# -------------------------
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&checkLogins=1'),
|
||||
length => 37,
|
||||
|
@ -112,18 +119,19 @@ ok( $res = $client->_post(
|
|||
);
|
||||
count(1);
|
||||
|
||||
( $host, $url, $query )
|
||||
= expectForm( $res, undef, '/ext2fcheck', 'token', 'code',
|
||||
'checkLogins' );
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/ext2fcheck', 'token', 'code', 'checkLogins' );
|
||||
|
||||
ok( $res->[2]->[0]
|
||||
=~ qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$query =~ s/code=/code=123456/;
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/ext2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
|
@ -136,11 +144,11 @@ count(1);
|
|||
my $id = expectCookie($res);
|
||||
|
||||
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||
ok( @c == 4, 'Four entries found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$client->logout($id);
|
||||
|
|
|
@ -9,15 +9,15 @@ use_ok('Lemonldap::NG::Common::FormEncode');
|
|||
count(1);
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{ ini => {
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
ext2fActivation => 1,
|
||||
ext2FSendCommand => 't/sendOTP.pl -uid $uid',
|
||||
ext2FValidateCommand => 't/vrfyOTP.pl -uid $uid -code $code',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
grantSessionRules =>
|
||||
{ 'Dwho_notAllowed##Test' => '$uid ne "dwho"' }
|
||||
grantSessionRules => { 'Dwho_notAllowed##Test' => '$uid ne "dwho"' }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -26,7 +26,8 @@ my $res;
|
|||
|
||||
# Try to authenticate
|
||||
# -------------------
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
|
@ -36,17 +37,19 @@ ok( $res = $client->_post(
|
|||
);
|
||||
count(1);
|
||||
|
||||
my ( $host, $url, $query )
|
||||
= expectForm( $res, undef, '/ext2fcheck', 'token', 'code' );
|
||||
my ( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/ext2fcheck', 'token', 'code' );
|
||||
|
||||
ok( $res->[2]->[0]
|
||||
=~ qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" class="form-control" id="extcode" trplaceholder="code">%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$query =~ s/code=/code=123456/;
|
||||
ok( $res = $client->_post(
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/ext2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
|
@ -58,12 +61,12 @@ count(1);
|
|||
|
||||
ok( $res->[2]->[0] =~ /<h3 trspan="Dwho_notAllowed">Dwho_notAllowed<\/h3>/,
|
||||
'dwho rejected with custom message' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
ok( $res->[2]->[0] =~ qr%src="/static/common/js/info.(?:min\.)?js"></script>%,
|
||||
'Found INFO js'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
'Found INFO js' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
clean_sessions();
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
my ($swt, $user) = @ARGV;
|
||||
my ( $swt, $user ) = @ARGV;
|
||||
|
||||
exit !( $swt eq '-uid' && $user eq 'dwho' );
|
||||
|
|
|
@ -58,6 +58,7 @@ use Data::Dumper;
|
|||
use LWP::UserAgent;
|
||||
use URI::Escape;
|
||||
use Lemonldap::NG::Common::FormEncode;
|
||||
|
||||
#use 5.10.0;
|
||||
|
||||
no warnings 'redefine';
|
||||
|
@ -625,7 +626,7 @@ sub _get {
|
|||
: ()
|
||||
),
|
||||
'REQUEST_METHOD' => $args{method} || 'GET',
|
||||
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
|
||||
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
|
||||
( $args{query} ? ( QUERY_STRING => $args{query} ) : () ),
|
||||
'SCRIPT_NAME' => '',
|
||||
'SERVER_NAME' => 'auth.example.com',
|
||||
|
@ -677,10 +678,10 @@ sub _post {
|
|||
: ()
|
||||
),
|
||||
'REQUEST_METHOD' => $args{method} || 'POST',
|
||||
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
|
||||
'SCRIPT_NAME' => '',
|
||||
'SERVER_NAME' => 'auth.example.com',
|
||||
'SERVER_PORT' => '80',
|
||||
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
|
||||
'SCRIPT_NAME' => '',
|
||||
'SERVER_NAME' => 'auth.example.com',
|
||||
'SERVER_PORT' => '80',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
( $args{custom} ? %{ $args{custom} } : () ),
|
||||
'psgix.input.buffered' => 0,
|
||||
|
|
|
@ -53,7 +53,7 @@ sub mirror {
|
|||
: ()
|
||||
),
|
||||
'REQUEST_METHOD' => $args{method} || 'GET',
|
||||
'REQUEST_URI' => ( $args{path} || '/' )
|
||||
'REQUEST_URI' => ( $args{path} || '/' )
|
||||
. ( $args{query} ? "?$args{query}" : '' ),
|
||||
( $args{query} ? ( QUERY_STRING => $args{query} ) : () ),
|
||||
'SCRIPT_NAME' => '',
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
my ($swt1, $user, $swt2, $code) = @ARGV;
|
||||
my ( $swt1, $user, $swt2, $code ) = @ARGV;
|
||||
|
||||
exit !( $swt1 eq '-uid' && $user eq 'dwho' && $swt2 eq '-code' && $code eq '123456' );
|
||||
exit !($swt1 eq '-uid'
|
||||
&& $user eq 'dwho'
|
||||
&& $swt2 eq '-code'
|
||||
&& $code eq '123456' );
|
||||
|
|
Loading…
Reference in New Issue