This commit is contained in:
Yadd 2022-02-16 17:43:29 +01:00
parent c0472d41db
commit b88a72c267
245 changed files with 1173 additions and 1101 deletions

View File

@ -1176,15 +1176,14 @@ test-diff:
done done
tidy: clean tidy: clean
@if perltidy -v|grep v20181120 >/dev/null; then \ @if perltidy -v|grep v20210717 >/dev/null; then \
find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \) -print -exec perltidy -se -b {} \; ; \ for f in `find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \)`; do \
else echo "Wrong perltidy version, please install Perl::Tidy@20181120" ; exit 1 ;\ echo -n $$f; \
perltidy -se -b $$f; \
echo; \
done; \
else echo "Wrong perltidy version, please install Perl::Tidy@20210717" ; exit 1 ;\
fi fi
for f in `find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \)`; do \
echo -n $$f; \
perltidy -se -b $$f; \
echo; \
done
find lemon*/ -name '*.bak' -delete find lemon*/ -name '*.bak' -delete
$(MAKE) json $(MAKE) json

View File

@ -63,10 +63,11 @@ sub testEmail {
eval { eval {
Lemonldap::NG::Common::EmailTransport::sendTestMail( $conf, $dest ); Lemonldap::NG::Common::EmailTransport::sendTestMail( $conf, $dest );
}; };
my $error = $@; my $error = $@;
if ($error) { if ($error) {
die $error; die $error;
} else { }
else {
print STDERR "Test email successfully sent to $dest\n"; print STDERR "Test email successfully sent to $dest\n";
} }
} }

View File

@ -190,7 +190,7 @@ sub getConf {
eval { $r = $self->{refLocalStorage}->get('conf') } eval { $r = $self->{refLocalStorage}->get('conf') }
if ( $> and not $args->{noCache} ); if ( $> and not $args->{noCache} );
$msg .= "Warn: $@" if ($@); $msg .= "Warn: $@" if ($@);
if ( ref($r) if ( ref($r)
and $r->{cfgNum} and $r->{cfgNum}
and $args->{cfgNum} and $args->{cfgNum}

View File

@ -7,9 +7,9 @@ use Mouse;
use Lemonldap::NG::Common::Conf; use Lemonldap::NG::Common::Conf;
has '_confAcc' => ( is => 'rw', isa => 'Lemonldap::NG::Common::Conf' ); has '_confAcc' => ( is => 'rw', isa => 'Lemonldap::NG::Common::Conf' );
has 'configStorage' => ( is => 'rw', isa => 'HashRef', default => sub { {} } ); has 'configStorage' => ( is => 'rw', isa => 'HashRef', default => sub { {} } );
has 'currentConf' => ( is => 'rw', required => 1, default => sub { {} } ); has 'currentConf' => ( is => 'rw', required => 1, default => sub { {} } );
has 'protection' => ( is => 'rw', isa => 'Str', default => 'manager' ); has 'protection' => ( is => 'rw', isa => 'Str', default => 'manager' );
our $VERSION = '2.0.11'; our $VERSION = '2.0.11';

View File

@ -194,8 +194,8 @@ sub store {
$operation = $self->ldap->add( $operation = $self->ldap->add(
$confDN, $confDN,
attrs => [ attrs => [
objectClass => [ 'top', $self->{ldapObjectClass} ], objectClass => [ 'top', $self->{ldapObjectClass} ],
$self->{ldapAttributeId} => $confName, $self->{ldapAttributeId} => $confName,
$self->{ldapAttributeContent} => \@confValues, $self->{ldapAttributeContent} => \@confValues,
] ]
); );

View File

@ -35,7 +35,7 @@ sub load {
cfgNum => 1, cfgNum => 1,
cfgDate => time, cfgDate => time,
cfgAuthor => 'LLNG Team', cfgAuthor => 'LLNG Team',
cfgLog => cfgLog =>
q"Do not edit this configuration, Null backend uses lemonldap-ng.ini values only", q"Do not edit this configuration, Null backend uses lemonldap-ng.ini values only",
}; };
} }

View File

@ -19,7 +19,7 @@ sub store {
$req = $self->_dbh->prepare( $req = $self->_dbh->prepare(
"INSERT INTO $self->{dbiTable} (cfgNum,field,value) VALUES (?,?,?)"); "INSERT INTO $self->{dbiTable} (cfgNum,field,value) VALUES (?,?,?)");
_delete($self,$cfgNum) if $lastCfg == $cfgNum; _delete( $self, $cfgNum ) if $lastCfg == $cfgNum;
unless ($req) { unless ($req) {
$self->logError; $self->logError;
return UNKNOWN_ERROR; return UNKNOWN_ERROR;

View File

@ -36,7 +36,8 @@ sub available {
my $sth = my $sth =
$self->_dbh->prepare( "SELECT DISTINCT cfgNum from " $self->_dbh->prepare( "SELECT DISTINCT cfgNum from "
. $self->{dbiTable} . $self->{dbiTable}
. " order by cfgNum" ) or $self->logError; . " order by cfgNum" )
or $self->logError;
$sth->execute() or $self->logError; $sth->execute() or $self->logError;
my @conf; my @conf;
while ( my @row = $sth->fetchrow_array ) { while ( my @row = $sth->fetchrow_array ) {
@ -105,8 +106,8 @@ sub unlock {
sub delete { sub delete {
my ( $self, $cfgNum ) = @_; my ( $self, $cfgNum ) = @_;
my $req = my $req =
$self->_dbh->prepare("DELETE FROM $self->{dbiTable} WHERE cfgNum=?") $self->_dbh->prepare("DELETE FROM $self->{dbiTable} WHERE cfgNum=?")
or $self->logError; or $self->logError;
my $res = $req->execute($cfgNum) or $self->logError; my $res = $req->execute($cfgNum) or $self->logError;
$Lemonldap::NG::Common::Conf::msg .= $Lemonldap::NG::Common::Conf::msg .=
"Unable to find conf $cfgNum (" . $self->_dbh->errstr . ")" "Unable to find conf $cfgNum (" . $self->_dbh->errstr . ")"

View File

@ -17,7 +17,7 @@ sub defaultValues {
}, },
'authChoiceParam' => 'lmAuth', 'authChoiceParam' => 'lmAuth',
'authentication' => 'Demo', 'authentication' => 'Demo',
'available2F' => 'available2F' =>
'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,WebAuthn,Yubikey,Radius', 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,WebAuthn,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,WebAuthn,Yubikey', 'available2FSelfRegistration' => 'TOTP,U2F,WebAuthn,Yubikey',
'bruteForceProtectionLockTimes' => '15, 30, 60, 300, 600', 'bruteForceProtectionLockTimes' => '15, 30, 60, 300, 600',
@ -102,7 +102,7 @@ sub defaultValues {
'globalLogoutTimer' => 1, 'globalLogoutTimer' => 1,
'globalStorage' => 'Apache::Session::File', 'globalStorage' => 'Apache::Session::File',
'globalStorageOptions' => { 'globalStorageOptions' => {
'Directory' => '/var/lib/lemonldap-ng/sessions/', 'Directory' => '/var/lib/lemonldap-ng/sessions/',
'generateModule' => 'generateModule' =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/' 'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/'
@ -175,20 +175,20 @@ sub defaultValues {
'locationRules' => { 'locationRules' => {
'default' => 'deny' 'default' => 'deny'
}, },
'logoutServices' => {}, 'logoutServices' => {},
'macros' => {}, 'macros' => {},
'mail2fActivation' => 0, 'mail2fActivation' => 0,
'mail2fCodeRegex' => '\\d{6}', 'mail2fCodeRegex' => '\\d{6}',
'mailCharset' => 'utf-8', 'mailCharset' => 'utf-8',
'mailFrom' => 'noreply@example.com', 'mailFrom' => 'noreply@example.com',
'mailSessionKey' => 'mail', 'mailSessionKey' => 'mail',
'mailTimeout' => 0, 'mailTimeout' => 0,
'mailUrl' => 'http://auth.example.com/resetpwd', 'mailUrl' => 'http://auth.example.com/resetpwd',
'managerDn' => '', 'managerDn' => '',
'managerPassword' => '', 'managerPassword' => '',
'max2FDevices' => 10, 'max2FDevices' => 10,
'max2FDevicesNameLength' => 20, 'max2FDevicesNameLength' => 20,
'multiValuesSeparator' => '; ', 'multiValuesSeparator' => '; ',
'mySessionAuthorizedRWKeys' => 'mySessionAuthorizedRWKeys' =>
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ], [ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
'newLocationWarningLocationAttribute' => 'ipAddr', 'newLocationWarningLocationAttribute' => 'ipAddr',
@ -196,7 +196,7 @@ sub defaultValues {
'newLocationWarningMaxValues' => '0', 'newLocationWarningMaxValues' => '0',
'notificationDefaultCond' => '', 'notificationDefaultCond' => '',
'notificationServerPOST' => 1, 'notificationServerPOST' => 1,
'notificationServerSentAttributes' => 'notificationServerSentAttributes' =>
'uid reference date title subtitle text check', 'uid reference date title subtitle text check',
'notificationsMaxRetrieve' => 3, 'notificationsMaxRetrieve' => 3,
'notificationStorage' => 'File', 'notificationStorage' => 'File',
@ -250,7 +250,7 @@ sub defaultValues {
'passwordPolicyMinUpper' => 0, 'passwordPolicyMinUpper' => 0,
'passwordPolicySpecialChar' => '__ALL__', 'passwordPolicySpecialChar' => '__ALL__',
'passwordResetAllowedRetries' => 3, 'passwordResetAllowedRetries' => 3,
'persistentSessionAttributes' => 'persistentSessionAttributes' =>
'_loginHistory _2fDevices notification_', '_loginHistory _2fDevices notification_',
'port' => -1, 'port' => -1,
'portal' => 'http://auth.example.com/', 'portal' => 'http://auth.example.com/',
@ -261,7 +261,7 @@ sub defaultValues {
'portalDisplayGeneratePassword' => 1, 'portalDisplayGeneratePassword' => 1,
'portalDisplayLoginHistory' => 1, 'portalDisplayLoginHistory' => 1,
'portalDisplayLogout' => 1, 'portalDisplayLogout' => 1,
'portalDisplayOidcConsents' => 'portalDisplayOidcConsents' =>
'$_oidcConsents && $_oidcConsents =~ /\\w+/', '$_oidcConsents && $_oidcConsents =~ /\\w+/',
'portalDisplayRefreshMyRights' => 1, 'portalDisplayRefreshMyRights' => 1,
'portalDisplayRegister' => 1, 'portalDisplayRegister' => 1,
@ -289,11 +289,11 @@ sub defaultValues {
'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService', 'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService',
'proxy' => 'http://auth.example.com/sessions' 'proxy' => 'http://auth.example.com/sessions'
}, },
'requireToken' => 1, 'requireToken' => 1,
'rest2fActivation' => 0, 'rest2fActivation' => 0,
'restAuthnLevel' => 2, 'restAuthnLevel' => 2,
'restClockTolerance' => 15, 'restClockTolerance' => 15,
'sameSite' => '', 'sameSite' => '',
'samlAttributeAuthorityDescriptorAttributeServiceSOAP' => 'samlAttributeAuthorityDescriptorAttributeServiceSOAP' =>
'urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/AA/SOAP;', 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/AA/SOAP;',
'samlAuthnContextMapKerberos' => 4, 'samlAuthnContextMapKerberos' => 4,
@ -333,7 +333,7 @@ sub defaultValues {
'0;1;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;#PORTAL#/saml/proxySingleSignOnArtifact', '0;1;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;#PORTAL#/saml/proxySingleSignOnArtifact',
'samlSPSSODescriptorAssertionConsumerServiceHTTPPost' => 'samlSPSSODescriptorAssertionConsumerServiceHTTPPost' =>
'1;0;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;#PORTAL#/saml/proxySingleSignOnPost', '1;0;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;#PORTAL#/saml/proxySingleSignOnPost',
'samlSPSSODescriptorAuthnRequestsSigned' => 1, 'samlSPSSODescriptorAuthnRequestsSigned' => 1,
'samlSPSSODescriptorSingleLogoutServiceHTTPPost' => 'samlSPSSODescriptorSingleLogoutServiceHTTPPost' =>
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;#PORTAL#/saml/proxySingleLogout;#PORTAL#/saml/proxySingleLogoutReturn', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST;#PORTAL#/saml/proxySingleLogout;#PORTAL#/saml/proxySingleLogoutReturn',
'samlSPSSODescriptorSingleLogoutServiceHTTPRedirect' => 'samlSPSSODescriptorSingleLogoutServiceHTTPRedirect' =>
@ -345,7 +345,7 @@ sub defaultValues {
'sfEngine' => '::2F::Engines::Default', 'sfEngine' => '::2F::Engines::Default',
'sfManagerRule' => 1, 'sfManagerRule' => 1,
'sfRemovedMsgRule' => 0, 'sfRemovedMsgRule' => 0,
'sfRemovedNotifMsg' => 'sfRemovedNotifMsg' =>
'_removedSF_ expired second factor(s) has/have been removed (_nameSF_)!', '_removedSF_ expired second factor(s) has/have been removed (_nameSF_)!',
'sfRemovedNotifRef' => 'RemoveSF', 'sfRemovedNotifRef' => 'RemoveSF',
'sfRemovedNotifTitle' => 'Second factor notification', 'sfRemovedNotifTitle' => 'Second factor notification',

View File

@ -394,16 +394,17 @@ sub _oidcMetaDataNodes {
my ( $id, $resp ) = ( 1, [] ); my ( $id, $resp ) = ( 1, [] );
# Handle RP Attributes # Handle RP Attributes
if ($query eq "oidcRPMetaDataExportedVars") { if ( $query eq "oidcRPMetaDataExportedVars" ) {
my $pk = eval { $self->getConfKey( $req, $query )->{$partner} } // {}; my $pk = eval { $self->getConfKey( $req, $query )->{$partner} } // {};
return $self->sendError( $req, undef, 400 ) if ( $req->error ); return $self->sendError( $req, undef, 400 ) if ( $req->error );
foreach my $h ( sort keys %$pk ) { foreach my $h ( sort keys %$pk ) {
# Set default values for type and array # Set default values for type and array
my $data = [ split /;/, $pk->{$h} ]; my $data = [ split /;/, $pk->{$h} ];
unless ( $data->[1]) { unless ( $data->[1] ) {
$data->[1] = "string"; $data->[1] = "string";
} }
unless ( $data->[2]) { unless ( $data->[2] ) {
$data->[2] = "auto"; $data->[2] = "auto";
} }
push @$resp, push @$resp,
@ -416,6 +417,7 @@ sub _oidcMetaDataNodes {
} }
return $self->sendJSONresponse( $req, $resp ); return $self->sendJSONresponse( $req, $resp );
} }
# Return all exported attributes if asked # Return all exported attributes if asked
elsif ( $query =~ elsif ( $query =~
/^(?:oidc${type}MetaDataExportedVars|oidcRPMetaDataOptionsExtraClaims|oidcRPMetaDataMacros|oidcRPMetaDataScopeRules)$/ /^(?:oidc${type}MetaDataExportedVars|oidcRPMetaDataOptionsExtraClaims|oidcRPMetaDataMacros|oidcRPMetaDataScopeRules)$/
@ -733,9 +735,9 @@ sub combModules {
my $res = []; my $res = [];
foreach my $mod ( keys %$val ) { foreach my $mod ( keys %$val ) {
my $tmp; my $tmp;
$tmp->{title} = $mod; $tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod"; $tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule'; $tmp->{type} = 'cmbModule';
$tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for)); $tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for));
my $over = $val->{$mod}->{over} // {}; my $over = $val->{$mod}->{over} // {};
$tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ]; $tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ];
@ -809,8 +811,8 @@ sub metadata {
} }
# Find next and previous conf # Find next and previous conf
my @a = $self->confAcc->available; my @a = $self->confAcc->available;
my $id = -1; my $id = -1;
my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a; my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a;
if ($ind) { $res->{prev} = $a[ $ind - 1 ]; } if ($ind) { $res->{prev} = $a[ $ind - 1 ]; }
if ( defined $ind and $ind < $#a ) { if ( defined $ind and $ind < $#a ) {

View File

@ -26,7 +26,7 @@ sub serviceToXML {
my ( $self, $conf, $type ) = @_; my ( $self, $conf, $type ) = @_;
seek DATA, $dataStart, 0; seek DATA, $dataStart, 0;
my $s = join '', <DATA>; my $s = join '', <DATA>;
my $template = HTML::Template->new( my $template = HTML::Template->new(
scalarref => \$s, scalarref => \$s,
die_on_bad_params => 0, die_on_bad_params => 0,

View File

@ -95,7 +95,7 @@ sub configTest {
} }
sub sendTestMail { sub sendTestMail {
my ($conf, $dest) = @_; my ( $conf, $dest ) = @_;
my $transport = Lemonldap::NG::Common::EmailTransport->new($conf); my $transport = Lemonldap::NG::Common::EmailTransport->new($conf);
my $message = MIME::Entity->build( my $message = MIME::Entity->build(
From => $conf->{mailFrom}, From => $conf->{mailFrom},
@ -110,7 +110,7 @@ sub sendTestMail {
# Send the mail # Send the mail
eval { sendmail( $message->stringify, { transport => $transport } ); }; eval { sendmail( $message->stringify, { transport => $transport } ); };
if ($@) { if ($@) {
my $error = ( $@->isa('Throwable::Error') ? $@->message : $@ ); my $error = ( $@->isa('Throwable::Error') ? $@->message : $@ );
die $error; die $error;
} }
} }

View File

@ -4,7 +4,7 @@ use strict;
use base 'Exporter'; use base 'Exporter';
our $VERSION = '2.0.10'; our $VERSION = '2.0.10';
our @EXPORT = qw(&isIPv6 &net6 &expand6); our @EXPORT = qw(&isIPv6 &net6 &expand6);
sub isIPv6 { sub isIPv6 {
my ($ip) = @_; my ($ip) = @_;

View File

@ -58,12 +58,12 @@ sub new {
} }
sub setRequestObj { sub setRequestObj {
my ($self, $req) = @_; my ( $self, $req ) = @_;
Log::Log4perl::MDC->put( "req", $req ); Log::Log4perl::MDC->put( "req", $req );
} }
sub clearRequestObj { sub clearRequestObj {
my ($self, $req) = @_; my ( $self, $req ) = @_;
my $text = Log::Log4perl::MDC->remove(); my $text = Log::Log4perl::MDC->remove();
} }

View File

@ -13,9 +13,9 @@ use Sentry::Raven;
our $VERSION = '2.0.14'; our $VERSION = '2.0.14';
sub new { sub new {
my $self = bless {}, shift; my $self = bless {}, shift;
my ($conf) = @_; my ($conf) = @_;
my $show = 1; my $show = 1;
$self->{raven} = Sentry::Raven->new( sentry_dsn => $conf->{sentryDsn} ); $self->{raven} = Sentry::Raven->new( sentry_dsn => $conf->{sentryDsn} );
foreach (qw(error warn notice info debug)) { foreach (qw(error warn notice info debug)) {
my $rl = $_; my $rl = $_;

View File

@ -356,7 +356,8 @@ sub _logAndHandle {
if ( ref( $self->logger ) and $self->logger->can('setRequestObj') ) { if ( ref( $self->logger ) and $self->logger->can('setRequestObj') ) {
$self->logger->setRequestObj($req); $self->logger->setRequestObj($req);
} }
if ( ref( $self->userLogger ) and $self->userLogger->can('setRequestObj') ) { if ( ref( $self->userLogger ) and $self->userLogger->can('setRequestObj') )
{
$self->userLogger->setRequestObj($req); $self->userLogger->setRequestObj($req);
} }
@ -367,7 +368,9 @@ sub _logAndHandle {
if ( ref( $self->logger ) and $self->logger->can('clearRequestObj') ) { if ( ref( $self->logger ) and $self->logger->can('clearRequestObj') ) {
$self->logger->clearRequestObj($req); $self->logger->clearRequestObj($req);
} }
if ( ref( $self->userLogger ) and $self->userLogger->can('clearRequestObj') ) { if ( ref( $self->userLogger )
and $self->userLogger->can('clearRequestObj') )
{
$self->userLogger->clearRequestObj($req); $self->userLogger->clearRequestObj($req);
} }

View File

@ -27,7 +27,7 @@ sub _get {
'REQUEST_URI' => $path . ( $query ? "?$query" : '' ), 'REQUEST_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '8002', 'SERVER_PORT' => '8002',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => '127.0.0.1:8002' 'HTTP_HOST' => '127.0.0.1:8002'
@ -52,7 +52,7 @@ sub _post {
'REQUEST_URI' => $path . ( $query ? "?$query" : '' ), 'REQUEST_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '8002', 'SERVER_PORT' => '8002',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => '127.0.0.1:8002', 'HTTP_HOST' => '127.0.0.1:8002',
@ -81,7 +81,7 @@ sub _put {
'REQUEST_URI' => $path . ( $query ? "?$query" : '' ), 'REQUEST_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '8002', 'SERVER_PORT' => '8002',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => '127.0.0.1:8002', 'HTTP_HOST' => '127.0.0.1:8002',
@ -110,7 +110,7 @@ sub _patch {
'REQUEST_URI' => $path . ( $query ? "?$query" : '' ), 'REQUEST_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '8002', 'SERVER_PORT' => '8002',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => '127.0.0.1:8002', 'HTTP_HOST' => '127.0.0.1:8002',
@ -137,7 +137,7 @@ sub _del {
'REQUEST_URI' => $path . ( $query ? "?$query" : '' ), 'REQUEST_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '8002', 'SERVER_PORT' => '8002',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => '127.0.0.1:8002', 'HTTP_HOST' => '127.0.0.1:8002',

View File

@ -48,8 +48,7 @@ sub userData {
return $self->{userData} return $self->{userData}
|| { || {
( $Lemonldap::NG::Handler::Main::tsv->{whatToTrace} ( $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|| '_whatToTrace' ) => $self->{user}, || '_whatToTrace' ) => $self->{user}, };
};
} }
sub respHeaders { sub respHeaders {

View File

@ -11,8 +11,8 @@ extends 'Lemonldap::NG::Common::PSGI';
# Properties # Properties
has 'routes' => ( has 'routes' => (
is => 'rw', is => 'rw',
isa => 'HashRef', isa => 'HashRef',
default => default =>
sub { { GET => {}, POST => {}, PUT => {}, PATCH => {}, DELETE => {} } } sub { { GET => {}, POST => {}, PUT => {}, PATCH => {}, DELETE => {} } }
); );

View File

@ -121,20 +121,29 @@ sub date {
return $year . $mon . $mday . $hour . $min . $sec; return $year . $mon . $mday . $hour . $min . $sec;
} }
## @function integer dateToTime(string date) ## @function integer dateToTime(string date)
# Converts a LDAP date into epoch time or returns undef upon failure. # Converts a LDAP date into epoch time or returns undef upon failure.
# @param $date string Date in YYYYMMDDHHMMSS[+/-0000] format. It may contain a differential timezone, otherwise default TZ is GMT # @param $date string Date in YYYYMMDDHHMMSS[+/-0000] format. It may contain a differential timezone, otherwise default TZ is GMT
# @return Date converted to time # @return Date converted to time
sub dateToTime { sub dateToTime {
my $date = shift; my $date = shift;
return undef unless ( $date ); return undef unless ($date);
# Parse date # Parse date
my ( $year, $month, $day, $hour, $min, $sec, $zone ) = ( $date =~ /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})([-+\w]*)/ ); my ( $year, $month, $day, $hour, $min, $sec, $zone ) =
( $date =~ /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})([-+\w]*)/ );
# Convert date to epoch time with GMT as default timezone if date contains none # Convert date to epoch time with GMT as default timezone if date contains none
return str2time( $year . "-" . $month . "-" . $day . "T" . $hour . ":" . $min . ":" . $sec . $zone, "GMT" ); return str2time(
$year . "-"
. $month . "-"
. $day . "T"
. $hour . ":"
. $min . ":"
. $sec
. $zone,
"GMT"
);
} }
## @function boolean checkDate(string start, string end, boolean default_access) ## @function boolean checkDate(string start, string end, boolean default_access)
@ -158,10 +167,10 @@ sub checkDate {
# Convert dates to epoch time # Convert dates to epoch time
my $starttime = &dateToTime($start); my $starttime = &dateToTime($start);
my $endtime = &dateToTime($end); my $endtime = &dateToTime($end);
# Convert current GMT date to epoch time # Convert current GMT date to epoch time
my $datetime = &dateToTime(&date(1)); my $datetime = &dateToTime( &date(1) );
return 1 if ( ( $datetime >= $starttime ) and ( $datetime <= $endtime ) ); return 1 if ( ( $datetime >= $starttime ) and ( $datetime <= $endtime ) );
return 0; return 0;

View File

@ -126,8 +126,8 @@ sub BUILD {
if ( $self->{info} ) { if ( $self->{info} ) {
foreach ( keys %{ $self->{info} } ) { foreach ( keys %{ $self->{info} } ) {
next if ( $_ eq "_session_id" and $data->{_session_id} ); next if ( $_ eq "_session_id" and $data->{_session_id} );
next if ( $_ eq "_session_kind" and $data->{_session_kind}); next if ( $_ eq "_session_kind" and $data->{_session_kind} );
if ( defined $self->{info}->{$_} ) { if ( defined $self->{info}->{$_} ) {
$data->{$_} = $self->{info}->{$_}; $data->{$_} = $self->{info}->{$_};
} }

View File

@ -258,13 +258,13 @@ sub getApacheSession {
my $apacheSession = Lemonldap::NG::Common::Session->new( { my $apacheSession = Lemonldap::NG::Common::Session->new( {
storageModule => $mod->{module}, storageModule => $mod->{module},
storageModuleOptions => $mod->{options}, storageModuleOptions => $mod->{options},
cacheModule => cacheModule =>
Lemonldap::NG::Handler::PSGI::Main->tsv->{sessionCacheModule}, Lemonldap::NG::Handler::PSGI::Main->tsv->{sessionCacheModule},
cacheModuleOptions => cacheModuleOptions =>
Lemonldap::NG::Handler::PSGI::Main->tsv->{sessionCacheOptions}, Lemonldap::NG::Handler::PSGI::Main->tsv->{sessionCacheOptions},
id => $id, id => $id,
force => $force, force => $force,
( $id ? () : ( kind => $mod->{kind} ) ), ( $id ? () : ( kind => $mod->{kind} ) ),
( $info ? ( info => $info ) : () ), ( $info ? ( info => $info ) : () ),
} }
); );
@ -294,7 +294,7 @@ sub getMod {
} }
sub getGlobal { sub getGlobal {
my ( $self ) = @_; my ($self) = @_;
return $self->sessionTypes->{global}; return $self->sessionTypes->{global};
} }

View File

@ -55,8 +55,11 @@ ok(
# Test "and" # Test "and"
@tests = ( '[A and B, A]', '[A,B] and [B,C]', @tests = (
'if(0) then [A,B] else [A,B] and [B,C]' ); '[A and B, A]',
'[A,B] and [B,C]',
'if(0) then [A,B] else [A,B] and [B,C]'
);
while ( my $expr = shift @tests ) { while ( my $expr = shift @tests ) {
ok( [ getok($expr) ]->[0] == 0, qq{"$expr" returns PE_OK as auth result} ) ok( [ getok($expr) ]->[0] == 0, qq{"$expr" returns PE_OK as auth result} )

View File

@ -240,7 +240,7 @@ is( @{$res}, 2, "Found 2 psessions" );
# Test search with where # Test search with where
$res = getJson( "search", { where => "uid=dwho" } ); $res = getJson( "search", { where => "uid=dwho" } );
is( @{$res}, 2, "Found 2 sessions" ); is( @{$res}, 2, "Found 2 sessions" );
is( ( grep { $_->{uid} eq "dwho" } @{$res} ), 2, "Both sessions are dwho" ); is( ( grep { $_->{uid} eq "dwho" } @{$res} ), 2, "Both sessions are dwho" );
# Test search with where and field selection # Test search with where and field selection
@ -259,7 +259,7 @@ is(
); );
# Delete session # Delete session
$cli->run( 'delete', {}, "9684dd2a6489bf2be2fbdd799a8028e3" ); $cli->run( 'delete', {}, "9684dd2a6489bf2be2fbdd799a8028e3" );
$cli->run( 'delete', { persistent => 1 }, "rtyler" ); $cli->run( 'delete', { persistent => 1 }, "rtyler" );
$res = getJson( "get", {}, "9684dd2a6489bf2be2fbdd799a8028e3" ); $res = getJson( "get", {}, "9684dd2a6489bf2be2fbdd799a8028e3" );
@ -348,7 +348,7 @@ is( ( keys %{$res} ), 2, "Found two consents" );
$cli->run( "consents", {}, "delete", "dwho", "rp-example" ); $cli->run( "consents", {}, "delete", "dwho", "rp-example" );
$res = getJson( "consents", {}, "get", "dwho" ); $res = getJson( "consents", {}, "get", "dwho" );
is( ( keys %{$res} ), 1, "Found one consent" ); is( ( keys %{$res} ), 1, "Found one consent" );
is( $res->{'rp-example'}, undef, "Consent for test-rp removed" ); is( $res->{'rp-example'}, undef, "Consent for test-rp removed" );
ok( $res->{'rp-example2'}, "Consent for test-rp2 still present" ); ok( $res->{'rp-example2'}, "Consent for test-rp2 still present" );

View File

@ -72,18 +72,18 @@ SKIP: {
'name' => 'Imported automatically' 'name' => 'Imported automatically'
}, },
{ {
'name' => 'U2F-1', 'name' => 'U2F-1',
'type' => 'U2F', 'type' => 'U2F',
'epoch' => 1588691728, 'epoch' => 1588691728,
'_keyHandle' => '_keyHandle' =>
'4aS6vXlFQpG5XZSoad6auM9fFu7Q1wazQYwfPtPKN_Hll6Up_ceeWkOgqxm49swWq4Vvcg5UlX0sQQhuRe8heA', '4aS6vXlFQpG5XZSoad6auM9fFu7Q1wazQYwfPtPKN_Hll6Up_ceeWkOgqxm49swWq4Vvcg5UlX0sQQhuRe8heA',
'_userKey' => '_userKey' =>
'BMgMqKPL2PhsjCNW78UEQyNF8zlJtrAAPtWMUDBp9VfDRF5oL2xkwFuyXRMPtRZ7lNfGijDrMc06bDNfp478sQQ', 'BMgMqKPL2PhsjCNW78UEQyNF8zlJtrAAPtWMUDBp9VfDRF5oL2xkwFuyXRMPtRZ7lNfGijDrMc06bDNfp478sQQ',
}, },
{ {
'name' => 'U2F-2', 'name' => 'U2F-2',
'type' => 'U2F', 'type' => 'U2F',
'epoch' => 1588691730, 'epoch' => 1588691730,
'_keyHandle' => '_keyHandle' =>
'F1Kk9V_O7KDPIx-mqp6CIjbz7ljA-ihWVWyoP1xYBe_HPLHR74aTLanmn0b4vI8DumiBWO1DAle3k6N55cXreg', 'F1Kk9V_O7KDPIx-mqp6CIjbz7ljA-ihWVWyoP1xYBe_HPLHR74aTLanmn0b4vI8DumiBWO1DAle3k6N55cXreg',
'_userKey' => '_userKey' =>
@ -128,9 +128,9 @@ SKIP: {
'name' => 'Imported automatically' 'name' => 'Imported automatically'
}, },
{ {
'name' => 'U2F-3', 'name' => 'U2F-3',
'type' => 'U2F', 'type' => 'U2F',
'epoch' => 1588691734, 'epoch' => 1588691734,
'_keyHandle' => '_keyHandle' =>
'4suXv5Cf10vbJEP72mVkLpBjhSqy5niOgfc0X_MjdxZ_g2e-V8biC6WyCTpF_kGV1FCa06YlcryPCtWUuUST_g', '4suXv5Cf10vbJEP72mVkLpBjhSqy5niOgfc0X_MjdxZ_g2e-V8biC6WyCTpF_kGV1FCa06YlcryPCtWUuUST_g',
'_userKey' => '_userKey' =>

View File

@ -48,7 +48,9 @@ sub launch {
if ( ref( $class->logger ) and $class->logger->can('setRequestObj') ) { if ( ref( $class->logger ) and $class->logger->can('setRequestObj') ) {
$class->logger->setRequestObj($req); $class->logger->setRequestObj($req);
} }
if ( ref( $class->userLogger ) and $class->userLogger->can('setRequestObj') ) { if ( ref( $class->userLogger )
and $class->userLogger->can('setRequestObj') )
{
$class->userLogger->setRequestObj($req); $class->userLogger->setRequestObj($req);
} }
@ -58,7 +60,9 @@ sub launch {
if ( ref( $class->logger ) and $class->logger->can('clearRequestObj') ) { if ( ref( $class->logger ) and $class->logger->can('clearRequestObj') ) {
$class->logger->clearRequestObj($req); $class->logger->clearRequestObj($req);
} }
if ( ref( $class->userLogger ) and $class->userLogger->can('clearRequestObj') ) { if ( ref( $class->userLogger )
and $class->userLogger->can('clearRequestObj') )
{
$class->userLogger->clearRequestObj($req); $class->userLogger->clearRequestObj($req);
} }
return $res; return $res;

View File

@ -53,8 +53,7 @@ sub _loadVhostConfig {
my $resp = $class->ua->request($get); my $resp = $class->ua->request($get);
if ( $resp->is_success ) { if ( $resp->is_success ) {
$class->logger->debug('Response is success'); $class->logger->debug('Response is success');
eval { eval { $json = from_json( $resp->content, { allow_nonref => 1 } ); };
$json = from_json( $resp->content, { allow_nonref => 1 } ); };
if ($@) { if ($@) {
$class->logger->debug('Bad json file received'); $class->logger->debug('Bad json file received');
$class->logger->error( $class->logger->error(
@ -92,7 +91,7 @@ q"I refuse to compile 'rules.json' when useSafeJail isn't activated! Yes I know,
$class->logger->debug("DevOps handler called by $vhost"); $class->logger->debug("DevOps handler called by $vhost");
$class->locationRulesInit( undef, { $vhost => $json->{rules} } ); $class->locationRulesInit( undef, { $vhost => $json->{rules} } );
$class->headersInit( undef, { $vhost => $json->{headers} } ); $class->headersInit( undef, { $vhost => $json->{headers} } );
$class->tsv->{lastVhostUpdate}->{$vhost} = time; $class->tsv->{lastVhostUpdate}->{$vhost} = time;
$class->tsv->{https}->{$vhost} = uc $req->env->{HTTPS_REDIRECT} eq 'ON' $class->tsv->{https}->{$vhost} = uc $req->env->{HTTPS_REDIRECT} eq 'ON'
if exists $req->env->{HTTPS_REDIRECT}; if exists $req->env->{HTTPS_REDIRECT};

View File

@ -118,7 +118,8 @@ sub _logAuthTrace {
if ( ref( $self->logger ) and $self->logger->can('setRequestObj') ) { if ( ref( $self->logger ) and $self->logger->can('setRequestObj') ) {
$self->logger->setRequestObj($req); $self->logger->setRequestObj($req);
} }
if ( ref( $self->userLogger ) and $self->userLogger->can('setRequestObj') ) { if ( ref( $self->userLogger ) and $self->userLogger->can('setRequestObj') )
{
$self->userLogger->setRequestObj($req); $self->userLogger->setRequestObj($req);
} }
@ -129,7 +130,9 @@ sub _logAuthTrace {
if ( ref( $self->logger ) and $self->logger->can('clearRequestObj') ) { if ( ref( $self->logger ) and $self->logger->can('clearRequestObj') ) {
$self->logger->clearRequestObj($req); $self->logger->clearRequestObj($req);
} }
if ( ref( $self->userLogger ) and $self->userLogger->can('clearRequestObj') ) { if ( ref( $self->userLogger )
and $self->userLogger->can('clearRequestObj') )
{
$self->userLogger->clearRequestObj($req); $self->userLogger->clearRequestObj($req);
} }

View File

@ -7,7 +7,8 @@ our $VERSION = '2.0.9';
sub fetchId { sub fetchId {
my ( $class, $req ) = @_; my ( $class, $req ) = @_;
my $token = $req->{env}->{HTTP_X_LLNG_TOKEN}; my $token = $req->{env}->{HTTP_X_LLNG_TOKEN};
return $class->Lemonldap::NG::Handler::Main::fetchId($req) unless ($token =~ /\w+/); return $class->Lemonldap::NG::Handler::Main::fetchId($req)
unless ( $token =~ /\w+/ );
$class->logger->debug("Found token: $token"); $class->logger->debug("Found token: $token");
# Decrypt token # Decrypt token

View File

@ -29,10 +29,10 @@ sub run {
my $localConfig = $class->localConfig; my $localConfig = $class->localConfig;
my $zimbraPreAuthKey = $localConfig->{zimbraPreAuthKey}; my $zimbraPreAuthKey = $localConfig->{zimbraPreAuthKey};
my $zimbraAccountKey = $localConfig->{zimbraAccountKey} || 'uid'; my $zimbraAccountKey = $localConfig->{zimbraAccountKey} || 'uid';
my $zimbraBy = $localConfig->{zimbraBy} || 'id'; my $zimbraBy = $localConfig->{zimbraBy} || 'id';
my $zimbraUrl = $localConfig->{zimbraUrl} || '/service/preauth'; my $zimbraUrl = $localConfig->{zimbraUrl} || '/service/preauth';
my $zimbraSsoUrl = $localConfig->{zimbraSsoUrl} || '^/zimbrasso$'; my $zimbraSsoUrl = $localConfig->{zimbraSsoUrl} || '^/zimbrasso$';
my $timeout = $localConfig->{'timeout'} || '0'; my $timeout = $localConfig->{'timeout'} || '0';
# Remove trailing white-spaces # Remove trailing white-spaces
$zimbraAccountKey =~ s/\s+$//; $zimbraAccountKey =~ s/\s+$//;

View File

@ -63,7 +63,9 @@ sub build_jail {
if ($build) { if ($build) {
@builtCustomFunctions = @builtCustomFunctions =
$self->customFunctions ? split( /[,\s]+/, $self->customFunctions ) : (); $self->customFunctions
? split( /[,\s]+/, $self->customFunctions )
: ();
foreach (@builtCustomFunctions) { foreach (@builtCustomFunctions) {
no warnings 'redefine'; no warnings 'redefine';
$api->logger->debug("Custom function: $_"); $api->logger->debug("Custom function: $_");

View File

@ -65,7 +65,8 @@ sub checkConf {
or $class->cfgNum != $conf->{cfgNum} or $class->cfgNum != $conf->{cfgNum}
or $class->cfgDate != $conf->{cfgDate} ) or $class->cfgDate != $conf->{cfgDate} )
{ {
$class->logger->debug("Get configuration $conf->{cfgNum} aged $conf->{cfgDate}"); $class->logger->debug(
"Get configuration $conf->{cfgNum} aged $conf->{cfgDate}");
unless ( $class->cfgNum( $conf->{cfgNum} ) unless ( $class->cfgNum( $conf->{cfgNum} )
&& $class->cfgDate( $conf->{cfgDate} ) ) && $class->cfgDate( $conf->{cfgDate} ) )
{ {

View File

@ -504,7 +504,7 @@ sub fetchId {
if $class->_isHttps( $req, $vhost ); if $class->_isHttps( $req, $vhost );
my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/ my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/
and not $class->_isHttps( $req, $vhost ) ); and not $class->_isHttps( $req, $vhost ) );
my $cn = $class->tsv->{cookieName}; my $cn = $class->tsv->{cookieName};
my $value = $lookForHttpCookie # Avoid prefix and bad cookie name (#2417) my $value = $lookForHttpCookie # Avoid prefix and bad cookie name (#2417)
? ( $t =~ /(?<![-.~])\b${cn}http=([^,; ]+)/o ? $1 : 0 ) ? ( $t =~ /(?<![-.~])\b${cn}http=([^,; ]+)/o ? $1 : 0 )
: ( $t =~ /(?<![-.~])\b$cn=([^,; ]+)/o ? $1 : 0 ); : ( $t =~ /(?<![-.~])\b$cn=([^,; ]+)/o ? $1 : 0 );
@ -536,8 +536,8 @@ sub retrieveSession {
# (15 seconds) # (15 seconds)
if ( defined $class->data->{_session_id} if ( defined $class->data->{_session_id}
and $id eq $class->data->{_session_id} and $id eq $class->data->{_session_id}
and and ( $now - $class->dataUpdate < $class->tsv->{handlerInternalCache} )
( $now - $class->dataUpdate < $class->tsv->{handlerInternalCache} ) ) )
{ {
$class->logger->debug("Get session $id from Handler internal cache"); $class->logger->debug("Get session $id from Handler internal cache");
return $class->data; return $class->data;
@ -898,8 +898,9 @@ sub postJavascript {
my $filler; my $filler;
foreach my $name ( keys %$data ) { foreach my $name ( keys %$data ) {
use bytes; use bytes;
my @characterSet = ( '0' ..'9', 'A' .. 'Z', 'a' .. 'z' ); my @characterSet = ( '0' .. '9', 'A' .. 'Z', 'a' .. 'z' );
my $value = join '' => map $characterSet[ rand @characterSet ], 1 .. bytes::length( $data->{$name} ); my $value = join '' => map $characterSet[ rand @characterSet ],
1 .. bytes::length( $data->{$name} );
$filler .= $filler .=
"form.find('input[name=\"$name\"], select[name=\"$name\"], textarea[name=\"$name\"]').val('$value')\n"; "form.find('input[name=\"$name\"], select[name=\"$name\"], textarea[name=\"$name\"]').val('$value')\n";
} }

View File

@ -36,7 +36,7 @@ ok(
( defined($code) and ref($code) eq 'CODE' ), ( defined($code) and ref($code) eq 'CODE' ),
'encode_base64 function is defined' 'encode_base64 function is defined'
); );
ok( $res = &$code, "Function works" ); ok( $res = &$code, "Function works" );
ok( $res eq 'dGVzdA==', 'Get good result' ); ok( $res eq 'dGVzdA==', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }"; $sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }";
@ -58,7 +58,7 @@ ok(
'checkDate extended function is defined' 'checkDate extended function is defined'
); );
ok( $res = &$code, "Function works" ); ok( $res = &$code, "Function works" );
ok( $res == 1, 'Get good result' ); ok( $res == 1, 'Get good result' );
$sub = "sub { return(checkDate('20000101000000+0100','21000101000000+0100')) }"; $sub = "sub { return(checkDate('20000101000000+0100','21000101000000+0100')) }";
$code = $jail->jail_reval($sub); $code = $jail->jail_reval($sub);
@ -67,7 +67,7 @@ ok(
'checkDate extended function is defined' 'checkDate extended function is defined'
); );
ok( $res = &$code, "Function works" ); ok( $res = &$code, "Function works" );
ok( $res == 1, 'Get good result' ); ok( $res == 1, 'Get good result' );
$sub = "sub { return(has2f_internal(\$_[0],\$_[1])) }"; $sub = "sub { return(has2f_internal(\$_[0],\$_[1])) }";
$code = $jail->jail_reval($sub); $code = $jail->jail_reval($sub);

View File

@ -15,7 +15,7 @@ my $SKIPUSER = 0;
# -------------------- # --------------------
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, ' Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, ' Code is 302' ) or explain( $res->[0], 302 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='
@ -224,8 +224,13 @@ ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(2); count(2);
# Forged headers # Forged headers
ok( $res = $client->_get( '/skipif/zz', undef, 'test1.example.com', undef, HTTP_AUTH_USER => 'rtyler' ), ok(
'Test skip() with forged header' ); $res = $client->_get(
'/skipif/zz', undef, 'test1.example.com', undef,
HTTP_AUTH_USER => 'rtyler'
),
'Test skip() with forged header'
);
ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 ); ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(2); count(2);

View File

@ -13,7 +13,7 @@ my $res;
# -------------------- # --------------------
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='

View File

@ -16,7 +16,7 @@ my $res;
# Unauthentified query # Unauthentified query
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 ); ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='

View File

@ -39,7 +39,7 @@ my $res;
# Unauth tests # Unauth tests
ok( $res = $client->_get('/test'), 'Get response' ); ok( $res = $client->_get('/test'), 'Get response' );
ok( $res->[0] == 200, 'Response code is 200' ) ok( $res->[0] == 200, 'Response code is 200' )
or print "Expect 200, got $res->[0]\n"; or print "Expect 200, got $res->[0]\n";
ok( $res->[2]->[0] eq 'Unauth', 'Get unauth result' ) ok( $res->[2]->[0] eq 'Unauth', 'Get unauth result' )
or print "Expect Unauth, got $res->[2]->[0]\n"; or print "Expect Unauth, got $res->[2]->[0]\n";
@ -64,7 +64,7 @@ count(3);
# Bad path test # Bad path test
ok( $res = $client->_get('/[]/test'), 'Try a bad path' ); ok( $res = $client->_get('/[]/test'), 'Try a bad path' );
ok( $res->[0] == 400, 'Response is 400' ); ok( $res->[0] == 400, 'Response is 400' );
count(2); count(2);
clean(); clean();

View File

@ -82,12 +82,12 @@ no warnings 'redefine';
sub LWP::UserAgent::request { sub LWP::UserAgent::request {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
ok( $req->header('host') eq 'devops.example.com', ok( $req->header('host') eq 'devops.example.com', 'Host header found' )
'Host header found' )
or explain( $req->headers(), 'devops.example.com' ); or explain( $req->headers(), 'devops.example.com' );
ok( $req->as_string() =~ m#http://devops.example.com/myfile.json#, ok( $req->as_string() =~ m#http://devops.example.com/myfile.json#,
'Rules file URL found' ) 'Rules file URL found' )
or explain( $req->as_string(), 'GET http://devops.example.com/myfile.json' ); or
explain( $req->as_string(), 'GET http://devops.example.com/myfile.json' );
count(2); count(2);
my $httpResp; my $httpResp;
my $s = '{ my $s = '{

View File

@ -14,8 +14,8 @@ init(
#logLevel => 'debug', #logLevel => 'debug',
vhostOptions => { vhostOptions => {
'test3.example.com' => { 'test3.example.com' => {
vhostHttps => 0, vhostHttps => 0,
vhostPort => 80, vhostPort => 80,
vhostDevOpsRulesUrl => vhostDevOpsRulesUrl =>
'http://donotuse.example.com/myfile.json', 'http://donotuse.example.com/myfile.json',
}, },

View File

@ -73,8 +73,7 @@ no warnings 'redefine';
sub LWP::UserAgent::request { sub LWP::UserAgent::request {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
ok( $req->header('host') eq 'test3.example.com', ok( $req->header('host') eq 'test3.example.com', 'Host header found' )
'Host header found' )
or explain( $req->headers(), 'test3.example.com' ); or explain( $req->headers(), 'test3.example.com' );
ok( $req->as_string() =~ m#http://127.0.0.1:80/rules.json#, ok( $req->as_string() =~ m#http://127.0.0.1:80/rules.json#,
'Rules file URL found' ) 'Rules file URL found' )

View File

@ -15,7 +15,7 @@ my $res;
ok( $res = $client->_get( '/', undef, 'test.example.org' ), ok( $res = $client->_get( '/', undef, 'test.example.org' ),
'Unauthentified query' ); 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='

View File

@ -21,7 +21,7 @@ my $res;
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
my $conf; my $conf;
eval { eval {
@ -41,7 +41,7 @@ Lemonldap::NG::Handler::Main->configReload($conf);
fail $@ if $@; fail $@ if $@;
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='

View File

@ -27,7 +27,7 @@ my $res;
ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
my %h = @{ $res->[1] }; my %h = @{ $res->[1] };
ok( ok(
$h{Location} eq 'http://auth.example.com/?url=' $h{Location} eq 'http://auth.example.com/?url='

View File

@ -50,7 +50,7 @@ init(
Lemonldap::NG::Common::Session->new( { Lemonldap::NG::Common::Session->new( {
storageModule => 'Apache::Session::File', storageModule => 'Apache::Session::File',
storageModuleOptions => { Directory => 't/sessions' }, storageModuleOptions => { Directory => 't/sessions' },
id => id =>
'f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249', 'f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249',
force => 1, force => 1,
kind => 'OIDCI', kind => 'OIDCI',
@ -144,7 +144,7 @@ ok(
$res = $client->_get( $res = $client->_get(
'/read', undef, '/read', undef,
'test1.example.com', '', 'test1.example.com', '',
VHOSTTYPE => 'OAuth2', VHOSTTYPE => 'OAuth2',
HTTP_AUTHORIZATION => HTTP_AUTHORIZATION =>
'Bearer f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249', 'Bearer f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249',
), ),
@ -165,7 +165,7 @@ ok(
$res = $client->_get( $res = $client->_get(
'/write', undef, '/write', undef,
'test1.example.com', '', 'test1.example.com', '',
VHOSTTYPE => 'OAuth2', VHOSTTYPE => 'OAuth2',
HTTP_AUTHORIZATION => HTTP_AUTHORIZATION =>
'Bearer f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249', 'Bearer f0fd4e85000ce35d062f97f5b466fc00abc2fad0406e03e086605f929ec4a249',
), ),
@ -178,7 +178,7 @@ ok(
$res = $client->_get( $res = $client->_get(
'/test', undef, '/test', undef,
'test1.example.com', '', 'test1.example.com', '',
VHOSTTYPE => 'OAuth2', VHOSTTYPE => 'OAuth2',
HTTP_AUTHORIZATION => HTTP_AUTHORIZATION =>
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwianRpIjoiZjBmZDRlODUwMDBjZTM1ZDA2MmY5N2Y1YjQ2NmZjMDBhYmMyZmFkMDQwNmUwM2UwODY2MDVmOTI5ZWM0YTI0OSJ9.h0RDBLo5Vy8lqbltEP2L496KOzJLhLCIRZZmEqcPuN8', 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwianRpIjoiZjBmZDRlODUwMDBjZTM1ZDA2MmY5N2Y1YjQ2NmZjMDBhYmMyZmFkMDQwNmUwM2UwODY2MDVmOTI5ZWM0YTI0OSJ9.h0RDBLo5Vy8lqbltEP2L496KOzJLhLCIRZZmEqcPuN8',
), ),

View File

@ -75,7 +75,7 @@ sub init {
'_utime' => $now, '_utime' => $now,
'_passwordDB' => 'Demo', '_passwordDB' => 'Demo',
'_auth' => 'Demo', '_auth' => 'Demo',
'UA' => 'UA' =>
'Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1' 'Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1'
}; };
@ -152,7 +152,7 @@ sub _get {
'X_ORIGINAL_URI' => $path . ( $query ? "?$query" : '' ), 'X_ORIGINAL_URI' => $path . ( $query ? "?$query" : '' ),
'SERVER_PORT' => '80', 'SERVER_PORT' => '80',
'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_USER_AGENT' => 'HTTP_USER_AGENT' =>
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox', 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_ADDR' => '127.0.0.1',
'HTTP_HOST' => $host, 'HTTP_HOST' => $host,

View File

@ -24,7 +24,7 @@ extends qw(
Lemonldap::NG::Common::Conf::AccessLib Lemonldap::NG::Common::Conf::AccessLib
); );
has csp => ( is => 'rw' ); has csp => ( is => 'rw' );
has loadedPlugins => ( is => 'rw', default => sub { [] } ); has loadedPlugins => ( is => 'rw', default => sub { [] } );
has hLoadedPlugins => ( is => 'rw', default => sub { {} } ); has hLoadedPlugins => ( is => 'rw', default => sub { {} } );

View File

@ -47,7 +47,8 @@ sub init {
$self->{hiddenAttributes} //= "_password"; $self->{hiddenAttributes} //= "_password";
$self->{hiddenAttributes} .= ' _session_id' $self->{hiddenAttributes} .= ' _session_id'
unless $conf->{displaySessionId}; unless $conf->{displaySessionId};
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = $self->{WebAuthnCheck} = '1'; $self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} =
$self->{WebAuthnCheck} = '1';
return 1; return 1;
} }
@ -68,7 +69,7 @@ sub del2F {
my $epoch = $params->{epoch} my $epoch = $params->{epoch}
or return $self->sendError( $req, 'Missing "epoch" parameter', 400 ); or return $self->sendError( $req, 'Missing "epoch" parameter', 400 );
if ( grep { $_ eq $type } @{_2FTYPES()} ) { if ( grep { $_ eq $type } @{ _2FTYPES() } ) {
$self->logger->debug( $self->logger->debug(
"Call procedure delete2F with type=$type and epoch=$epoch"); "Call procedure delete2F with type=$type and epoch=$epoch");
return $self->delete2F( $req, $session, $skey ); return $self->delete2F( $req, $session, $skey );
@ -118,7 +119,7 @@ sub sfa {
$moduleOptions->{backend} = $mod->{module}; $moduleOptions->{backend} = $mod->{module};
# Select 2FA sessions to display # Select 2FA sessions to display
foreach (@{_2FTYPES()}) { foreach ( @{ _2FTYPES() } ) {
$self->{ $_ . 'Check' } = delete $params->{ $_ . 'Check' } $self->{ $_ . 'Check' } = delete $params->{ $_ . 'Check' }
if ( defined $params->{ $_ . 'Check' } ); if ( defined $params->{ $_ . 'Check' } );
} }
@ -189,7 +190,7 @@ sub sfa {
# Remove sessions without at least one 2F device(s) # Remove sessions without at least one 2F device(s)
$self->logger->debug( $self->logger->debug(
"Removing sessions without at least one 2F device(s)..."); "Removing sessions without at least one 2F device(s)...");
my $_2f_types_re = join ('|', @{_2FTYPES()}); my $_2f_types_re = join( '|', @{ _2FTYPES() } );
foreach my $session ( keys %$res ) { foreach my $session ( keys %$res ) {
delete $res->{$session} delete $res->{$session}
unless ( defined $res->{$session}->{_2fDevices} unless ( defined $res->{$session}->{_2fDevices}
@ -200,7 +201,7 @@ sub sfa {
# Filter 2FA sessions if needed # Filter 2FA sessions if needed
$self->logger->debug("Filtering 2F sessions..."); $self->logger->debug("Filtering 2F sessions...");
my $all = ( keys %$res ); my $all = ( keys %$res );
foreach (@{_2FTYPES()}) { foreach ( @{ _2FTYPES() } ) {
if ( $self->{ $_ . 'Check' } eq '2' ) { if ( $self->{ $_ . 'Check' } eq '2' ) {
foreach my $session ( keys %$res ) { foreach my $session ( keys %$res ) {
delete $res->{$session} delete $res->{$session}
@ -268,7 +269,7 @@ qq{Use of an uninitialized attribute "$group" to group sessions},
# { session => <sessionId>, userId => <_session_uid> } # { session => <sessionId>, userId => <_session_uid> }
else { else {
$res = [ $res = [
sort { $a->{date} <=> $b->{date} } sort { $a->{date} <=> $b->{date} }
map { { session => $_, userId => $res->{$_}->{_session_uid} } } map { { session => $_, userId => $res->{$_}->{_session_uid} } }
keys %$res keys %$res
]; ];

View File

@ -337,7 +337,7 @@ sub _checkType {
return { return {
res => "ko", res => "ko",
code => 400, code => 400,
msg => msg =>
"Invalid input: Type \"$type\" does not exist. Allowed values for type are: \"U2F\", \"TOTP\", \"WebAuthn\" or \"UBK\"" "Invalid input: Type \"$type\" does not exist. Allowed values for type are: \"U2F\", \"TOTP\", \"WebAuthn\" or \"UBK\""
} }
unless ( $type =~ /\b(?:U2F|TOTP|UBK|WebAuthn)\b/i ); unless ( $type =~ /\b(?:U2F|TOTP|UBK|WebAuthn)\b/i );

View File

@ -312,8 +312,8 @@ sub _isNewCasAppServiceUrlUnique {
# Check service paramater # Check service paramater
unless ( ref $casApp->{options}->{service} eq "ARRAY" ) { unless ( ref $casApp->{options}->{service} eq "ARRAY" ) {
return { return {
res => 'ko', res => 'ko',
msg => "The parameter 'service' must be an array", msg => "The parameter 'service' must be an array",
}; };
} }

View File

@ -140,7 +140,7 @@ sub addOidcRp {
409 409
) if ( defined $self->_getOidcRpByClientId( $conf, $add->{clientId} ) ); ) if ( defined $self->_getOidcRpByClientId( $conf, $add->{clientId} ) );
$add->{options} = {} unless ( defined $add->{options} ); $add->{options} = {} unless ( defined $add->{options} );
$add->{options}->{clientId} = $add->{clientId}; $add->{options}->{clientId} = $add->{clientId};
$add->{options}->{redirectUris} = $add->{redirectUris}; $add->{options}->{redirectUris} = $add->{redirectUris};
@ -246,8 +246,8 @@ sub replaceOidcRp {
return $self->sendError( $req, $res->{msg}, 409 ) return $self->sendError( $req, $res->{msg}, 409 )
unless ( $res->{res} eq 'ok' ); unless ( $res->{res} eq 'ok' );
$replace->{options} = {} unless ( defined $replace->{options} ); $replace->{options} = {} unless ( defined $replace->{options} );
$replace->{options}->{clientId} = $replace->{clientId}; $replace->{options}->{clientId} = $replace->{clientId};
$replace->{options}->{redirectUris} = $replace->{redirectUris}; $replace->{options}->{redirectUris} = $replace->{redirectUris};
$res = $self->_pushOidcRp( $conf, $confKey, $replace, 1 ); $res = $self->_pushOidcRp( $conf, $confKey, $replace, 1 );

View File

@ -67,7 +67,7 @@ sub types {
'hostname' => { 'hostname' => {
'form' => 'text', 'form' => 'text',
'msgFail' => '__badHostname__', 'msgFail' => '__badHostname__',
'test' => 'test' =>
qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))?$/ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))?$/
}, },
'int' => { 'int' => {
@ -257,7 +257,7 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\
'url' => { 'url' => {
'form' => 'text', 'form' => 'text',
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:^$|(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?))/ qr/(?:^$|(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?))/
} }
}; };
@ -802,7 +802,7 @@ sub attributes {
}, },
'casSrvMetaDataOptionsUrl' => { 'casSrvMetaDataOptionsUrl' => {
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text' 'type' => 'text'
}, },
@ -1341,7 +1341,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'domain' => { 'domain' => {
'default' => 'example.com', 'default' => 'example.com',
'msgFail' => '__badDomainName__', 'msgFail' => '__badDomainName__',
'test' => 'test' =>
qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/, qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/,
'type' => 'text' 'type' => 'text'
}, },
@ -1484,7 +1484,7 @@ qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-
}, },
'globalStorageOptions' => { 'globalStorageOptions' => {
'default' => { 'default' => {
'Directory' => '/var/lib/lemonldap-ng/sessions/', 'Directory' => '/var/lib/lemonldap-ng/sessions/',
'generateModule' => 'generateModule' =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/' 'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/'
@ -1609,7 +1609,7 @@ qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-
'issuerDBGetParameters' => { 'issuerDBGetParameters' => {
'default' => {}, 'default' => {},
'keyMsgFail' => '__badHostname__', 'keyMsgFail' => '__badHostname__',
'keyTest' => 'keyTest' =>
qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)$/, qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)$/,
'test' => { 'test' => {
'keyMsgFail' => '__badKeyName__', 'keyMsgFail' => '__badKeyName__',
@ -2808,7 +2808,7 @@ m[^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
'pdataDomain' => { 'pdataDomain' => {
'default' => '', 'default' => '',
'msgFail' => '__badDomainName__', 'msgFail' => '__badDomainName__',
'test' => 'test' =>
qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/, qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/,
'type' => 'text' 'type' => 'text'
}, },
@ -2829,7 +2829,7 @@ qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-
'portal' => { 'portal' => {
'default' => 'http://auth.example.com/', 'default' => 'http://auth.example.com/',
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'url' 'type' => 'url'
}, },
@ -3136,7 +3136,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'keyTest' => 'keyTest' =>
qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+))(?::\d+)?$/, qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+))(?::\d+)?$/,
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'keyTextContainer' 'type' => 'keyTextContainer'
}, },
@ -3288,19 +3288,19 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
}, },
'samlCommonDomainCookieDomain' => { 'samlCommonDomainCookieDomain' => {
'msgFail' => '__badDomainName__', 'msgFail' => '__badDomainName__',
'test' => 'test' =>
qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)$/, qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)$/,
'type' => 'text' 'type' => 'text'
}, },
'samlCommonDomainCookieReader' => { 'samlCommonDomainCookieReader' => {
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text' 'type' => 'text'
}, },
'samlCommonDomainCookieWriter' => { 'samlCommonDomainCookieWriter' => {
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text' 'type' => 'text'
}, },
@ -3317,7 +3317,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
}, },
'samlDiscoveryProtocolURL' => { 'samlDiscoveryProtocolURL' => {
'msgFail' => '__badUrl__', 'msgFail' => '__badUrl__',
'test' => 'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/, qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text' 'type' => 'text'
}, },
@ -4110,7 +4110,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
}, },
'SMTPServer' => { 'SMTPServer' => {
'default' => '', 'default' => '',
'test' => 'test' =>
qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+))(?::\d+)?)?$/, qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+))(?::\d+)?)?$/,
'type' => 'text' 'type' => 'text'
}, },

View File

@ -207,7 +207,8 @@ EOF
foreach (@simpleHashKeys) { foreach (@simpleHashKeys) {
$ra->add($_); $ra->add($_);
} }
print F "our \$simpleHashKeys = '" . $ra->as_string . "';\n" print F "our \$simpleHashKeys = '"
. $ra->as_string . "';\n"
. "our \$specialNodeKeys = '${ignoreKeys}s';\n"; . "our \$specialNodeKeys = '${ignoreKeys}s';\n";
foreach ( sort keys %cnodesRe ) { foreach ( sort keys %cnodesRe ) {
print F "our \$${_}Keys = '$cnodesRe{$_}';\n"; print F "our \$${_}Keys = '$cnodesRe{$_}';\n";
@ -467,7 +468,7 @@ sub buildPortalConstants() {
printf STDERR $format, $self->portalConstantsFile; printf STDERR $format, $self->portalConstantsFile;
open( F, '>', $self->portalConstantsFile ) or die($!); open( F, '>', $self->portalConstantsFile ) or die($!);
my $urire = $RE{URI}{HTTP}{ -scheme=>qr/https?/ }{-keep}; my $urire = $RE{URI}{HTTP}{ -scheme => qr/https?/ }{-keep};
$urire =~ s/([\$\@])/\\$1/g; $urire =~ s/([\$\@])/\\$1/g;
my $content = <<EOF; my $content = <<EOF;
# This file is generated by $module. Don't modify it by hand # This file is generated by $module. Don't modify it by hand
@ -596,7 +597,7 @@ sub scanTree {
# Subnode # Subnode
elsif ( ref($leaf) ) { elsif ( ref($leaf) ) {
$jleaf->{title} = $jleaf->{id} = $leaf->{title}; $jleaf->{title} = $jleaf->{id} = $leaf->{title};
$jleaf->{type} = $leaf->{form} if ( $leaf->{form} ); $jleaf->{type} = $leaf->{form} if ( $leaf->{form} );
if ( $leaf->{title} =~ /^((?:oidc|saml|cas)Service)MetaData$/ ) { if ( $leaf->{title} =~ /^((?:oidc|saml|cas)Service)MetaData$/ ) {
no strict 'refs'; no strict 'refs';
my @tmp = $self->scanLeaf( $leaf->{nodes} ); my @tmp = $self->scanLeaf( $leaf->{nodes} );
@ -677,6 +678,7 @@ sub scanTree {
my $type = $attr->{type}; my $type = $attr->{type};
$type =~ s/Container//; $type =~ s/Container//;
foreach my $k ( sort keys( %{ $attr->{default} } ) ) { foreach my $k ( sort keys( %{ $attr->{default} } ) ) {
# Special handling for oidcAttribute # Special handling for oidcAttribute
my $default = $attr->{default}->{$k}; my $default = $attr->{default}->{$k};
if ( $attr->{type} eq 'oidcAttributeContainer' ) { if ( $attr->{type} eq 'oidcAttributeContainer' ) {
@ -703,9 +705,9 @@ sub scanTree {
push @cnodesKeys, $leaf; push @cnodesKeys, $leaf;
} }
# issue 2439 # issue 2439
# FIXME: in future versions, oidcOPMetaDataJSON and samlIDPMetaDataXML shoud # FIXME: in future versions, oidcOPMetaDataJSON and samlIDPMetaDataXML shoud
# behave the same # behave the same
if ( $leaf =~ /^oidcOPMetaData(?:JSON|JWKS)$/ ) { if ( $leaf =~ /^oidcOPMetaData(?:JSON|JWKS)$/ ) {
push @simpleHashKeys, $leaf; push @simpleHashKeys, $leaf;
} }

View File

@ -239,7 +239,7 @@ sub attributes {
# Other # Other
checkTime => { checkTime => {
type => 'int', type => 'int',
documentation => documentation =>
'Timeout to check new configuration in local cache', 'Timeout to check new configuration in local cache',
default => 600, default => 600,
@ -248,7 +248,7 @@ sub attributes {
mySessionAuthorizedRWKeys => { mySessionAuthorizedRWKeys => {
type => 'array', type => 'array',
documentation => 'Alterable session keys by user itself', documentation => 'Alterable session keys by user itself',
default => default =>
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ], [ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
}, },
configStorage => { configStorage => {
@ -297,7 +297,7 @@ sub attributes {
flags => 'h', flags => 'h',
}, },
confirmFormMethod => { confirmFormMethod => {
type => "select", type => "select",
select => select =>
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ], [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
default => 'post', default => 'post',
@ -318,7 +318,7 @@ sub attributes {
flags => 'h', flags => 'h',
}, },
infoFormMethod => { infoFormMethod => {
type => "select", type => "select",
select => select =>
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ], [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
default => 'get', default => 'get',
@ -379,13 +379,13 @@ sub attributes {
documentation => 'Enable portal status', documentation => 'Enable portal status',
}, },
portalUserAttr => { portalUserAttr => {
type => 'text', type => 'text',
default => '_user', default => '_user',
documentation => documentation =>
'Session parameter to display connected user in portal', 'Session parameter to display connected user in portal',
}, },
redirectFormMethod => { redirectFormMethod => {
type => "select", type => "select",
select => select =>
[ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ], [ { k => 'get', v => 'GET' }, { k => 'post', v => 'POST' }, ],
default => 'get', default => 'get',
@ -446,8 +446,8 @@ sub attributes {
documentation => 'Disable fingerprint checkng', documentation => 'Disable fingerprint checkng',
}, },
stayConnectedTimeout => { stayConnectedTimeout => {
type => 'int', type => 'int',
default => 2592000, default => 2592000,
documentation => documentation =>
'StayConnected persistent connexion session timeout', 'StayConnected persistent connexion session timeout',
flags => 'm', flags => 'm',
@ -518,7 +518,7 @@ sub attributes {
flags => 'p', flags => 'p',
}, },
checkUserSearchAttributes => { checkUserSearchAttributes => {
type => 'text', type => 'text',
documentation => documentation =>
'Attributes used for retrieving sessions in user DataBase', 'Attributes used for retrieving sessions in user DataBase',
flags => 'p', flags => 'p',
@ -745,14 +745,14 @@ sub attributes {
flags => 'p', flags => 'p',
}, },
skipRenewConfirmation => { skipRenewConfirmation => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Avoid asking confirmation when an Issuer asks to renew auth', 'Avoid asking confirmation when an Issuer asks to renew auth',
}, },
skipUpgradeConfirmation => { skipUpgradeConfirmation => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Avoid asking confirmation during a session upgrade', 'Avoid asking confirmation during a session upgrade',
}, },
@ -761,7 +761,7 @@ sub attributes {
documentation => 'Refresh sessions plugin', documentation => 'Refresh sessions plugin',
}, },
forceGlobalStorageIssuerOTT => { forceGlobalStorageIssuerOTT => {
type => 'bool', type => 'bool',
documentation => documentation =>
'Force Issuer tokens to be stored into Global Storage', 'Force Issuer tokens to be stored into Global Storage',
}, },
@ -845,8 +845,8 @@ sub attributes {
documentation => 'Show error if session is expired', documentation => 'Show error if session is expired',
}, },
portalErrorOnMailNotFound => { portalErrorOnMailNotFound => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Show error if mail is not found in password reset process', 'Show error if mail is not found in password reset process',
}, },
@ -938,15 +938,15 @@ sub attributes {
documentation => 'Check XSS', documentation => 'Check XSS',
}, },
portalForceAuthn => { portalForceAuthn => {
default => 0, default => 0,
help => 'forcereauthn.html', help => 'forcereauthn.html',
type => 'bool', type => 'bool',
documentation => documentation =>
'Enable force to authenticate when displaying portal', 'Enable force to authenticate when displaying portal',
}, },
portalForceAuthnInterval => { portalForceAuthnInterval => {
default => 5, default => 5,
type => 'int', type => 'int',
documentation => documentation =>
'Maximum interval in seconds since last authentication to force reauthentication', 'Maximum interval in seconds since last authentication to force reauthentication',
}, },
@ -977,15 +977,15 @@ sub attributes {
documentation => 'Max lock time', documentation => 'Max lock time',
}, },
bruteForceProtectionIncrementalTempo => { bruteForceProtectionIncrementalTempo => {
default => 0, default => 0,
help => 'bruteforceprotection.html', help => 'bruteforceprotection.html',
type => 'bool', type => 'bool',
documentation => documentation =>
'Enable incremental lock time for brute force attack protection', 'Enable incremental lock time for brute force attack protection',
}, },
bruteForceProtectionLockTimes => { bruteForceProtectionLockTimes => {
type => 'text', type => 'text',
default => '15, 30, 60, 300, 600', default => '15, 30, 60, 300, 600',
documentation => documentation =>
'Incremental lock time values for brute force attack protection', 'Incremental lock time values for brute force attack protection',
}, },
@ -1021,38 +1021,38 @@ sub attributes {
documentation => 'Enable Cross-Origin Resource Sharing', documentation => 'Enable Cross-Origin Resource Sharing',
}, },
corsAllow_Credentials => { corsAllow_Credentials => {
type => 'text', type => 'text',
default => 'true', default => 'true',
documentation => documentation =>
'Allow credentials for Cross-Origin Resource Sharing', 'Allow credentials for Cross-Origin Resource Sharing',
}, },
corsAllow_Headers => { corsAllow_Headers => {
type => 'text', type => 'text',
default => '*', default => '*',
documentation => documentation =>
'Allowed headers for Cross-Origin Resource Sharing', 'Allowed headers for Cross-Origin Resource Sharing',
}, },
corsAllow_Methods => { corsAllow_Methods => {
type => 'text', type => 'text',
default => 'POST,GET', default => 'POST,GET',
documentation => documentation =>
'Allowed methods for Cross-Origin Resource Sharing', 'Allowed methods for Cross-Origin Resource Sharing',
}, },
corsAllow_Origin => { corsAllow_Origin => {
type => 'text', type => 'text',
default => '*', default => '*',
documentation => documentation =>
'Allowed origine for Cross-Origin Resource Sharing', 'Allowed origine for Cross-Origin Resource Sharing',
}, },
corsExpose_Headers => { corsExpose_Headers => {
type => 'text', type => 'text',
default => '*', default => '*',
documentation => documentation =>
'Exposed headers for Cross-Origin Resource Sharing', 'Exposed headers for Cross-Origin Resource Sharing',
}, },
corsMax_Age => { corsMax_Age => {
type => 'text', type => 'text',
default => '86400', # 24 hours default => '86400', # 24 hours
documentation => 'MAx-age for Cross-Origin Resource Sharing', documentation => 'MAx-age for Cross-Origin Resource Sharing',
}, },
cspDefault => { cspDefault => {
@ -1061,8 +1061,8 @@ sub attributes {
documentation => 'Default value for Content-Security-Policy', documentation => 'Default value for Content-Security-Policy',
}, },
cspFormAction => { cspFormAction => {
type => 'text', type => 'text',
default => "*", default => "*",
documentation => documentation =>
'Form action destination for Content-Security-Policy', 'Form action destination for Content-Security-Policy',
}, },
@ -1082,8 +1082,8 @@ sub attributes {
documentation => 'Style source for Content-Security-Policy', documentation => 'Style source for Content-Security-Policy',
}, },
cspConnect => { cspConnect => {
type => 'text', type => 'text',
default => "'self'", default => "'self'",
documentation => documentation =>
'Authorized Ajax destination for Content-Security-Policy', 'Authorized Ajax destination for Content-Security-Policy',
}, },
@ -1254,8 +1254,8 @@ sub attributes {
documentation => 'Display logout tab in portal', documentation => 'Display logout tab in portal',
}, },
portalDisplayCertificateResetByMail => { portalDisplayCertificateResetByMail => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Display certificate reset by mail button in portal', 'Display certificate reset by mail button in portal',
}, },
@ -1280,8 +1280,8 @@ sub attributes {
documentation => 'Display OIDC consent tab in portal', documentation => 'Display OIDC consent tab in portal',
}, },
portalDisplayGeneratePassword => { portalDisplayGeneratePassword => {
default => 1, default => 1,
type => 'bool', type => 'bool',
documentation => documentation =>
'Display password generate box in reset password form', 'Display password generate box in reset password form',
}, },
@ -1431,8 +1431,8 @@ sub attributes {
documentation => 'Notification server activation', documentation => 'Notification server activation',
}, },
notificationServerSentAttributes => { notificationServerSentAttributes => {
type => 'text', type => 'text',
default => 'uid reference date title subtitle text check', default => 'uid reference date title subtitle text check',
documentation => documentation =>
'Prameters to send with notification server GET method', 'Prameters to send with notification server GET method',
flags => 'p', flags => 'p',
@ -1510,8 +1510,8 @@ sub attributes {
globalStorageOptions => { globalStorageOptions => {
type => 'keyTextContainer', type => 'keyTextContainer',
default => { default => {
'Directory' => '/var/lib/lemonldap-ng/sessions/', 'Directory' => '/var/lib/lemonldap-ng/sessions/',
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/', 'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock/',
'generateModule' => 'generateModule' =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
}, },
@ -1637,8 +1637,8 @@ sub attributes {
documentation => 'Send a mail when password is changed', documentation => 'Send a mail when password is changed',
}, },
portalRequireOldPassword => { portalRequireOldPassword => {
default => 1, default => 1,
type => 'boolOrExpr', type => 'boolOrExpr',
documentation => documentation =>
'Rule to require old password to change the password', 'Rule to require old password to change the password',
}, },
@ -1853,7 +1853,7 @@ sub attributes {
documentation => 'Upgrade session activation', documentation => 'Upgrade session activation',
}, },
forceGlobalStorageUpgradeOTT => { forceGlobalStorageUpgradeOTT => {
type => 'bool', type => 'bool',
documentation => documentation =>
'Force Upgrade tokens be stored into Global Storage', 'Force Upgrade tokens be stored into Global Storage',
}, },
@ -1882,7 +1882,7 @@ sub attributes {
documentation => 'U2F self registration activation', documentation => 'U2F self registration activation',
}, },
u2fAuthnLevel => { u2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by password+U2F' 'Authentication level for users authentified by password+U2F'
}, },
@ -1916,7 +1916,7 @@ sub attributes {
documentation => 'TOTP self registration activation', documentation => 'TOTP self registration activation',
}, },
totp2fAuthnLevel => { totp2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by password+TOTP' 'Authentication level for users authentified by password+TOTP'
}, },
@ -1969,7 +1969,7 @@ sub attributes {
documentation => 'UTOTP activation (mixed U2F/TOTP module)', documentation => 'UTOTP activation (mixed U2F/TOTP module)',
}, },
utotp2fAuthnLevel => { utotp2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by password+(U2F or TOTP)' 'Authentication level for users authentified by password+(U2F or TOTP)'
}, },
@ -2006,7 +2006,7 @@ sub attributes {
documentation => 'Second factor code timeout', documentation => 'Second factor code timeout',
}, },
mail2fAuthnLevel => { mail2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authenticated by Mail second factor' 'Authentication level for users authenticated by Mail second factor'
}, },
@ -2043,7 +2043,7 @@ sub attributes {
documentation => 'Validation command of External second factor', documentation => 'Validation command of External second factor',
}, },
ext2fAuthnLevel => { ext2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by External second factor' 'Authentication level for users authentified by External second factor'
}, },
@ -2074,7 +2074,7 @@ sub attributes {
documentation => 'Radius 2f verification timeout', documentation => 'Radius 2f verification timeout',
}, },
radius2fAuthnLevel => { radius2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authenticated by Radius second factor' 'Authentication level for users authenticated by Radius second factor'
}, },
@ -2118,7 +2118,7 @@ sub attributes {
documentation => 'Args for REST 2F init', documentation => 'Args for REST 2F init',
}, },
rest2fAuthnLevel => { rest2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by REST second factor' 'Authentication level for users authentified by REST second factor'
}, },
@ -2143,7 +2143,7 @@ sub attributes {
documentation => 'Yubikey self registration activation', documentation => 'Yubikey self registration activation',
}, },
yubikey2fAuthnLevel => { yubikey2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by Yubikey second factor' 'Authentication level for users authentified by Yubikey second factor'
}, },
@ -2182,7 +2182,7 @@ sub attributes {
documentation => 'Authorize users to remove existing Yubikey', documentation => 'Authorize users to remove existing Yubikey',
}, },
yubikey2fFromSessionAttribute => { yubikey2fFromSessionAttribute => {
type => 'text', type => 'text',
documentation => documentation =>
'Provision yubikey from the given session variable', 'Provision yubikey from the given session variable',
}, },
@ -2203,9 +2203,9 @@ sub attributes {
documentation => 'WebAuthn self registration activation', documentation => 'WebAuthn self registration activation',
}, },
webauthn2fAuthnLevel => { webauthn2fAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level for users authentified by WebAuthn second factor' 'Authentication level for users authentified by WebAuthn second factor'
}, },
webauthn2fLabel => { webauthn2fLabel => {
type => 'text', type => 'text',
@ -2218,11 +2218,11 @@ sub attributes {
webauthn2fUserVerification => { webauthn2fUserVerification => {
type => 'select', type => 'select',
select => [ select => [
{ k => 'discouraged', v => 'Discouraged' }, { k => 'discouraged', v => 'Discouraged' },
{ k => 'preferred', v => 'Preferred' }, { k => 'preferred', v => 'Preferred' },
{ k => 'required', v => 'Required' }, { k => 'required', v => 'Required' },
], ],
default => 'preferred', default => 'preferred',
documentation => 'Verify user during registration and login', documentation => 'Verify user during registration and login',
}, },
webauthn2fUserCanRemoveKey => { webauthn2fUserCanRemoveKey => {
@ -2239,7 +2239,6 @@ sub attributes {
documentation => 'WebAuthn Relying Party display name', documentation => 'WebAuthn Relying Party display name',
}, },
# Single session # Single session
notifyDeleted => { notifyDeleted => {
default => 1, default => 1,
@ -2284,14 +2283,14 @@ sub attributes {
documentation => 'Enable REST password reset server', documentation => 'Enable REST password reset server',
}, },
restExportSecretKeys => { restExportSecretKeys => {
default => 0, default => 0,
type => 'bool', type => 'bool',
documentation => documentation =>
'Allow to export secret keys in REST session server', 'Allow to export secret keys in REST session server',
}, },
restClockTolerance => { restClockTolerance => {
default => 15, default => 15,
type => 'int', type => 'int',
documentation => documentation =>
'How tolerant the REST session server will be to clock dift', 'How tolerant the REST session server will be to clock dift',
}, },
@ -2320,7 +2319,7 @@ sub attributes {
documentation => 'Enable SOAP config server', documentation => 'Enable SOAP config server',
}, },
exportedAttr => { exportedAttr => {
type => 'text', type => 'text',
documentation => documentation =>
'List of attributes to export by SOAP or REST servers', 'List of attributes to export by SOAP or REST servers',
}, },
@ -2583,7 +2582,7 @@ sub attributes {
documentation => 'CAS User attribute', documentation => 'CAS User attribute',
}, },
casAppMetaDataOptionsAuthnLevel => { casAppMetaDataOptionsAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level requires to access to this CAS application', 'Authentication level requires to access to this CAS application',
}, },
@ -2745,8 +2744,8 @@ sub attributes {
default => 'RSA_SHA256', default => 'RSA_SHA256',
}, },
samlServiceUseCertificateInResponse => { samlServiceUseCertificateInResponse => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Use certificate instead of public key in SAML responses', 'Use certificate instead of public key in SAML responses',
}, },
@ -2769,8 +2768,8 @@ sub attributes {
documentation => 'SAML authn context password level', documentation => 'SAML authn context password level',
}, },
samlAuthnContextMapPasswordProtectedTransport => { samlAuthnContextMapPasswordProtectedTransport => {
type => 'int', type => 'int',
default => 3, default => 3,
documentation => documentation =>
'SAML authn context password protected transport level', 'SAML authn context password protected transport level',
}, },
@ -3131,7 +3130,7 @@ sub attributes {
documentation => 'SAML SP SLO SOAP', documentation => 'SAML SP SLO SOAP',
}, },
samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact => { samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact => {
type => 'samlAssertion', type => 'samlAssertion',
default => default =>
'0;1;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;' '0;1;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact;'
. '#PORTAL#/saml/proxySingleSignOnArtifact', . '#PORTAL#/saml/proxySingleSignOnArtifact',
@ -3223,7 +3222,7 @@ sub attributes {
default => 1, default => 1,
}, },
samlSPMetaDataOptionsAuthnLevel => { samlSPMetaDataOptionsAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level requires to access to this SP', 'Authentication level requires to access to this SP',
}, },
@ -3335,9 +3334,9 @@ sub attributes {
documentation => 'Rule to display second factor Manager link', documentation => 'Rule to display second factor Manager link',
}, },
sfRemovedMsgRule => { sfRemovedMsgRule => {
type => 'boolOrExpr', type => 'boolOrExpr',
default => 0, default => 0,
help => 'secondfactor.html', help => 'secondfactor.html',
documentation => documentation =>
'Display a message if at leat one expired SF has been removed', 'Display a message if at leat one expired SF has been removed',
}, },
@ -3359,7 +3358,7 @@ sub attributes {
documentation => 'Notification title', documentation => 'Notification title',
}, },
sfRemovedNotifMsg => { sfRemovedNotifMsg => {
type => 'text', type => 'text',
default => default =>
'_removedSF_ expired second factor(s) has/have been removed (_nameSF_)!', '_removedSF_ expired second factor(s) has/have been removed (_nameSF_)!',
help => 'secondfactor.html', help => 'secondfactor.html',
@ -3370,13 +3369,14 @@ sub attributes {
documentation => 'Timeout for 2F registration process', documentation => 'Timeout for 2F registration process',
}, },
available2F => { available2F => {
type => 'text', type => 'text',
default => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,WebAuthn,Yubikey,Radius', default =>
'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,WebAuthn,Yubikey,Radius',
documentation => 'Available second factor modules', documentation => 'Available second factor modules',
}, },
available2FSelfRegistration => { available2FSelfRegistration => {
type => 'text', type => 'text',
default => 'TOTP,U2F,WebAuthn,Yubikey', default => 'TOTP,U2F,WebAuthn,Yubikey',
documentation => documentation =>
'Available self-registration modules for second factor', 'Available self-registration modules for second factor',
}, },
@ -3540,8 +3540,8 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'LDAP attribute name for member in groups', documentation => 'LDAP attribute name for member in groups',
}, },
ldapGroupAttributeNameUser => { ldapGroupAttributeNameUser => {
type => 'text', type => 'text',
default => 'dn', default => 'dn',
documentation => documentation =>
'LDAP attribute name in user entry referenced as member in groups', 'LDAP attribute name in user entry referenced as member in groups',
}, },
@ -3551,8 +3551,8 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'LDAP attributes to search in groups', documentation => 'LDAP attributes to search in groups',
}, },
ldapGroupAttributeNameGroup => { ldapGroupAttributeNameGroup => {
type => 'text', type => 'text',
default => 'dn', default => 'dn',
documentation => documentation =>
'LDAP attribute name in group entry referenced as member in groups', 'LDAP attribute name in group entry referenced as member in groups',
}, },
@ -3594,12 +3594,12 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 'require', default => 'require',
}, },
ldapCAFile => { ldapCAFile => {
type => 'text', type => 'text',
documentation => documentation =>
'Location of the certificate file for LDAP connections', 'Location of the certificate file for LDAP connections',
}, },
ldapCAPath => { ldapCAPath => {
type => 'text', type => 'text',
documentation => documentation =>
'Location of the CA directory for LDAP connections', 'Location of the CA directory for LDAP connections',
}, },
@ -3736,7 +3736,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
type => 'keyTextContainer', type => 'keyTextContainer',
default => { default => {
proxy => 'http://auth.example.com/sessions', proxy => 'http://auth.example.com/sessions',
ns => ns =>
'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService', 'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService',
}, },
documentation => 'Apache::Session module parameters', documentation => 'Apache::Session module parameters',
@ -3834,7 +3834,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 'id,first-name,last-name,email-address' default => 'id,first-name,last-name,email-address'
}, },
linkedInUserField => { type => 'text', default => 'emailAddress' }, linkedInUserField => { type => 'text', default => 'emailAddress' },
linkedInScope => linkedInScope =>
{ type => 'text', default => 'r_liteprofile r_emailaddress' }, { type => 'text', default => 'r_liteprofile r_emailaddress' },
# GitHub # GitHub
@ -3881,10 +3881,10 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
dbiUserTable => { type => 'text', }, dbiUserTable => { type => 'text', },
# TODO: add dbiMailCol # TODO: add dbiMailCol
dbiAuthLoginCol => { type => 'text', }, dbiAuthLoginCol => { type => 'text', },
dbiAuthPasswordCol => { type => 'text', }, dbiAuthPasswordCol => { type => 'text', },
dbiPasswordMailCol => { type => 'text', }, dbiPasswordMailCol => { type => 'text', },
userPivot => { type => 'text', }, userPivot => { type => 'text', },
dbiAuthPasswordHash => dbiAuthPasswordHash =>
{ type => 'text', help => 'authdbi.html#password', }, { type => 'text', help => 'authdbi.html#password', },
dbiDynamicHashEnabled => dbiDynamicHashEnabled =>
@ -4269,13 +4269,13 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'OpenID Connect global access token TTL', documentation => 'OpenID Connect global access token TTL',
}, },
oidcServiceDynamicRegistrationExportedVars => { oidcServiceDynamicRegistrationExportedVars => {
type => 'keyTextContainer', type => 'keyTextContainer',
documentation => documentation =>
'OpenID Connect exported variables for dynamic registration', 'OpenID Connect exported variables for dynamic registration',
}, },
oidcServiceDynamicRegistrationExtraClaims => { oidcServiceDynamicRegistrationExtraClaims => {
type => 'keyTextContainer', type => 'keyTextContainer',
keyTest => qr/^[\x21\x23-\x5B\x5D-\x7E]+$/, keyTest => qr/^[\x21\x23-\x5B\x5D-\x7E]+$/,
documentation => documentation =>
'OpenID Connect extra claims for dynamic registration', 'OpenID Connect extra claims for dynamic registration',
}, },
@ -4334,7 +4334,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
oidcOPMetaDataOptionsJWKSTimeout => { type => 'int', default => 0 }, oidcOPMetaDataOptionsJWKSTimeout => { type => 'int', default => 0 },
oidcOPMetaDataOptionsClientID => { type => 'text', }, oidcOPMetaDataOptionsClientID => { type => 'text', },
oidcOPMetaDataOptionsClientSecret => { type => 'password', }, oidcOPMetaDataOptionsClientSecret => { type => 'password', },
oidcOPMetaDataOptionsScope => oidcOPMetaDataOptionsScope =>
{ type => 'text', default => 'openid profile' }, { type => 'text', default => 'openid profile' },
oidcOPMetaDataOptionsDisplay => { oidcOPMetaDataOptionsDisplay => {
type => 'select', type => 'select',
@ -4363,10 +4363,10 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
{ type => 'bool', default => 1 }, { type => 'bool', default => 1 },
oidcOPMetaDataOptionsIDTokenMaxAge => { type => 'int', default => 30 }, oidcOPMetaDataOptionsIDTokenMaxAge => { type => 'int', default => 30 },
oidcOPMetaDataOptionsUseNonce => { type => 'bool', default => 1 }, oidcOPMetaDataOptionsUseNonce => { type => 'bool', default => 1 },
oidcOPMetaDataOptionsDisplayName => { type => 'text', }, oidcOPMetaDataOptionsDisplayName => { type => 'text', },
oidcOPMetaDataOptionsIcon => { type => 'text', }, oidcOPMetaDataOptionsIcon => { type => 'text', },
oidcOPMetaDataOptionsStoreIDToken => { type => 'bool', default => 0 }, oidcOPMetaDataOptionsStoreIDToken => { type => 'bool', default => 0 },
oidcOPMetaDataOptionsSortNumber => { type => 'int', }, oidcOPMetaDataOptionsSortNumber => { type => 'int', },
# OpenID Connect relying parties # OpenID Connect relying parties
oidcRPMetaDataExportedVars => { oidcRPMetaDataExportedVars => {
@ -4398,7 +4398,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
], ],
default => 'HS512', default => 'HS512',
}, },
oidcRPMetaDataOptionsIDTokenExpiration => { type => 'int' }, oidcRPMetaDataOptionsIDTokenExpiration => { type => 'int' },
oidcRPMetaDataOptionsIDTokenForceClaims => oidcRPMetaDataOptionsIDTokenForceClaims =>
{ type => 'bool', default => 0 }, { type => 'bool', default => 0 },
oidcRPMetaDataOptionsAccessTokenSignAlg => { oidcRPMetaDataOptionsAccessTokenSignAlg => {
@ -4480,8 +4480,8 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'Allow offline access', documentation => 'Allow offline access',
}, },
oidcRPMetaDataOptionsAllowPasswordGrant => { oidcRPMetaDataOptionsAllowPasswordGrant => {
type => 'bool', type => 'bool',
default => 0, default => 0,
documentation => documentation =>
'Allow OAuth2 Resource Owner Password Credentials Grant', 'Allow OAuth2 Resource Owner Password Credentials Grant',
}, },
@ -4496,7 +4496,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'Issue refresh tokens', documentation => 'Issue refresh tokens',
}, },
oidcRPMetaDataOptionsAuthnLevel => { oidcRPMetaDataOptionsAuthnLevel => {
type => 'int', type => 'int',
documentation => documentation =>
'Authentication level requires to access to this RP', 'Authentication level requires to access to this RP',
}, },

View File

@ -72,7 +72,7 @@ sub tree {
}, },
{ {
title => 'passwordManagement', title => 'passwordManagement',
help => help =>
'portalcustom.html#password-management', 'portalcustom.html#password-management',
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes => [
@ -99,7 +99,7 @@ sub tree {
}, },
{ {
title => 'portalOther', title => 'portalOther',
help => help =>
'portalcustom.html#other-parameters', 'portalcustom.html#other-parameters',
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes => [
@ -129,7 +129,7 @@ sub tree {
}, },
{ {
title => 'authParams', title => 'authParams',
help => help =>
'start.html#authentication-users-and-password-databases', 'start.html#authentication-users-and-password-databases',
form => 'authParams', form => 'authParams',
nodes => [ nodes => [
@ -650,7 +650,7 @@ sub tree {
}, },
{ {
title => 'soapServices', title => 'soapServices',
help => help =>
'portalservers.html#SOAP_(deprecated)', 'portalservers.html#SOAP_(deprecated)',
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes => [
@ -684,7 +684,7 @@ sub tree {
'notificationStorageOptions', 'notificationStorageOptions',
{ {
title => 'serverNotification', title => 'serverNotification',
help => help =>
'notifications.html#notification-server', 'notifications.html#notification-server',
nodes => [ nodes => [
'notificationServer', 'notificationServer',

View File

@ -24,8 +24,8 @@ has cfgNum => (
has log => ( is => 'rw' ); has log => ( is => 'rw' );
has req => ( is => 'ro' ); has req => ( is => 'ro' );
has sep => ( is => 'rw', isa => 'Str', default => '/' ); has sep => ( is => 'rw', isa => 'Str', default => '/' );
has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" ); has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" );
has yes => ( is => 'rw', isa => 'Bool', default => 0 ); has yes => ( is => 'rw', isa => 'Bool', default => 0 );
has safe => ( is => 'rw', isa => 'Bool', default => 0 ); has safe => ( is => 'rw', isa => 'Bool', default => 0 );
has force => ( is => 'rw', isa => 'Bool', default => 0 ); has force => ( is => 'rw', isa => 'Bool', default => 0 );
@ -317,8 +317,8 @@ sub lastCfg {
sub save { sub save {
my ($self) = @_; my ($self) = @_;
my $conf = $self->jsonResponse( '/confs/' . $self->cfgNum, 'full=1' ); my $conf = $self->jsonResponse( '/confs/' . $self->cfgNum, 'full=1' );
my $json = JSON->new->indent->canonical; my $json = JSON->new->indent->canonical;
print $json->encode($conf); print $json->encode($conf);
} }
@ -404,9 +404,9 @@ sub _getKey {
sub _setKey { sub _setKey {
my ( $self, $conf, $key, $value ) = @_; my ( $self, $conf, $key, $value ) = @_;
my $sep = $self->sep; my $sep = $self->sep;
my (@path) = split $sep, $key; my (@path) = split $sep, $key;
my $last = pop @path; my $last = pop @path;
while ( my $next = shift @path ) { while ( my $next = shift @path ) {
$conf = $conf->{$next}; $conf = $conf->{$next};
} }

View File

@ -20,9 +20,10 @@ sub diff {
$res[$i]->{$key} = $tmp[$i] if ( $tmp[$i] ); $res[$i]->{$key} = $tmp[$i] if ( $tmp[$i] );
} }
} }
elsif ( $key =~ $hashParameters elsif (
or $key =~ $hashParameters
( ref( $conf[0]->{$key} ) and ref( $conf[0]->{$key} ) eq 'HASH' ) ) or ( ref( $conf[0]->{$key} ) and ref( $conf[0]->{$key} ) eq 'HASH' )
)
{ {
if ( ref $conf[1]->{$key} ) { if ( ref $conf[1]->{$key} ) {
my @tmp = my @tmp =

View File

@ -438,8 +438,8 @@ sub _scanNodes {
$self->_scanNodes($subNodes); $self->_scanNodes($subNodes);
} }
} }
elsif ( elsif ( $target =~
$target =~ /^oidc(?:O|R)PMetaData(?:ExportedVars|Macros|ScopeRules)$/ ) /^oidc(?:O|R)PMetaData(?:ExportedVars|Macros|ScopeRules)$/ )
{ {
hdebug(" $target"); hdebug(" $target");
if ( $leaf->{cnodes} ) { if ( $leaf->{cnodes} ) {

View File

@ -106,7 +106,7 @@ sub zeroConf {
}, },
'cfgNum' => 0, 'cfgNum' => 0,
'globalStorageOptions' => { 'globalStorageOptions' => {
'Directory' => $sessionDir, 'Directory' => $sessionDir,
'generateModule' => 'generateModule' =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
'LockDirectory' => "$sessionDir/lock" 'LockDirectory' => "$sessionDir/lock"
@ -177,14 +177,14 @@ sub zeroConf {
'inGroup("timelords") or $uid eq "rtyler"', 'inGroup("timelords") or $uid eq "rtyler"',
} }
}, },
'whatToTrace' => '_whatToTrace', 'whatToTrace' => '_whatToTrace',
'securedCookie' => 0, 'securedCookie' => 0,
'cookieName' => 'lemonldap', 'cookieName' => 'lemonldap',
'cfgAuthor' => 'The LemonLDAP::NG team', 'cfgAuthor' => 'The LemonLDAP::NG team',
'cfgDate' => '1627287638', 'cfgDate' => '1627287638',
'cfgVersion' => $VERSION, 'cfgVersion' => $VERSION,
'exportedVars' => {}, 'exportedVars' => {},
'portalSkin' => 'bootstrap', 'portalSkin' => 'bootstrap',
'portalSkinBackground' => 'portalSkinBackground' =>
'1280px-Cedar_Breaks_National_Monument_partially.jpg', '1280px-Cedar_Breaks_National_Monument_partially.jpg',
'mailUrl' => "http://auth.$domain/resetpwd", 'mailUrl' => "http://auth.$domain/resetpwd",

View File

@ -248,7 +248,8 @@ sub sessions {
value => $uid, value => $uid,
count => scalar( @{ $r->{$uid} } ), count => scalar( @{ $r->{$uid} } ),
sessions => [ sessions => [
map { { map {
{
session => session =>
$self->_maybeEncryptSessionId( $_->{_sessionId} ), $self->_maybeEncryptSessionId( $_->{_sessionId} ),
date => $_->{_utime} date => $_->{_utime}
@ -399,7 +400,8 @@ qq{Use of an uninitialized attribute "$group" to group sessions},
else { else {
$res = [ $res = [
sort { $a->{date} <=> $b->{date} } sort { $a->{date} <=> $b->{date} }
map { { map {
{
session => $self->_maybeEncryptSessionId($_), session => $self->_maybeEncryptSessionId($_),
date => $res->{$_}->{_utime} date => $res->{$_}->{_utime}
} }
@ -459,8 +461,8 @@ sub delSession {
} }
sub cmpIPv4 { sub cmpIPv4 {
my @a = split /\./, $_[0]; my @a = split /\./, $_[0];
my @b = split /\./, $_[1]; my @b = split /\./, $_[1];
my $cmp = 0; my $cmp = 0;
F: for ( my $i = 0 ; $i < 4 ; $i++ ) { F: for ( my $i = 0 ; $i < 4 ; $i++ ) {
if ( $a[$i] != $b[$i] ) { if ( $a[$i] != $b[$i] ) {

View File

@ -20,9 +20,9 @@ sub newSession {
$tmp = Lemonldap::NG::Common::Session->new( { $tmp = Lemonldap::NG::Common::Session->new( {
storageModule => 'Apache::Session::File', storageModule => 'Apache::Session::File',
storageModuleOptions => { storageModuleOptions => {
Directory => 't/sessions', Directory => 't/sessions',
LockDirectory => 't/sessions', LockDirectory => 't/sessions',
backend => 'Apache::Session::File', backend => 'Apache::Session::File',
generateModule => generateModule =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
}, },

View File

@ -129,7 +129,7 @@ sub checkGet {
my $res = get( $test, $type, $confKey ); my $res = get( $test, $type, $confKey );
check200( $test, $res ); check200( $test, $res );
my @path = split '/', $attrPath; my @path = split '/', $attrPath;
my $key = from_json( $res->[2]->[0] ); my $key = from_json( $res->[2]->[0] );
for (@path) { for (@path) {
if ( ref($key) eq 'ARRAY' ) { if ( ref($key) eq 'ARRAY' ) {
$key = $key->[$_]; $key = $key->[$_];
@ -326,7 +326,7 @@ checkAddFailsOnInvalidConfkey( $test, 'cat', $cat3 );
checkAddFailsOnInvalidConfkey checkAddFailsOnInvalidConfkey
$test = "Cat - Update should succeed and keep existing values"; $test = "Cat - Update should succeed and keep existing values";
$cat1->{order} = 3; $cat1->{order} = 3;
delete $cat1->{catname}; delete $cat1->{catname};
checkUpdate( $test, 'cat', 'mycat1', $cat1 ); checkUpdate( $test, 'cat', 'mycat1', $cat1 );
@ -419,7 +419,7 @@ $test = "App - Get app myapp1 from mycat3 should err on not found";
checkGetNotFound( $test, 'app/mycat3', 'myapp1' ); checkGetNotFound( $test, 'app/mycat3', 'myapp1' );
$test = "App - Add app myapp1 to mycat3 should err on not found"; $test = "App - Add app myapp1 to mycat3 should err on not found";
checkAddNotFound( $test, 'app/mycat3', $app1); checkAddNotFound( $test, 'app/mycat3', $app1 );
$test = "App - Add app1 to cat1 should succeed"; $test = "App - Add app1 to cat1 should succeed";
checkAdd( $test, 'app/mycat1', $app1 ); checkAdd( $test, 'app/mycat1', $app1 );

View File

@ -23,10 +23,10 @@ is( $brokenconfig->{status}, 'ko', 'Got expected global status' );
is( $brokenconfig->{status_config}, 'ko', 'Got expected config status' ); is( $brokenconfig->{status_config}, 'ko', 'Got expected config status' );
rename 't/conf/lmConf-1.json.broken', 't/conf/lmConf-1.json'; rename 't/conf/lmConf-1.json.broken', 't/conf/lmConf-1.json';
my $allfine = getStatus( "Back to normal" ); my $allfine = getStatus("Back to normal");
is( $allfine->{status}, 'ok', 'Got expected global status' ); is( $allfine->{status}, 'ok', 'Got expected global status' );
is( $allfine->{status_config}, 'ok', 'Got expected config status' ); is( $allfine->{status_config}, 'ok', 'Got expected config status' );
is( $allfine->{status_sessions}, 'unknown', 'Not implemented yet' ); is( $allfine->{status_sessions}, 'unknown', 'Not implemented yet' );
is( $allfine->{status_psessions}, 'unknown', 'Not implemented yet' ); is( $allfine->{status_psessions}, 'unknown', 'Not implemented yet' );
# Clean up generated files, except for "lmConf-1.json" # Clean up generated files, except for "lmConf-1.json"

View File

@ -276,7 +276,7 @@ sub checkFindByProviderId {
($gotProviderId) = $result->{metadata} =~ m/entityID=['"](.+?)['"]/i; ($gotProviderId) = $result->{metadata} =~ m/entityID=['"](.+?)['"]/i;
} }
elsif ( $providerIdName eq 'serviceUrl' ) { elsif ( $providerIdName eq 'serviceUrl' ) {
$gotProviderId = shift @{$result->{options}->{service}}; $gotProviderId = shift @{ $result->{options}->{service} };
} }
else { else {
$gotProviderId = $result->{$providerIdName}; $gotProviderId = $result->{$providerIdName};
@ -337,8 +337,8 @@ my $oidcRp = {
email => 'mail', email => 'mail',
}, },
options => { options => {
clientSecret => 'secret', clientSecret => 'secret',
icon => 'web.png', icon => 'web.png',
postLogoutRedirectUris => postLogoutRedirectUris =>
[ "http://url/logout1", "http://url/logout2" ], [ "http://url/logout1", "http://url/logout2" ],
} }
@ -534,7 +534,7 @@ $samlSp->{options}->{checkSLOMessageSignature} = 1;
$samlSp->{options}->{encryptionMode} = 'nameid'; $samlSp->{options}->{encryptionMode} = 'nameid';
delete $samlSp->{options}->{sessionNotOnOrAfterTimeout}; delete $samlSp->{options}->{sessionNotOnOrAfterTimeout};
delete $samlSp->{exportedAttributes}; delete $samlSp->{exportedAttributes};
$samlSp->{macros}->{family_name} = '$sn', $samlSp->{macros}->{family_name} = '$sn',
$samlSp->{exportedAttributes}->{cn}->{name} = "cn", $samlSp->{exportedAttributes}->{cn}->{name} = "cn",
$samlSp->{exportedAttributes}->{cn}->{friendlyName} = "common_name", $samlSp->{exportedAttributes}->{cn}->{friendlyName} = "common_name",
$samlSp->{exportedAttributes}->{cn}->{mandatory} = "false", $samlSp->{exportedAttributes}->{cn}->{mandatory} = "false",
@ -646,7 +646,9 @@ my $casApp = {
given_name => '$firstName', given_name => '$firstName',
}, },
options => { options => {
service => [ 'http://mycasapp.example.com', 'http://mycasapp2.example.com/test' ], service => [
'http://mycasapp.example.com', 'http://mycasapp2.example.com/test'
],
rule => '$uid eq \'dwho\'', rule => '$uid eq \'dwho\'',
userAttribute => 'uid' userAttribute => 'uid'
} }
@ -663,7 +665,7 @@ $test = "CasApp - Add should fail on duplicate confKey";
checkAddFailsIfExists( $test, 'cas/app', $casApp ); checkAddFailsIfExists( $test, 'cas/app', $casApp );
$test = "CasApp - Update should succeed and keep existing values"; $test = "CasApp - Update should succeed and keep existing values";
$casApp->{options}->{service} = [ 'http://mycasapp.acme.com' ]; $casApp->{options}->{service} = ['http://mycasapp.acme.com'];
$casApp->{options}->{userAttribute} = 'cn'; $casApp->{options}->{userAttribute} = 'cn';
delete $casApp->{options}->{rule}; delete $casApp->{options}->{rule};
delete $casApp->{macros}; delete $casApp->{macros};
@ -672,7 +674,7 @@ $casApp->{macros}->{given_name} = '$givenName';
$casApp->{exportedVars}->{cn} = 'uid'; $casApp->{exportedVars}->{cn} = 'uid';
checkUpdate( $test, 'cas/app', 'myCasApp1', $casApp ); checkUpdate( $test, 'cas/app', 'myCasApp1', $casApp );
checkGet( $test, 'cas/app', 'myCasApp1', 'options/service/0', checkGet( $test, 'cas/app', 'myCasApp1', 'options/service/0',
'http://mycasapp.acme.com'); 'http://mycasapp.acme.com' );
checkGet( $test, 'cas/app', 'myCasApp1', 'options/userAttribute', 'cn' ); checkGet( $test, 'cas/app', 'myCasApp1', 'options/userAttribute', 'cn' );
checkGet( $test, 'cas/app', 'myCasApp1', 'options/rule', '$uid eq \'dwho\'' ); checkGet( $test, 'cas/app', 'myCasApp1', 'options/rule', '$uid eq \'dwho\'' );
checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/cn', 'uid' ); checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/cn', 'uid' );
@ -686,17 +688,17 @@ delete $casApp->{options}->{playingPossum};
$test = "CasApp - Add should fail on non existing options"; $test = "CasApp - Add should fail on non existing options";
$casApp->{confKey} = 'myCasApp2'; $casApp->{confKey} = 'myCasApp2';
$casApp->{options}->{service} = [ 'http://mycasapp.skynet.com' ]; $casApp->{options}->{service} = ['http://mycasapp.skynet.com'];
$casApp->{options}->{playingPossum} = 'ElephantInTheRoom'; $casApp->{options}->{playingPossum} = 'ElephantInTheRoom';
checkAddWithUnknownAttributes( $test, 'cas/app', $casApp ); checkAddWithUnknownAttributes( $test, 'cas/app', $casApp );
delete $casApp->{options}->{playingPossum}; delete $casApp->{options}->{playingPossum};
$test = "CasApp - Add should fail because service host already exists"; $test = "CasApp - Add should fail because service host already exists";
$casApp->{options}->{service} = [ 'http://mycasapp.acme.com/ignoredbyissuer' ]; $casApp->{options}->{service} = ['http://mycasapp.acme.com/ignoredbyissuer'];
checkAddFailsIfExists( $test, 'cas/app', $casApp ); checkAddFailsIfExists( $test, 'cas/app', $casApp );
$test = "CasApp - 2nd add should succeed"; $test = "CasApp - 2nd add should succeed";
$casApp->{options}->{service} = [ 'http://mycasapp.skynet.com' ]; $casApp->{options}->{service} = ['http://mycasapp.skynet.com'];
checkAdd( $test, 'cas/app', $casApp ); checkAdd( $test, 'cas/app', $casApp );
$test = "CasApp - Update should fail if confKey not found"; $test = "CasApp - Update should fail if confKey not found";
@ -714,7 +716,7 @@ $test = "CasApp - Replace should fail on non existing or invalid options";
$casApp->{options}->{playingPossum} = 'elephant'; $casApp->{options}->{playingPossum} = 'elephant';
checkReplaceWithInvalidAttribute( $test, 'cas/app', 'myCasApp2', $casApp ); checkReplaceWithInvalidAttribute( $test, 'cas/app', 'myCasApp2', $casApp );
delete $casApp->{options}->{playingPossum}; delete $casApp->{options}->{playingPossum};
$casApp->{options}->{service} = [ "XXX" ]; $casApp->{options}->{service} = ["XXX"];
checkReplaceWithInvalidAttribute( $test, 'cas/app', 'myCasApp2', $casApp ); checkReplaceWithInvalidAttribute( $test, 'cas/app', 'myCasApp2', $casApp );
$test = "CasApp - Replace should fail if service is not an array"; $test = "CasApp - Replace should fail if service is not an array";

View File

@ -58,7 +58,7 @@ while (<F>) {
close F; close F;
ok( $hstruct = from_json($hstruct), 'struct.json is JSON' ); ok( $hstruct = from_json($hstruct), 'struct.json is JSON' );
ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' ) ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' )
or print STDERR "Expected: ARRAY, got: " . ( ref $hstruct ) . "\n"; or print STDERR "Expected: ARRAY, got: " . ( ref $hstruct ) . "\n";
count(2); count(2);

View File

@ -15,7 +15,7 @@ sub checkResult {
like( $key->{private}, qr/BEGIN/, "is PEM formatted" ); like( $key->{private}, qr/BEGIN/, "is PEM formatted" );
like( $key->{public}, qr/BEGIN/, "is PEM formatted" ); like( $key->{public}, qr/BEGIN/, "is PEM formatted" );
ok( $key->{hash}, "hash is non empty" ) if $expecthash; ok( $key->{hash}, "hash is non empty" ) if $expecthash;
count(1) if $expecthash; count(1) if $expecthash;
count(4); count(4);
} }
@ -53,7 +53,7 @@ checkResult($res);
ok( ok(
$res = &client->_post( $res = &client->_post(
'/confs/newCertificate', '', IO::String->new('{"password":"hello"}'), '/confs/newCertificate', '', IO::String->new('{"password":"hello"}'),
'application/json', 20, 'application/json', 20,
), ),
"Request succeed" "Request succeed"
); );

View File

@ -20,7 +20,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" ) ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
@ -36,7 +36,7 @@ count(6);
foreach my $i ( 0 .. 3 ) { foreach my $i ( 0 .. 3 ) {
ok( ok(
$resBody->{details}->{__warnings__}->[$i]->{message} =~ $resBody->{details}->{__warnings__}->[$i]->{message} =~
/\b(unprotected|cross-domain-authentication|retries|__badExpressionAssignment__)\b/, /\b(unprotected|cross-domain-authentication|retries|__badExpressionAssignment__)\b/,
"Warning with 'unprotect', 'CDA', 'assignment' or 'retries' found" "Warning with 'unprotect', 'CDA', 'assignment' or 'retries' found"
) or print STDERR Dumper($resBody); ) or print STDERR Dumper($resBody);
count(1); count(1);

View File

@ -20,7 +20,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($resBody); or print STDERR Dumper($resBody);

View File

@ -16,7 +16,7 @@ unlink 't/conf/lmConf-2.json';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" ) ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -63,7 +63,7 @@ SKIP: {
Lemonldap::NG::Manager::Cli->run(@args); Lemonldap::NG::Manager::Cli->run(@args);
my $res = $dbh->selectrow_hashref( my $res = $dbh->selectrow_hashref(
"SELECT * FROM lmConfig WHERE field='ldapSetPassword'"); "SELECT * FROM lmConfig WHERE field='ldapSetPassword'");
ok( $res, 'Key inserted' ); ok( $res, 'Key inserted' );
ok( $res and $res->{value} == '0', 'Value is 0' ); ok( $res and $res->{value} == '0', 'Value is 0' );
} }

View File

@ -16,8 +16,8 @@ sub newSession {
$tmp = Lemonldap::NG::Common::Session->new( { $tmp = Lemonldap::NG::Common::Session->new( {
storageModule => 'Apache::Session::File', storageModule => 'Apache::Session::File',
storageModuleOptions => { storageModuleOptions => {
Directory => 't/sessions', Directory => 't/sessions',
LockDirectory => 't/sessions', LockDirectory => 't/sessions',
generateModule => generateModule =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
}, },
@ -148,7 +148,7 @@ count(5);
foreach (@ids) { foreach (@ids) {
my $res; my $res;
ok( $res = &client->_del("/sessions/global/$_"), "Delete $_" ); ok( $res = &client->_del("/sessions/global/$_"), "Delete $_" );
ok( $res->[0] == 200, 'Result code is 200' ); ok( $res->[0] == 200, 'Result code is 200' );
ok( from_json( $res->[2]->[0] )->{result} == 1, ok( from_json( $res->[2]->[0] )->{result} == 1,
'Body is JSON and result==1' ); 'Body is JSON and result==1' );
count(3); count(3);

View File

@ -86,7 +86,7 @@ SKIP: {
$notif = '{"done":1}'; $notif = '{"done":1}';
$res = $client->jsonPutResponse( $res = $client->jsonPutResponse(
'notifications/actives/dwho_Test', 'notifications/actives/dwho_Test',
'', IO::String->new($notif), '', IO::String->new($notif),
'application/json', length($notif) 'application/json', length($notif)
); );
ok( $res->{result} == 1, 'Result = 1' ); ok( $res->{result} == 1, 'Result = 1' );

View File

@ -56,7 +56,7 @@ displayTests('actives');
$notif = '{"done":1}'; $notif = '{"done":1}';
$res = &client->jsonPutResponse( $res = &client->jsonPutResponse(
'notifications/actives/dwho_Test', 'notifications/actives/dwho_Test',
'', IO::String->new($notif), '', IO::String->new($notif),
'application/json', length($notif) 'application/json', length($notif)
); );
ok( $res->{result} == 1, 'Result = 1' ); ok( $res->{result} == 1, 'Result = 1' );
@ -137,7 +137,7 @@ sub displayTests {
) or diag Dumper($res); ) or diag Dumper($res);
my $internal_ref = $res->{values}->[0]->{notification}; my $internal_ref = $res->{values}->[0]->{notification};
my $ref = $res->{values}->[0]->{reference}; my $ref = $res->{values}->[0]->{reference};
$res = &client->jsonResponse( "notifications/$type/$internal_ref" ); $res = &client->jsonResponse("notifications/$type/$internal_ref");
ok( $res->{done} eq $internal_ref, 'Internal reference found' ) ok( $res->{done} eq $internal_ref, 'Internal reference found' )
or diag Dumper($res); or diag Dumper($res);
ok( $res = eval { from_json( $res->{notifications}->[0] ) }, ok( $res = eval { from_json( $res->{notifications}->[0] ) },

View File

@ -16,8 +16,8 @@ sub newSession {
$tmp = Lemonldap::NG::Common::Session->new( { $tmp = Lemonldap::NG::Common::Session->new( {
storageModule => 'Apache::Session::File', storageModule => 'Apache::Session::File',
storageModuleOptions => { storageModuleOptions => {
Directory => 't/sessions', Directory => 't/sessions',
LockDirectory => 't/sessions', LockDirectory => 't/sessions',
generateModule => generateModule =>
'Lemonldap::NG::Common::Apache::Session::Generate::SHA256', 'Lemonldap::NG::Common::Apache::Session::Generate::SHA256',
}, },
@ -277,7 +277,7 @@ $res = &client->jsonResponse( '/sfa/persistent',
'uid=*&groupBy=substr(uid,0)&U2FCheck=2&TOTPCheck=2&UBKCheck=2' ); 'uid=*&groupBy=substr(uid,0)&U2FCheck=2&TOTPCheck=2&UBKCheck=2' );
ok( $res->{result} == 1, ok( $res->{result} == 1,
'Search "uid"=* & UBK & TOTP & UBK - Result code = 1' ); 'Search "uid"=* & UBK & TOTP & UBK - Result code = 1' );
ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res); ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res);
ok( @{ $res->{values} } == 1, 'List 1 result' ); ok( @{ $res->{values} } == 1, 'List 1 result' );
ok( $res->{values}->[0]->{value} && $res->{values}->[0]->{value} eq 'd', ok( $res->{values}->[0]->{value} && $res->{values}->[0]->{value} eq 'd',
'Result match "uid=d"' ) 'Result match "uid=d"' )

View File

@ -18,7 +18,8 @@ sub body {
# Test that key value is sent # Test that key value is sent
my $res = &client->jsonResponse('/view/1/portalDisplayOidcConsents'); my $res = &client->jsonResponse('/view/1/portalDisplayOidcConsents');
ok( $res->{value} eq '$_oidcConsents && $_oidcConsents =~ /\\w+/', 'Key found' ); ok( $res->{value} eq '$_oidcConsents && $_oidcConsents =~ /\\w+/',
'Key found' );
count(1); count(1);
# Test that hidden key values are NOT sent # Test that hidden key values are NOT sent

View File

@ -38,14 +38,14 @@ my @notManagedAttributes = (
'syslogFacility', 'userLogger', 'logLevel', 'syslogFacility', 'userLogger', 'logLevel',
# Plugins parameters # Plugins parameters
'notificationsMaxRetrieve', 'persistentSessionAttributes', 'notificationsMaxRetrieve', 'persistentSessionAttributes',
# PSGI/CGI protection (must be set in lemonldap-ng.ini) # PSGI/CGI protection (must be set in lemonldap-ng.ini)
'protection', 'protection',
# SecureToken handler # SecureToken handler
'secureTokenAllowOnError', 'secureTokenAttribute', 'secureTokenExpiration', 'secureTokenAllowOnError', 'secureTokenAttribute', 'secureTokenExpiration',
'secureTokenHeader', 'secureTokenMemcachedServers', 'secureTokenUrls', 'secureTokenHeader', 'secureTokenMemcachedServers', 'secureTokenUrls',
# Sessions and OTT storage # Sessions and OTT storage
'configStorage', 'localStorageOptions', 'localStorage', 'configStorage', 'localStorageOptions', 'localStorage',
@ -157,7 +157,7 @@ sub scanTree {
# Nodes must have a title # Nodes must have a title
ok( $name = $leaf->{title}, "Node has a name" ); ok( $name = $leaf->{title}, "Node has a name" );
ok( $name =~ /^\w+$/, "Name is a string" ); ok( $name =~ /^\w+$/, "Name is a string" );
# Nodes must have leafs or subnodes # Nodes must have leafs or subnodes
ok( ( ok( (

View File

@ -385,7 +385,8 @@ sub run {
MSG => $self->canUpdateSfa($req) || 'choose2f', MSG => $self->canUpdateSfa($req) || 'choose2f',
ALERT => ( $self->canUpdateSfa($req) ? 'warning' : 'positive' ), ALERT => ( $self->canUpdateSfa($req) ? 'warning' : 'positive' ),
MODULES => [ MODULES => [
map { { map {
{
CODE => $_->prefix, CODE => $_->prefix,
LOGO => $_->logo, LOGO => $_->logo,
LABEL => $_->label LABEL => $_->label
@ -440,7 +441,7 @@ sub _choice {
return $self->p->do( return $self->p->do(
$req, $req,
[ [
sub { $res }, 'controlUrl', sub { $res }, 'controlUrl',
'buildCookie', @{ $self->p->endAuth }, 'buildCookie', @{ $self->p->endAuth },
] ]
); );

View File

@ -180,7 +180,7 @@ sub _registration {
if ( if (
$self->find2fByKey( $self->find2fByKey(
$req, $req->userData, $self->type, $req, $req->userData, $self->type,
"_credentialId", $credential_id "_credentialId", $credential_id
) )
) )

View File

@ -255,8 +255,7 @@ sub authenticate {
sub setAuthSessionInfo { sub setAuthSessionInfo {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
$req->{sessionInfo}->{authenticationLevel} = $self->conf->{casAuthnLevel}; $req->{sessionInfo}->{authenticationLevel} = $self->conf->{casAuthnLevel};
$req->{sessionInfo}->{_casSrv} $req->{sessionInfo}->{_casSrv} = $req->data->{_casSrvCurrent};
= $req->data->{_casSrvCurrent};
return PE_OK; return PE_OK;
} }

View File

@ -155,8 +155,7 @@ sub extractFormInfo {
$self->logger->debug("Response from GitHub User API: $user_content"); $self->logger->debug("Response from GitHub User API: $user_content");
eval { eval { $json_hash = from_json( $user_content, { allow_nonref => 1 } ); };
$json_hash = from_json( $user_content, { allow_nonref => 1 } ); };
if ($@) { if ($@) {
$self->logger->error("Unable to decode JSON $user_content"); $self->logger->error("Unable to decode JSON $user_content");
return PE_ERROR; return PE_ERROR;

View File

@ -33,7 +33,7 @@ has reWebIDWhitelist => ( is => 'rw' );
sub init { sub init {
my ($self) = @_; my ($self) = @_;
my @hosts = split /\s+/, $self->{conf}->{webIDWhitelist}; my @hosts = split /\s+/, $self->{conf}->{webIDWhitelist};
unless (@hosts) { unless (@hosts) {
$self->error( $self->error(
'WebID white list is empty. Set it in manager, use * to accept all FOAF providers' 'WebID white list is empty. Set it in manager, use * to accept all FOAF providers'

View File

@ -90,11 +90,13 @@ sub extractFormInfo {
# 3. If user and oldpassword defined -> password form # 3. If user and oldpassword defined -> password form
elsif ( $defUser and $defOldPassword ) { elsif ( $defUser and $defOldPassword ) {
$res = PE_PASSWORDFORMEMPTY $res = PE_PASSWORDFORMEMPTY
unless ( ( $req->{user} = $req->param('user') ) unless (
( $req->{user} = $req->param('user') )
&& ( $req->data->{oldpassword} = $req->param('oldpassword') ) && ( $req->data->{oldpassword} = $req->param('oldpassword') )
&& ( $req->data->{newpassword} = $req->param('newpassword') ) && ( $req->data->{newpassword} = $req->param('newpassword') )
&& ( $req->data->{confirmpassword} = && ( $req->data->{confirmpassword} =
$req->param('confirmpassword') ) ); $req->param('confirmpassword') )
);
} }
# If form seems empty # If form seems empty

View File

@ -703,7 +703,7 @@ sub run {
# Store data in session # Store data in session
my $code_payload = { my $code_payload = {
code_challenge => $oidc_request->{'code_challenge'}, code_challenge => $oidc_request->{'code_challenge'},
code_challenge_method => code_challenge_method =>
$oidc_request->{'code_challenge_method'}, $oidc_request->{'code_challenge_method'},
nonce => $oidc_request->{'nonce'}, nonce => $oidc_request->{'nonce'},
@ -765,7 +765,7 @@ sub run {
$self->logger->error("Unable to create Access Token"); $self->logger->error("Unable to create Access Token");
$self->returnRedirectError( $req, $self->returnRedirectError( $req,
$oidc_request->{'redirect_uri'}, $oidc_request->{'redirect_uri'},
"server_error", undef, undef, "server_error", undef, undef,
$oidc_request->{'state'}, 1 ); $oidc_request->{'state'}, 1 );
} }
@ -873,7 +873,7 @@ sub run {
$self->logger->error("Unable to create Access Token"); $self->logger->error("Unable to create Access Token");
return $self->returnRedirectError( $req, return $self->returnRedirectError( $req,
$oidc_request->{'redirect_uri'}, $oidc_request->{'redirect_uri'},
"server_error", undef, undef, "server_error", undef, undef,
$oidc_request->{'state'}, 1 ); $oidc_request->{'state'}, 1 );
} }
@ -1328,7 +1328,7 @@ sub _handlePasswordGrant {
access_token => "$access_token", access_token => "$access_token",
token_type => 'Bearer', token_type => 'Bearer',
expires_in => $expires_in + 0, expires_in => $expires_in + 0,
( ( $scope ne $req_scope ) ? ( scope => "$scope" ) : () ), ( ( $scope ne $req_scope ) ? ( scope => "$scope" ) : () ),
( $refresh_token ? ( refresh_token => "$refresh_token" ) : () ), ( $refresh_token ? ( refresh_token => "$refresh_token" ) : () ),
( $id_token ? ( id_token => "$id_token" ) : () ), ( $id_token ? ( id_token => "$id_token" ) : () ),
}; };
@ -1526,7 +1526,7 @@ sub _handleAuthorizationCodeGrant {
expires_in => $expires_in + 0, expires_in => $expires_in + 0,
id_token => "$id_token", id_token => "$id_token",
( $refresh_token ? ( refresh_token => "$refresh_token" ) : () ), ( $refresh_token ? ( refresh_token => "$refresh_token" ) : () ),
( ( $req_scope ne $scope ) ? ( scope => "$scope" ) : () ), ( ( $req_scope ne $scope ) ? ( scope => "$scope" ) : () ),
}; };
my $cRP = $apacheSession->data->{_oidcConnectedRP} || ''; my $cRP = $apacheSession->data->{_oidcConnectedRP} || '';
@ -2195,10 +2195,10 @@ sub metadata {
# Scopes # Scopes
scopes_supported => [qw/openid profile email address phone/], scopes_supported => [qw/openid profile email address phone/],
response_types_supported => $response_types, response_types_supported => $response_types,
grant_types_supported => $grant_types, grant_types_supported => $grant_types,
acr_values_supported => \@acr, acr_values_supported => \@acr,
subject_types_supported => ["public"], subject_types_supported => ["public"],
token_endpoint_auth_methods_supported => token_endpoint_auth_methods_supported =>
[qw/client_secret_post client_secret_basic/], [qw/client_secret_post client_secret_basic/],
introspection_endpoint_auth_methods_supported => introspection_endpoint_auth_methods_supported =>
@ -2380,9 +2380,9 @@ sub _generateIDToken {
exp => $id_token_exp, # expiration exp => $id_token_exp, # expiration
iat => time, # Issued time iat => time, # Issued time
auth_time => $sessionInfo->{_lastAuthnUTime}, # Authentication time auth_time => $sessionInfo->{_lastAuthnUTime}, # Authentication time
acr => $id_token_acr, # Authentication Context Class Reference acr => $id_token_acr, # Authentication Context Class Reference
azp => $client_id, # Authorized party azp => $client_id, # Authorized party
# TODO amr # TODO amr
}; };
for ( keys %{$extra_claims} ) { for ( keys %{$extra_claims} ) {

View File

@ -281,7 +281,7 @@ sub run {
$req->data->{_proxiedRequest} = $request; $req->data->{_proxiedRequest} = $request;
$req->data->{_proxiedMethod} = $method; $req->data->{_proxiedMethod} = $method;
$req->data->{_proxiedRelayState} = $relaystate, $req->data->{_proxiedRelayState} = $relaystate,
$req->data->{_proxiedArtifact} = $artifact; $req->data->{_proxiedArtifact} = $artifact;
} }
# Process the request or use IDP initiated mode # Process the request or use IDP initiated mode
@ -597,8 +597,8 @@ sub run {
# Get session key associated with NameIDFormat # Get session key associated with NameIDFormat
# Not for unspecified, transient, persistent, entity, encrypted # Not for unspecified, transient, persistent, entity, encrypted
my $nameIDFormatConfiguration = { my $nameIDFormatConfiguration = {
$self->getNameIDFormat("email") => 'samlNameIDFormatMapEmail', $self->getNameIDFormat("email") => 'samlNameIDFormatMapEmail',
$self->getNameIDFormat("x509") => 'samlNameIDFormatMapX509', $self->getNameIDFormat("x509") => 'samlNameIDFormatMapX509',
$self->getNameIDFormat("windows") => $self->getNameIDFormat("windows") =>
'samlNameIDFormatMapWindows', 'samlNameIDFormatMapWindows',
$self->getNameIDFormat("kerberos") => $self->getNameIDFormat("kerberos") =>

View File

@ -9,10 +9,10 @@ with 'Lemonldap::NG::Portal::Lib::OverConf';
our $VERSION = '2.0.14'; our $VERSION = '2.0.14';
has modules => ( is => 'rw', default => sub { {} } ); has modules => ( is => 'rw', default => sub { {} } );
has rules => ( is => 'rw', default => sub { {} } ); has rules => ( is => 'rw', default => sub { {} } );
has type => ( is => 'rw' ); has type => ( is => 'rw' );
has catch => ( is => 'rw', default => sub { {} } ); has catch => ( is => 'rw', default => sub { {} } );
has sessionKey => ( is => 'ro', default => '_choice' ); has sessionKey => ( is => 'ro', default => '_choice' );
my $_choiceRules; my $_choiceRules;

View File

@ -72,8 +72,8 @@ has findUserFilter => (
is => 'ro', is => 'ro',
lazy => 1, lazy => 1,
builder => sub { builder => sub {
$_[0]->conf->{AuthLDAPFilter} || $_[0]->conf->{AuthLDAPFilter}
$_[0]->conf->{LDAPFilter} || $_[0]->conf->{LDAPFilter}
|| '(&(uid=$user)(objectClass=inetOrgPerson))'; || '(&(uid=$user)(objectClass=inetOrgPerson))';
} }
); );

View File

@ -52,7 +52,7 @@ sub new {
( $conf->{ldapVerify} ? ( verify => $conf->{ldapVerify} ) : () ), ( $conf->{ldapVerify} ? ( verify => $conf->{ldapVerify} ) : () ),
); );
unless ($self) { unless ($self) {
$portal->logger->error("LDAP initialization error: ". $@); $portal->logger->error( "LDAP initialization error: " . $@ );
return 0; return 0;
} }
elsif ( $Net::LDAP::VERSION < '0.64' ) { elsif ( $Net::LDAP::VERSION < '0.64' ) {

View File

@ -69,7 +69,7 @@ sub checkForNotifications {
} }
# Transform notifications # Transform notifications
my $i = 0; # Files count my $i = 0; # Files count
my $now = strftime "%Y-%m-%d", localtime; my $now = strftime "%Y-%m-%d", localtime;
foreach my $file ( values %$notifs ) { foreach my $file ( values %$notifs ) {

View File

@ -29,7 +29,7 @@ use constant PROFILE => [
qw/name family_name given_name middle_name nickname preferred_username qw/name family_name given_name middle_name nickname preferred_username
profile picture website gender birthdate zoneinfo locale updated_at/ profile picture website gender birthdate zoneinfo locale updated_at/
]; ];
use constant EMAIL => [qw/email email_verified/]; use constant EMAIL => [qw/email email_verified/];
use constant ADDRESS => use constant ADDRESS =>
[qw/formatted street_address locality region postal_code country/]; [qw/formatted street_address locality region postal_code country/];
use constant PHONE => [qw/phone_number phone_number_verified/]; use constant PHONE => [qw/phone_number phone_number_verified/];
@ -1031,7 +1031,7 @@ sub storeState {
# check if there are data to store # check if there are data to store
my $infos; my $infos;
foreach (@data) { foreach (@data) {
$infos->{$_} = $req->{$_} if $req->{$_}; $infos->{$_} = $req->{$_} if $req->{$_};
$infos->{"data_$_"} = $req->data->{$_} if $req->data->{$_}; $infos->{"data_$_"} = $req->data->{$_} if $req->data->{$_};
} }
return unless ($infos); return unless ($infos);

View File

@ -59,7 +59,7 @@ sub checkRemoteId {
# Trying to recover session from global session storage # Trying to recover session from global session storage
my $remoteSession = Lemonldap::NG::Common::Session->new( { my $remoteSession = Lemonldap::NG::Common::Session->new( {
storageModule => $self->conf->{remoteGlobalStorage}, storageModule => $self->conf->{remoteGlobalStorage},
storageModuleOptions => storageModuleOptions =>
$self->conf->{remoteGlobalStorageOptions}, $self->conf->{remoteGlobalStorageOptions},
cacheModule => $self->conf->{localSessionStorage}, cacheModule => $self->conf->{localSessionStorage},

View File

@ -1888,7 +1888,8 @@ sub resolveArtifact {
$self->logger->debug("Get message $message"); $self->logger->debug("Get message $message");
} }
else { else {
$self->logger->error("Error while sending message: ".$soap_answer->status_line); $self->logger->error(
"Error while sending message: " . $soap_answer->status_line );
} }
} }
@ -3039,7 +3040,7 @@ sub createAttributeValue {
# Decode UTF-8 # Decode UTF-8
$self->logger->debug("Decode UTF8 value $value") if $force_utf8; $self->logger->debug("Decode UTF8 value $value") if $force_utf8;
$value = decode( "utf8", $value ) if $force_utf8; $value = decode( "utf8", $value ) if $force_utf8;
$self->logger->debug("Create attribute value $value"); $self->logger->debug("Create attribute value $value");
# SAML2 attribute value # SAML2 attribute value

View File

@ -47,7 +47,7 @@ sub loadMailTemplate {
my ( $self, $req, $name, %prm ) = @_; my ( $self, $req, $name, %prm ) = @_;
# HTML::Template cache interferes with email translation (#1897) # HTML::Template cache interferes with email translation (#1897)
$prm{cache} = 0 unless defined $prm{cache}; $prm{cache} = 0 unless defined $prm{cache};
$prm{params}->{STATIC_PREFIX} = $self->p->staticPrefix; $prm{params}->{STATIC_PREFIX} = $self->p->staticPrefix;
$prm{params}->{MAIN_LOGO} = $self->conf->{portalMainLogo}; $prm{params}->{MAIN_LOGO} = $self->conf->{portalMainLogo};
my %extra = my %extra =

View File

@ -10,8 +10,8 @@ use Carp;
our $VERSION = '2.0.12'; our $VERSION = '2.0.12';
has rp_id => ( is => 'rw', lazy => 1, builder => "_build_rp_id" ); has rp_id => ( is => 'rw', lazy => 1, builder => "_build_rp_id" );
has origin => ( is => 'rw', lazy => 1, builder => "_build_origin" ); has origin => ( is => 'rw', lazy => 1, builder => "_build_origin" );
has type => ( is => 'ro', default => 'WebAuthn' ); has type => ( is => 'ro', default => 'WebAuthn' );
has verifier => ( is => 'rw', lazy => 1, builder => "_build_verifier" ); has verifier => ( is => 'rw', lazy => 1, builder => "_build_verifier" );

View File

@ -32,7 +32,7 @@ sub authCancel { '_authCancel' }
sub _betweenAuthAndData { _wrapEntryPoint( @_, 'betweenAuthAndData' ); } sub _betweenAuthAndData { _wrapEntryPoint( @_, 'betweenAuthAndData' ); }
sub _afterData { _wrapEntryPoint( @_, 'afterData' ); } sub _afterData { _wrapEntryPoint( @_, 'afterData' ); }
sub _endAuth { _wrapEntryPoint( @_, 'endAuth' ); } sub _endAuth { _wrapEntryPoint( @_, 'endAuth' ); }
sub _forAuthUser { _wrapEntryPoint( @_, 'forAuthUser', 1 ); } sub _forAuthUser { _wrapEntryPoint( @_, 'forAuthUser', 1 ); }
sub _beforeLogout { _wrapEntryPoint( @_, 'beforeLogout', 1 ); } sub _beforeLogout { _wrapEntryPoint( @_, 'beforeLogout', 1 ); }
sub _authCancel { _wrapEntryPoint( @_, 'authCancel' ); } sub _authCancel { _wrapEntryPoint( @_, 'authCancel' ); }

View File

@ -11,6 +11,6 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
has authnLevel => ( is => 'rw' ); has authnLevel => ( is => 'rw' );
sub stop {0} sub stop { 0 }
1; 1;

View File

@ -7,7 +7,7 @@ use Exporter 'import';
our $VERSION = '2.0.14'; our $VERSION = '2.0.14';
use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main'; use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main';
use constant URIRE => use constant URIRE =>
qr{(((?^:https?))://((?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::((?:[0-9]*)))?(/(((?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?]((?:(?:[;/?:\@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)}; qr{(((?^:https?))://((?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::((?:[0-9]*)))?(/(((?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():\@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?]((?:(?:[;/?:\@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)};
use constant { use constant {
PE_IDPCHOICE => -5, PE_IDPCHOICE => -5,

View File

@ -29,7 +29,8 @@ sub displayInit {
else { else {
$self->logger->error( $self->logger->error(
qq(Skin rule "$skinRule" returns an error: ) qq(Skin rule "$skinRule" returns an error: )
. HANDLER->tsv->{jail}->error || 'Unable to compile rule' ); . HANDLER->tsv->{jail}->error
|| 'Unable to compile rule' );
} }
} }
} }
@ -37,14 +38,14 @@ sub displayInit {
HANDLER->substitute( $self->conf->{portalRequireOldPassword} ) ); HANDLER->substitute( $self->conf->{portalRequireOldPassword} ) );
unless ($rule) { unless ($rule) {
my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule'; my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule';
$self->logger->error( "Bad requireOldPwd rule: $error" ); $self->logger->error("Bad requireOldPwd rule: $error");
} }
$self->requireOldPwd($rule); $self->requireOldPwd($rule);
$rule = $rule =
HANDLER->buildSub( HANDLER->substitute( $self->conf->{stayConnected} ) ); HANDLER->buildSub( HANDLER->substitute( $self->conf->{stayConnected} ) );
unless ($rule) { unless ($rule) {
my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule'; my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule';
$self->logger->error( "Bad stayConnected rule: $error" ); $self->logger->error("Bad stayConnected rule: $error");
} }
$self->stayConnected($rule); $self->stayConnected($rule);

View File

@ -128,9 +128,9 @@ sub init {
# Purge loaded module list # Purge loaded module list
$self->loadedModules( {} ); $self->loadedModules( {} );
$self->afterSub( {} ); $self->afterSub( {} );
$self->aroundSub( {} ); $self->aroundSub( {} );
$self->hook( {} ); $self->hook( {} );
# Insert `reloadConf` in handler reload stack # Insert `reloadConf` in handler reload stack
Lemonldap::NG::Handler::Main->onReload( $self, 'reloadConf' ); Lemonldap::NG::Handler::Main->onReload( $self, 'reloadConf' );
@ -228,10 +228,10 @@ sub reloadConf {
foreach ( qw(_macros _groups), @entryPoints ) { foreach ( qw(_macros _groups), @entryPoints ) {
$self->{$_} = []; $self->{$_} = [];
} }
$self->afterSub( {} ); $self->afterSub( {} );
$self->aroundSub( {} ); $self->aroundSub( {} );
$self->spRules( {} ); $self->spRules( {} );
$self->hook( {} ); $self->hook( {} );
# Plugin history fields # Plugin history fields
$self->pluginSessionDataToRemember( {} ); $self->pluginSessionDataToRemember( {} );

Some files were not shown because too many files have changed in this diff Show More