make tidy

This commit is contained in:
Clément OUDOT 2018-10-12 10:04:03 +02:00
parent f43139321c
commit c5f9a7f95b
39 changed files with 765 additions and 690 deletions

View File

@ -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,
{

View File

@ -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()',

View File

@ -21,7 +21,7 @@ ok(
cookieName => 'lemonldap',
securedCookie => 0,
https => 0,
userLogger => 'Lemonldap::NG::Common::Logger::Null',
userLogger => 'Lemonldap::NG::Common::Logger::Null',
}
),
'initialization'

View File

@ -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(

View File

@ -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'
},

View File

@ -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',

View File

@ -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},

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 ) )

View File

@ -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

View File

@ -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' );

View File

@ -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{$_},
);
}

View File

@ -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}

View File

@ -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};
}

View File

@ -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 );
}
}
: ()
)
]

View File

@ -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}";
}

View File

@ -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;

View File

@ -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} };

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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') );

View File

@ -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},
]
);

View File

@ -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;

View File

@ -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(
{

View File

@ -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();

View File

@ -16,7 +16,6 @@ my $mailSend = 0;
my $mail2 = 0;
SKIP: {
eval
'require Email::Sender::Simple;use GD::SecurityImage;use Image::Magick;';

View File

@ -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 );

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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' );

View File

@ -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);

View File

@ -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();

View File

@ -2,6 +2,6 @@
use strict;
use warnings;
my ($swt, $user) = @ARGV;
my ( $swt, $user ) = @ARGV;
exit !( $swt eq '-uid' && $user eq 'dwho' );

View File

@ -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,

View File

@ -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' => '',

View File

@ -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' );