Merge branch 'v2.0'
This commit is contained in:
commit
b7c8d30b3f
|
@ -1,5 +1,6 @@
|
|||
/etc/lemonldap-ng/manager-apache2.conf
|
||||
/etc/lemonldap-ng/manager-nginx.conf
|
||||
/usr/share/man/man3/Lemonldap::NG::Manager*
|
||||
/usr/share/perl5/Lemonldap/NG/Manager*
|
||||
/usr/share/lemonldap-ng/bin/lemonldap-ng-cli
|
||||
/usr/share/lemonldap-ng/manager
|
||||
|
|
|
@ -37,6 +37,7 @@ lib/Lemonldap/NG/Common/Crypto.pm
|
|||
lib/Lemonldap/NG/Common/FormEncode.pm
|
||||
lib/Lemonldap/NG/Common/IO/Filter.pm
|
||||
lib/Lemonldap/NG/Common/IPv6.pm
|
||||
lib/Lemonldap/NG/Common/Logger/_Duplicate.pm
|
||||
lib/Lemonldap/NG/Common/Logger/Apache2.pm
|
||||
lib/Lemonldap/NG/Common/Logger/Dispatch.pm
|
||||
lib/Lemonldap/NG/Common/Logger/Log4perl.pm
|
||||
|
|
|
@ -292,6 +292,11 @@ languages = en, fr, vi, it, ar, de, fi
|
|||
; Set to 0 to disable error on XSS attack detection
|
||||
;checkXSS = 0
|
||||
|
||||
; pdata cookie domain
|
||||
; pdata cookie could not be sent with cross domains AJAX request
|
||||
; Null is default value
|
||||
;pdataDomain = example.com
|
||||
|
||||
; CUSTOM PLUGINS
|
||||
; If you want to add custom plugins, set list here (comma separated)
|
||||
; Read Lemonldap::NG::Portal::Main::Plugin(3pm) man page.
|
||||
|
|
|
@ -28,7 +28,7 @@ sub defaultValues {
|
|||
'casAccessControlPolicy' => 'none',
|
||||
'casAuthnLevel' => 1,
|
||||
'checkTime' => 600,
|
||||
'checkUserHiddenAttributes' => '_loginHistory hGroups',
|
||||
'checkUserHiddenAttributes' => '_loginHistory _session_id hGroups',
|
||||
'checkUserIdRule' => 1,
|
||||
'checkXSS' => 1,
|
||||
'confirmFormMethod' => 'post',
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package Lemonldap::NG::Common::Logger::_Duplicate;
|
||||
|
||||
use strict;
|
||||
|
||||
our $VERSION = '2.0.6';
|
||||
|
||||
sub new {
|
||||
my $self = bless {}, shift;
|
||||
my ( $conf, %args ) = @_;
|
||||
eval "require $args{logger}";
|
||||
die $@ if ($@);
|
||||
$self->{logger} = $args{logger}->new(@_);
|
||||
$self->{dup} = $args{dup} or die 'Missing dup';
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
no strict;
|
||||
$AUTOLOAD =~ s/.*:://;
|
||||
return if $AUTOLOAD eq 'DESTROY';
|
||||
$self->{logger}->$AUTOLOAD(@_);
|
||||
my $msg = shift;
|
||||
$msg = "[$AUTOLOAD] $msg";
|
||||
$self->{dup}->debug( $msg, @_ );
|
||||
}
|
||||
|
||||
1;
|
|
@ -49,7 +49,15 @@ sub init {
|
|||
$logger = $ENV{LLNG_USERLOGGER} || $args->{userLogger} || $logger;
|
||||
eval "require $logger";
|
||||
die $@ if ($@);
|
||||
$self->userLogger( $logger->new( $self, user => 1 ) );
|
||||
require Lemonldap::NG::Common::Logger::_Duplicate;
|
||||
$self->userLogger(
|
||||
Lemonldap::NG::Common::Logger::_Duplicate->new(
|
||||
$self,
|
||||
user => 1,
|
||||
logger => $logger,
|
||||
dup => $self->logger
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
|
|
@ -51,7 +51,7 @@ Available actions:
|
|||
- restore - : import configuration from STDIN
|
||||
- restore <file> : import configuration from file
|
||||
|
||||
See Lemonldap::NG::Common::Cli(3) or Lemonldap::NG::Manager::CLi(3) for more
|
||||
See Lemonldap::NG::Manager::Cli(3) for more
|
||||
};
|
||||
}
|
||||
__END__
|
||||
|
@ -96,7 +96,7 @@ Set some values
|
|||
|
||||
lemonldap-ng-cli is a command line interface to interact with Lemonldap::NG
|
||||
configuration. Commands are described in L<Lemonldap::NG::Manager::Cli>
|
||||
and L<Lemonldap::NG::Common::CLi>
|
||||
and L<Lemonldap::NG::Common::Cli>
|
||||
|
||||
=head2 Available commands
|
||||
|
||||
|
@ -122,7 +122,7 @@ and L<Lemonldap::NG::Common::CLi>
|
|||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Lemonldap::NG::Manager::Cli>, L<Lemonldap::NG::Common::CLi>
|
||||
L<Lemonldap::NG::Manager::Cli>, L<Lemonldap::NG::Common::Cli>
|
||||
L<http://lemonldap-ng.org/>
|
||||
|
||||
=head1 AUTHORS
|
||||
|
|
|
@ -44,6 +44,7 @@ sub run {
|
|||
: ""
|
||||
)
|
||||
);
|
||||
$req->data->{'noTry'} = 1;
|
||||
return $class->REDIRECT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,15 @@ sub logLevelInit {
|
|||
$logger = $class->localConfig->{userLogger} || $logger;
|
||||
eval "require $logger";
|
||||
die $@ if ($@);
|
||||
$class->userLogger( $logger->new( $class->localConfig, user => 1 ) );
|
||||
require Lemonldap::NG::Common::Logger::_Duplicate;
|
||||
$class->userLogger(
|
||||
Lemonldap::NG::Common::Logger::_Duplicate->new(
|
||||
$class->localConfig,
|
||||
user => 1,
|
||||
logger => $logger,
|
||||
dup => $class->logger
|
||||
)
|
||||
);
|
||||
$class->logger->debug("User logger $logger loaded");
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ sub _run {
|
|||
$self->routes( $self->authRoutes );
|
||||
$req->userData( $self->api->data );
|
||||
}
|
||||
elsif ( $res->[0] != 403 ) {
|
||||
elsif ( $res->[0] != 403 and not $req->data->{noTry} ) {
|
||||
|
||||
# Unset headers (handler adds a Location header)
|
||||
$self->logger->debug(
|
||||
|
|
|
@ -36,7 +36,7 @@ my $crypt = Lemonldap::NG::Common::Crypto->new('qwertyui');
|
|||
my $token = $crypt->encrypt(
|
||||
join ':', time,
|
||||
$sessionId, 'test1.example.com',
|
||||
'XFromVH=app1-auth.example.com', 'serviceHeader1=service_Header1',
|
||||
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId",
|
||||
'test2.example.com', '*.example.com'
|
||||
);
|
||||
|
||||
|
@ -51,11 +51,11 @@ ok(
|
|||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
count(2);
|
||||
|
||||
my @headers = grep { /service/ } @{ $res->[1] };
|
||||
my @values = grep { /\.example\.com/ } @{ $res->[1] };
|
||||
my @headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
my @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
|
||||
ok( @headers == 4, 'Found 4 service headers' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
ok( @values == 2, 'Found 2 service header values' )
|
||||
ok( @values == 4, 'Found 4 service header values' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
|
@ -73,11 +73,11 @@ ok(
|
|||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@values = grep { /\.example\.com/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
|
||||
ok( @headers == 4, 'Found 4 service headers' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
ok( @values == 2, 'Found 2 service header values' )
|
||||
ok( @values == 4, 'Found 4 service header values' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
|
@ -95,7 +95,7 @@ ok(
|
|||
ok( $res->[0] == 302, 'Code is 200' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
@ -114,11 +114,11 @@ ok(
|
|||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@values = grep { /\.example\.com/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
|
||||
ok( @headers == 4, 'Found 4 service headers' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
ok( @values == 2, 'Found 2 service header values' )
|
||||
ok( @values == 4, 'Found 4 service header values' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
|
@ -136,7 +136,7 @@ ok(
|
|||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
@ -152,7 +152,7 @@ ok(
|
|||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
@ -169,7 +169,7 @@ ok(
|
|||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service/ } @{ $res->[1] };
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
|
|
@ -785,7 +785,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'type' => 'bool'
|
||||
},
|
||||
'checkUserHiddenAttributes' => {
|
||||
'default' => '_loginHistory hGroups',
|
||||
'default' => '_loginHistory _session_id hGroups',
|
||||
'type' => 'text'
|
||||
},
|
||||
'checkUserIdRule' => {
|
||||
|
@ -2223,6 +2223,13 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 3,
|
||||
'type' => 'int'
|
||||
},
|
||||
'pdataDomain' => {
|
||||
'default' => '',
|
||||
'msgFail' => '__badDomainName__',
|
||||
'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])[.]?))?$/,
|
||||
'type' => 'text'
|
||||
},
|
||||
'persistentStorage' => {
|
||||
'type' => 'PerlModule'
|
||||
},
|
||||
|
|
|
@ -435,7 +435,7 @@ sub attributes {
|
|||
},
|
||||
checkUserHiddenAttributes => {
|
||||
type => 'text',
|
||||
default => '_loginHistory hGroups',
|
||||
default => '_loginHistory _session_id hGroups',
|
||||
documentation => 'Attributes to hide in CheckUser plugin',
|
||||
flags => 'p',
|
||||
},
|
||||
|
@ -980,6 +980,14 @@ sub attributes {
|
|||
documentation => 'DNS domain',
|
||||
flags => 'hp',
|
||||
},
|
||||
pdataDomain => {
|
||||
type => 'text',
|
||||
test => qr/^(?:$Regexp::Common::URI::RFC2396::hostname)?$/,
|
||||
msgFail => '__badDomainName__',
|
||||
default => '',
|
||||
documentation => 'pdata cookie DNS domain',
|
||||
flags => 'hp',
|
||||
},
|
||||
httpOnly => {
|
||||
default => 1,
|
||||
type => 'bool',
|
||||
|
|
|
@ -634,7 +634,7 @@ sub tests {
|
|||
checkMailResetSecurity => sub {
|
||||
return 1 unless ( $conf->{portalDisplayResetPassword} );
|
||||
return ( -1,
|
||||
'"passwordMailReset" plugin is enabled without CSRF Token neither Captcha required !!!'
|
||||
'"passwordMailReset" plugin is enabled without CSRF Token neither Captcha required'
|
||||
)
|
||||
unless ( $conf->{requireToken}
|
||||
or $conf->{captcha_mail_enabled} );
|
||||
|
@ -655,7 +655,7 @@ sub tests {
|
|||
return 1;
|
||||
},
|
||||
|
||||
# Warn if persistent storage is disabled with 2FA, History, OIDCConsents and Notifications
|
||||
# Warn if persistent storage is disabled with 2FA, History, OIDCConsents, Notifications or BruteForce protection
|
||||
persistentStorage => sub {
|
||||
return 1 unless ( $conf->{disablePersistentStorage} );
|
||||
return ( 1, "2FA enabled WITHOUT persistent session storage" )
|
||||
|
@ -671,6 +671,9 @@ sub tests {
|
|||
return ( 1,
|
||||
"Notifications enabled WITHOUT persistent session storage" )
|
||||
if ( $conf->{notification} );
|
||||
return ( 1,
|
||||
"BruteForceProtection plugin enabled WITHOUT persistent session storage" )
|
||||
if ( $conf->{bruteForceProtection} );
|
||||
|
||||
# Return
|
||||
return 1;
|
||||
|
|
|
@ -283,7 +283,7 @@ sub newNotification {
|
|||
|
||||
# Check if posted date > today
|
||||
unless ( $json->{date} ge $dDate ) {
|
||||
$self->logger->debug("Posted Date < today !!! ");
|
||||
$self->logger->debug("Posted Date < today");
|
||||
$json->{date} = $dDate;
|
||||
}
|
||||
$self->logger->debug("Notification Date = $json->{date}");
|
||||
|
|
|
@ -80,7 +80,7 @@ sub viewDiff {
|
|||
# Check Diff activation rule
|
||||
unless ( $self->diffRule->( $req, $req->{userData} ) ) {
|
||||
my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
|
||||
$self->userLogger->warn("$user tried to compare configurations!!!");
|
||||
$self->userLogger->warn("$user is not authorized to compare configurations");
|
||||
return $self->sendJSONresponse( $req, { 'value' => '_Hidden_' } );
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ sub viewKey {
|
|||
$self->logger->debug(
|
||||
" $req->{env}->{REQUEST_URI} -> URI FORBIDDEN");
|
||||
my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
|
||||
$self->userLogger->warn("$user tried to browse configurations!!!");
|
||||
$self->userLogger->warn("$user is not authorized to browse configurations");
|
||||
$self->rejectKey( $req, @args );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"اسم الفئة",
|
||||
"cda":"نطاقات متعددة",
|
||||
"contentSecurityPolicy":"السياسة الأمنية للمحتوى",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"استخدام القاعدة",
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"Category name",
|
||||
"cda":"Mehrere Domains",
|
||||
"contentSecurityPolicy":"Content security policy",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"Use rule",
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"Category name",
|
||||
"cda":"Multiple domains",
|
||||
"contentSecurityPolicy":"Content security policy",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"Use rule",
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"Nome della categoria",
|
||||
"cda":"Domini multipli",
|
||||
"contentSecurityPolicy":"Politica di protezione dei contenuti",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"Utilizza la regola",
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"Tên thể loại",
|
||||
"cda":"Nhiều tên miền",
|
||||
"contentSecurityPolicy":"Chính sách bảo mật nội dung",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"Quy tắc sử dụng",
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
"categoryName":"分类名称",
|
||||
"cda":"Multiple domains",
|
||||
"contentSecurityPolicy":"Content security policy",
|
||||
"contextSwitching":"Switch context anoter user",
|
||||
"contextSwitching":"Switch context another user",
|
||||
"contextSwitchingHiddenAttributes":"Hidden attributes",
|
||||
"contextSwitchingIdRule":"Identities use rule",
|
||||
"contextSwitchingRule":"Use rule",
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -55,7 +55,7 @@ my @notManagedAttributes = (
|
|||
'configStorage', 'status', 'localStorageOptions', 'localStorage',
|
||||
'max2FDevices', 'max2FDevicesNameLength', 'checkTime',
|
||||
'mySessionAuthorizedRWKeys', 'handlerInternalCache',
|
||||
'handlerServiceTokenTTL', 'impersonationPrefix'
|
||||
'handlerServiceTokenTTL', 'impersonationPrefix', 'pdataDomain',
|
||||
);
|
||||
|
||||
# Words used either as attribute name and node title
|
||||
|
|
|
@ -475,6 +475,7 @@ t/32-Auth-and-issuer-OIDC-authorization_code-with-authchoice.t
|
|||
t/32-Auth-and-issuer-OIDC-authorization_code-with-none-alg.t
|
||||
t/32-Auth-and-issuer-OIDC-authorization_code.t
|
||||
t/32-Auth-and-issuer-OIDC-hybrid.t
|
||||
t/32-Auth-and-issuer-OIDC-implicit-no-token.t
|
||||
t/32-Auth-and-issuer-OIDC-implicit.t
|
||||
t/32-Auth-and-issuer-OIDC-sorted.t
|
||||
t/32-CAS-10.t
|
||||
|
|
|
@ -195,7 +195,7 @@ sub run {
|
|||
"Found $removed EXPIRED 2F device(s) => Update persistent session"
|
||||
);
|
||||
$self->userLogger->notice(
|
||||
" -> $removed EXPIRED 2F device(s) removed");
|
||||
" -> $removed expired 2F device(s) removed");
|
||||
@$_2fDevices =
|
||||
map { $_->{type} =~ /\bEXPIRED\b/ ? () : $_ } @$_2fDevices;
|
||||
$self->p->updatePersistentSession( $req,
|
||||
|
|
|
@ -156,7 +156,7 @@ sub run {
|
|||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error("Max number of 2F devices is reached !!!");
|
||||
$self->userLogger->warn("Max number of 2F devices is reached");
|
||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||
400 );
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ sub run {
|
|||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Registered 2F Device(s) : $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error("Max number of 2F devices is reached !!!");
|
||||
$self->userLogger->warn("Max number of 2F devices is reached");
|
||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||
400 );
|
||||
}
|
||||
|
|
|
@ -103,8 +103,8 @@ sub run {
|
|||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error(
|
||||
"Max number of 2F devices is reached !!!");
|
||||
$self->userLogger->warn(
|
||||
"Max number of 2F devices is reached");
|
||||
return $self->p->sendHtml(
|
||||
$req, 'error',
|
||||
params => {
|
||||
|
|
|
@ -125,7 +125,7 @@ sub verify {
|
|||
|
||||
unless ( $session->{__ch} and $session->{__ch} eq $challenge ) {
|
||||
$self->userLogger->error(
|
||||
"U2F challenge changes by user !!! $session->{__ch} / $challenge"
|
||||
"U2F challenge changed by user: $session->{__ch} / $challenge"
|
||||
);
|
||||
$req->error(PE_BADCREDENTIALS);
|
||||
return $self->fail($req);
|
||||
|
|
|
@ -667,7 +667,8 @@ sub run {
|
|||
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsIDTokenSignAlg};
|
||||
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
|
||||
$at_hash = $self->createHash( $access_token, $hash_level );
|
||||
$at_hash = $self->createHash( $access_token, $hash_level )
|
||||
if $hash_level;
|
||||
}
|
||||
|
||||
# ID token payload
|
||||
|
@ -713,6 +714,20 @@ sub run {
|
|||
$id_token_payload_hash->{'acr'} = $id_token_acr
|
||||
if $id_token_acr;
|
||||
|
||||
if ( $response_type !~ /\btoken\b/ ) {
|
||||
|
||||
# No access_token
|
||||
# Claims must be set in id_token
|
||||
my $claims =
|
||||
$self->buildUserInfoResponse( $oidc_request->{'scope'},
|
||||
$rp, $req->id );
|
||||
|
||||
foreach ( keys %$claims ) {
|
||||
$id_token_payload_hash->{$_} = $claims->{$_}
|
||||
unless ( $_ eq "sub" );
|
||||
}
|
||||
}
|
||||
|
||||
# Create ID Token
|
||||
my $id_token =
|
||||
$self->createIDToken( $id_token_payload_hash, $rp );
|
||||
|
@ -768,7 +783,8 @@ sub run {
|
|||
$self->logger->debug("Generated code: $code");
|
||||
|
||||
# Compute hash to store in c_hash
|
||||
$c_hash = $self->createHash( $code, $hash_level );
|
||||
$c_hash = $self->createHash( $code, $hash_level )
|
||||
if $hash_level;
|
||||
|
||||
if ( $response_type =~ /\btoken\b/ ) {
|
||||
|
||||
|
@ -798,7 +814,8 @@ sub run {
|
|||
"Generated access token: $access_token");
|
||||
|
||||
# Compute hash to store in at_hash
|
||||
$at_hash = $self->createHash( $access_token, $hash_level );
|
||||
$at_hash = $self->createHash( $access_token, $hash_level )
|
||||
if $hash_level;
|
||||
}
|
||||
|
||||
if ( $response_type =~ /\bid_token\b/ ) {
|
||||
|
@ -837,6 +854,20 @@ sub run {
|
|||
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
|
||||
$id_token_payload_hash->{'c_hash'} = $c_hash if $c_hash;
|
||||
|
||||
if ( $response_type !~ /\btoken\b/ ) {
|
||||
|
||||
# No access_token
|
||||
# Claims must be set in id_token
|
||||
my $claims = $self->buildUserInfoResponse(
|
||||
$oidc_request->{'scope'},
|
||||
$rp, $req->id );
|
||||
|
||||
foreach ( keys %$claims ) {
|
||||
$id_token_payload_hash->{$_} = $claims->{$_}
|
||||
unless ( $_ eq "sub" );
|
||||
}
|
||||
}
|
||||
|
||||
# Create ID Token
|
||||
$id_token =
|
||||
$self->createIDToken( $id_token_payload_hash, $rp );
|
||||
|
@ -1094,7 +1125,8 @@ sub token {
|
|||
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsIDTokenSignAlg};
|
||||
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
|
||||
my $at_hash = $self->createHash( $access_token, $hash_level );
|
||||
my $at_hash = $self->createHash( $access_token, $hash_level )
|
||||
if $hash_level;
|
||||
|
||||
# ID token payload
|
||||
my $id_token_exp = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
|
@ -1459,7 +1491,16 @@ sub metadata {
|
|||
|
||||
my @acr = keys %{ $self->conf->{oidcServiceMetaDataAuthnContext} };
|
||||
|
||||
# Add a slash to path value if issuer has no trailing slash
|
||||
# List response types depending on allowed flows
|
||||
my $response_types = [];
|
||||
push( @$response_types, "code" )
|
||||
if $self->conf->{oidcServiceAllowAuthorizationCodeFlow};
|
||||
push( @$response_types, "id_token", "id_token token" )
|
||||
if $self->conf->{oidcServiceAllowImplicitFlow};
|
||||
push( @$response_types,
|
||||
"code id_token",
|
||||
"code token", "code id_token token" )
|
||||
if $self->conf->{oidcServiceAllowHybridFlow};
|
||||
|
||||
# Create OpenID configuration hash;
|
||||
return $self->p->sendJSONresponse(
|
||||
|
@ -1488,14 +1529,7 @@ sub metadata {
|
|||
|
||||
# Scopes
|
||||
scopes_supported => [qw/openid profile email address phone/],
|
||||
response_types_supported => [
|
||||
"code",
|
||||
"id_token",
|
||||
"id_token token",
|
||||
"code id_token",
|
||||
"code token",
|
||||
"code id_token token"
|
||||
],
|
||||
response_types_supported => $response_types,
|
||||
grant_types_supported => [qw/authorization_code implicit hybrid/],
|
||||
acr_values_supported => \@acr,
|
||||
subject_types_supported => ["public"],
|
||||
|
|
|
@ -185,7 +185,7 @@ sub getForm {
|
|||
split( /[;\|]/, $self->conf->{authChoiceModules}->{$_} );
|
||||
|
||||
unless ( $_choiceRules->{$_} ) {
|
||||
$self->logger->error("$_ has no rule !!!");
|
||||
$self->logger->error("$_ has no rule");
|
||||
$_choiceRules->{$_} = sub { 1 };
|
||||
}
|
||||
unless ( $_choiceRules->{$_}->( $req->env ) ) {
|
||||
|
|
|
@ -1341,7 +1341,7 @@ sub buildLogoutResponse {
|
|||
|
||||
# Create session_state parameter
|
||||
# @param session_id Session ID
|
||||
# @param client_id CLient ID
|
||||
# @param client_id Client ID
|
||||
# return String Session state
|
||||
sub createSessionState {
|
||||
my ( $self, $session_id, $client_id ) = @_;
|
||||
|
|
|
@ -356,6 +356,7 @@ sub reloadConf {
|
|||
unless ( $_[0]->pdata->{keepPdata} ) {
|
||||
$self->logger->debug('Cleaning pdata');
|
||||
$_[0]->pdata( {} );
|
||||
$self->userLogger->notice( $_[0]->user . ' connected' ) if $_[0]->user;
|
||||
}
|
||||
return PE_OK;
|
||||
};
|
||||
|
|
|
@ -264,6 +264,35 @@ plugins to use "aroundSub" in the same time.
|
|||
|
||||
=back
|
||||
|
||||
=head1 LOGGING
|
||||
|
||||
Logging is provided by $self->logger and $self->userLogger. The following rules
|
||||
must be applied:
|
||||
|
||||
=over
|
||||
|
||||
=item logger->debug: technical debugging messages
|
||||
|
||||
=item logger->info: simple technical information
|
||||
|
||||
=item logger->notice: technical information that could interest administrators
|
||||
|
||||
=item logger->warn: technical warning
|
||||
|
||||
=item logger->error: error that must be reported to administrator
|
||||
|
||||
=item userLogger->info: simple information about user's action
|
||||
|
||||
=item userLogger->notice: information that may be registered (auth success,...)
|
||||
|
||||
=item userLogger->warn: bad action of a user (auth failure). Auth/Combination
|
||||
transform it to "info" when another authentication scheme is available
|
||||
|
||||
=item userLogger->error: bad action of a user that must be reported, (even if
|
||||
another backend is available with Combination)
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<http://lemonldap-ng.org>
|
||||
|
|
|
@ -62,11 +62,13 @@ sub handler {
|
|||
(
|
||||
%{ $req->pdata }
|
||||
? ( value => uri_escape( JSON::to_json( $req->pdata ) ) )
|
||||
: (
|
||||
value => '',
|
||||
expires => 'Wed, 21 Oct 2015 00:00:00 GMT'
|
||||
)
|
||||
)
|
||||
: ( value => '', expires => 'Wed, 21 Oct 2015 00:00:00 GMT' )
|
||||
),
|
||||
(
|
||||
$self->conf->{pdataDomain}
|
||||
? ( domain => $self->conf->{pdataDomain}, )
|
||||
: ()
|
||||
),
|
||||
);
|
||||
push @{ $res->[1] }, 'Set-Cookie', $self->cookie(%v);
|
||||
}
|
||||
|
|
|
@ -32,20 +32,19 @@ sub run {
|
|||
my @lastFailedLoginEpoch = ();
|
||||
|
||||
# Auth_N-2 failed login epoch
|
||||
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin} ) {
|
||||
$countFailed = @{ $req->sessionInfo->{_loginHistory}->{failedLogin} };
|
||||
}
|
||||
|
||||
$countFailed = @{ $req->sessionInfo->{_loginHistory}->{failedLogin} }
|
||||
if ( $req->sessionInfo->{_loginHistory}->{failedLogin} );
|
||||
$self->logger->debug(" Number of failedLogin = $countFailed");
|
||||
|
||||
return PE_OK
|
||||
if ( $countFailed <= $self->conf->{bruteForceProtectionMaxFailed} );
|
||||
|
||||
foreach ( 0 .. $self->conf->{bruteForceProtectionMaxFailed} - 1 ) {
|
||||
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] ) {
|
||||
push @lastFailedLoginEpoch,
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]->{_utime};
|
||||
}
|
||||
push @lastFailedLoginEpoch,
|
||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]->{_utime}
|
||||
if ( $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] );
|
||||
}
|
||||
|
||||
$self->logger->debug("BruteForceProtection enabled");
|
||||
|
||||
# If Auth_N-MaxFailed older than MaxAge -> another try allowed
|
||||
|
@ -54,6 +53,7 @@ sub run {
|
|||
$lastFailedLoginEpoch[ $self->conf->{bruteForceProtectionMaxFailed} - 1 ]
|
||||
if $self->conf->{bruteForceProtectionMaxFailed};
|
||||
$self->logger->debug(" -> MaxAge = $MaxAge");
|
||||
|
||||
return PE_OK
|
||||
if ( $MaxAge > $self->conf->{bruteForceProtectionMaxAge} );
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ sub init {
|
|||
sub check {
|
||||
my ( $self, $req ) = @_;
|
||||
my ( $attrs, $array_attrs, $array_hdrs ) = ( {}, [], [] );
|
||||
my $msg = my $auth = my $compute = '';
|
||||
my $msg = my $auth = my $compute = '';
|
||||
my $authLevel = $req->userData->{authenticationLevel};
|
||||
my $authMode = $req->userData->{_auth};
|
||||
|
||||
# Check token
|
||||
if ( $self->ottRule->( $req, {} ) ) {
|
||||
|
@ -82,6 +84,7 @@ sub check {
|
|||
my $params = {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => "PE$msg",
|
||||
ALERTE => 'alert-warning',
|
||||
|
@ -110,6 +113,7 @@ sub check {
|
|||
params => {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => 'PE' . PE_MALFORMEDUSER,
|
||||
ALERTE => 'alert-warning',
|
||||
|
@ -123,19 +127,17 @@ sub check {
|
|||
);
|
||||
}
|
||||
|
||||
if ( $user eq $req->{user} or !$user ) {
|
||||
$self->logger->debug("checkUser requested for myself");
|
||||
$self->userLogger->notice("Return userData...");
|
||||
$self->userLogger->warn("Using spoofed SSO groups if exist!!!")
|
||||
if ( !$user or $user eq $req->{user} ) {
|
||||
$self->userLogger->info("checkUser requested for himself");
|
||||
$self->userLogger->info("Using spoofed SSO groups if exist")
|
||||
if ( $self->conf->{impersonationRule} );
|
||||
$attrs = $req->userData;
|
||||
$user = $req->{user};
|
||||
}
|
||||
else {
|
||||
$self->logger->debug("checkUser requested for $user");
|
||||
$self->userLogger->info("checkUser requested for $user");
|
||||
|
||||
# Try to retrieve session from sessions DB
|
||||
$self->userLogger->notice('Try to retrieve session from DB...');
|
||||
$self->logger->debug('Try to retrieve session from DB...');
|
||||
my $moduleOptions = $self->conf->{globalStorageOptions} || {};
|
||||
$moduleOptions->{backend} = $self->conf->{globalStorage};
|
||||
|
@ -153,9 +155,8 @@ sub check {
|
|||
}
|
||||
unless ( defined $attrs->{_session_id} ) {
|
||||
$req->{user} = $user;
|
||||
$self->userLogger->notice(
|
||||
"NO session found in DB. Compute userData...");
|
||||
$self->logger->debug("NO session found in DB. Compute userData...");
|
||||
$self->userLogger->info(
|
||||
"No session found in DB. Compute userData...");
|
||||
$attrs = $self->_userData($req);
|
||||
$compute = 1;
|
||||
}
|
||||
|
@ -171,7 +172,19 @@ sub check {
|
|||
$self->{conf}->{impersonationMergeSSOgroups} eq 1
|
||||
? 'checkUserMerged'
|
||||
: 'checkUser';
|
||||
$msg = 'checkUserComputeSession' if $compute;
|
||||
if ($compute) {
|
||||
$msg = 'checkUserComputeSession';
|
||||
$attrs->{authenticationLevel} = $authLevel;
|
||||
$attrs->{_auth} = $authMode;
|
||||
|
||||
if ( $self->conf->{impersonationRule} ) {
|
||||
$self->logger->debug("Map real attributes...");
|
||||
my %realAttrs = map {
|
||||
( "$self->{conf}->{impersonationPrefix}$_" => $attrs->{$_} )
|
||||
} keys %$attrs;
|
||||
$attrs = { %$attrs, %realAttrs };
|
||||
}
|
||||
}
|
||||
|
||||
# Create an array of hashes for template loop
|
||||
$self->logger->debug("Delete hidden or empty attributes");
|
||||
|
@ -209,18 +222,16 @@ sub check {
|
|||
$auth = $self->_authorization( $req, $url, $attrs );
|
||||
if ( $auth >= 0 ) {
|
||||
$auth = $auth ? "allowed" : "forbidden";
|
||||
$self->userLogger->notice(
|
||||
"checkUser -> $attrs->{ $self->{conf}->{whatToTrace} } is "
|
||||
. uc($auth)
|
||||
. " to access: $url" );
|
||||
$self->logger->debug(
|
||||
"checkUser: $attrs->{ $self->{conf}->{whatToTrace} } is "
|
||||
. "$auth to access to $url" );
|
||||
|
||||
# Return VirtualHost headers
|
||||
$array_hdrs = $self->_headers( $req, $url, $attrs );
|
||||
}
|
||||
else {
|
||||
$auth = 'VHnotFound';
|
||||
$self->userLogger->notice(
|
||||
"checkUser -> URL: $url has no configuration");
|
||||
$self->userLogger->info("checkUser: $url has no configuration");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,6 +243,7 @@ sub check {
|
|||
my $params = {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => $msg,
|
||||
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
|
||||
|
@ -262,8 +274,7 @@ sub display {
|
|||
my ( $attrs, $array_attrs ) = ( {}, [] );
|
||||
|
||||
$self->logger->debug("Display current session data...");
|
||||
$self->userLogger->notice("Retrieve session from Sessions database");
|
||||
$self->userLogger->warn("Using spoofed SSO groups if exist!!!")
|
||||
$self->userLogger->info("Using spoofed SSO groups if exist")
|
||||
if ( $self->conf->{impersonationRule} );
|
||||
$attrs = $req->userData;
|
||||
|
||||
|
@ -293,6 +304,7 @@ sub display {
|
|||
my $params = {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => (
|
||||
$self->{conf}->{impersonationMergeSSOgroups} ? 'checkUserMerged'
|
||||
|
|
|
@ -2,8 +2,15 @@ package Lemonldap::NG::Portal::Plugins::ContextSwitching;
|
|||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants
|
||||
qw( PE_OK PE_REDIRECT PE_BADCREDENTIALS PE_IMPERSONATION_SERVICE_NOT_ALLOWED PE_MALFORMEDUSER );
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_OK
|
||||
PE_ERROR
|
||||
PE_REDIRECT
|
||||
PE_MALFORMEDUSER
|
||||
PE_BADCREDENTIALS
|
||||
PE_SESSIONEXPIRED
|
||||
PE_IMPERSONATION_SERVICE_NOT_ALLOWED
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.6';
|
||||
|
||||
|
@ -62,12 +69,20 @@ sub init {
|
|||
|
||||
sub display {
|
||||
my ( $self, $req ) = @_;
|
||||
my $realSessionId =
|
||||
$req->userData->{"$self->{conf}->{impersonationPrefix}_session_id"};
|
||||
my $realSession;
|
||||
unless ( $realSession = $self->p->getApacheSession($realSessionId) ) {
|
||||
$self->userLogger->info(
|
||||
"ContextSwitching: session $realSessionId expired");
|
||||
return $self->p->do( $req, [ sub { PE_SESSIONEXPIRED } ] );
|
||||
}
|
||||
|
||||
# Check access rules
|
||||
unless ( $self->rule->( $req, $req->userData )
|
||||
|| $req->userData->{"$self->{conf}->{impersonationPrefix}_session_id"} )
|
||||
{
|
||||
$self->userLogger->error('Context switching service not authorized');
|
||||
$self->userLogger->warn('ContextSwitching service NOT authorized');
|
||||
return $self->p->do( $req,
|
||||
[ sub { PE_IMPERSONATION_SERVICE_NOT_ALLOWED } ] );
|
||||
}
|
||||
|
@ -75,13 +90,17 @@ sub display {
|
|||
if ( $req->userData->{"$self->{conf}->{impersonationPrefix}_session_id"} ) {
|
||||
$self->logger->debug('Request to stop ContextSwitching');
|
||||
if ( $self->conf->{contextSwitchingStopWithLogout} ) {
|
||||
$self->logger->debug('Send logout request');
|
||||
$self->userLogger->notice("Stop ContextSwitching for $req->{user}");
|
||||
$self->userLogger->info("Remove real session $realSession");
|
||||
$realSession->remove;
|
||||
return $self->p->do( $req,
|
||||
[ @{ $self->p->beforeLogout }, 'authLogout', 'deleteSession' ]
|
||||
);
|
||||
|
||||
}
|
||||
else {
|
||||
$req = $self->_abortImpersonation( $req, 0 );
|
||||
$req = $self->_abortImpersonation( $req, $req->{user},
|
||||
$realSession->data->{ $self->conf->{whatToTrace} }, 0 );
|
||||
$self->p->updateSession( $req, $req->userData );
|
||||
return $self->p->do( $req, [ sub { PE_REDIRECT } ] );
|
||||
}
|
||||
|
@ -91,6 +110,7 @@ sub display {
|
|||
my $params = {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => 'contextSwitching_ON',
|
||||
ALERTE => 'alert-danger',
|
||||
|
@ -109,11 +129,12 @@ sub display {
|
|||
sub run {
|
||||
my ( $self, $req ) = @_;
|
||||
my $statut = PE_OK;
|
||||
my $realId = $req->{user};
|
||||
my $spoofId = $req->param('spoofId') || ''; # ContextSwitching required ?
|
||||
|
||||
# Check activation rule
|
||||
unless ( $self->rule->( $req, $req->userData ) ) {
|
||||
$self->userLogger->warn('Context switching service not authorized');
|
||||
$self->userLogger->warn('ContextSwitching service NOT authorized');
|
||||
$spoofId = '';
|
||||
return $self->p->do( $req,
|
||||
[ sub { PE_IMPERSONATION_SERVICE_NOT_ALLOWED } ] );
|
||||
|
@ -125,12 +146,12 @@ sub run {
|
|||
unless ( $spoofId =~ /$self->{conf}->{userControl}/o ) {
|
||||
$self->userLogger->warn('Malformed spoofed Id');
|
||||
$self->logger->debug(
|
||||
"Context switching tried with spoofed Id: $spoofId");
|
||||
"ContextSwitching tried with spoofed Id: $spoofId");
|
||||
return $self->p->do( $req, [ sub { PE_MALFORMEDUSER } ] );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->logger->debug("No context switching required");
|
||||
$self->logger->debug("contextSwitching NOT required");
|
||||
$req->urldc( $self->conf->{portal} );
|
||||
return $self->p->do( $req, [ sub { PE_OK } ] );
|
||||
}
|
||||
|
@ -148,17 +169,28 @@ sub run {
|
|||
|
||||
# Main session
|
||||
$self->p->updateSession( $req, $req->sessionInfo );
|
||||
$self->userLogger->notice(
|
||||
"ContextSwitching: Update $realId session with $spoofId session data");
|
||||
|
||||
return $self->p->do( $req, [ sub { $statut } ] );
|
||||
}
|
||||
|
||||
sub _switchContext {
|
||||
my ( $self, $req, $spoofId ) = @_;
|
||||
my $realSessionId = $req->userData->{_session_id};
|
||||
my $realId = $req->{user};
|
||||
my $raz = 0;
|
||||
$req->{user} = $spoofId;
|
||||
|
||||
# Search user in database & create session
|
||||
$req->steps( [ 'getUser', $self->p->sessionData, 'buildCookie' ] );
|
||||
$req->steps( [
|
||||
'getUser', 'setAuthSessionInfo',
|
||||
'setSessionInfo', 'setMacros',
|
||||
'setGroups', 'setPersistentSessionInfo',
|
||||
'setLocalGroups', 'store',
|
||||
'buildCookie'
|
||||
]
|
||||
);
|
||||
if ( my $error = $self->p->process($req) ) {
|
||||
if ( $error == PE_BADCREDENTIALS ) {
|
||||
$self->userLogger->warn(
|
||||
|
@ -178,37 +210,52 @@ sub _switchContext {
|
|||
. $req->{user}
|
||||
. ")" );
|
||||
$self->logger->debug('Identity NOT authorized');
|
||||
$req->error(PE_MALFORMEDUSER); # Hide error to preserve protected Id
|
||||
$req->error(PE_MALFORMEDUSER); # Catch error to preserve protected Id
|
||||
$raz = 1;
|
||||
}
|
||||
|
||||
$req->sessionInfo->{"$self->{conf}->{impersonationPrefix}_session_id"} =
|
||||
$realSessionId;
|
||||
$self->userLogger->notice(
|
||||
"Start ContextSwitching: $realId becomes $spoofId ")
|
||||
unless $raz;
|
||||
|
||||
return $raz ? $self->_abortImpersonation( $req, 1 ) : $req;
|
||||
return $raz
|
||||
? $self->_abortImpersonation( $req, $spoofId, $realId, 1 )
|
||||
: $req;
|
||||
}
|
||||
|
||||
sub _abortImpersonation {
|
||||
my ( $self, $req, $abort ) = @_;
|
||||
my ( $self, $req, $spoofId, $realId, $abort ) = @_;
|
||||
my $type = $abort ? 'sessionInfo' : 'userData';
|
||||
my $realSessionId =
|
||||
$req->{$type}->{"$self->{conf}->{impersonationPrefix}_session_id"};
|
||||
my $session = $self->p->getApacheSession($realSessionId)->data;
|
||||
my $session;
|
||||
unless ( $session = $self->p->getApacheSession($realSessionId) ) {
|
||||
$self->userLogger->info("Session $session expired");
|
||||
return $req->error(PE_SESSIONEXPIRED);
|
||||
}
|
||||
|
||||
if ($abort) {
|
||||
$self->logger->debug('ABORT ContextSwitching');
|
||||
$self->userLogger->notice('ABORT ContextSwitching');
|
||||
$self->p->updateSession( $req, { '_session_kind' => 'SPOOF' } );
|
||||
$self->userLogger->notice(
|
||||
"Abort ContextSwitching: $spoofId by $realId");
|
||||
if ( my $abortSession = $self->p->getApacheSession( $req->id ) ) {
|
||||
$abortSession->remove;
|
||||
}
|
||||
else {
|
||||
$self->userLogger->info(
|
||||
"ContextSwitching: session " . $req->id . " expired" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->logger->debug('STOP ContextSwitching');
|
||||
$self->userLogger->notice('STOP ContextSwitching');
|
||||
$self->userLogger->notice(
|
||||
"Stop ContextSwitching for $realId with uid $spoofId");
|
||||
$self->p->deleteSession($req);
|
||||
}
|
||||
|
||||
# Restore real session
|
||||
$req->{$type} = {%$session};
|
||||
$req->{user} = $session->{_user};
|
||||
$req->{$type} = { %{ $session->data } };
|
||||
$req->{user} = $session->data->{_user};
|
||||
$req->urldc( $self->conf->{portal} );
|
||||
$req->id($realSessionId);
|
||||
$self->p->buildCookie($req);
|
||||
|
|
|
@ -119,8 +119,7 @@ sub run {
|
|||
$self->logger->debug("Populating spoof session...");
|
||||
foreach (qw (_auth _userDB)) {
|
||||
$self->logger->debug("Processing $_...");
|
||||
my $spk = "$self->{conf}->{impersonationPrefix}$_";
|
||||
$spoofSession->{$_} = $realSession->{$spk};
|
||||
$spoofSession->{$_} = $realSession->{"$self->{conf}->{impersonationPrefix}$_"};
|
||||
}
|
||||
|
||||
# Merging SSO Groups and hGroups & dedup
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Launch SSL request
|
||||
|
||||
tryssl = () ->
|
||||
console.log 'Call URL -> ', window.datas.sslHost
|
||||
$.ajax window.datas.sslHost,
|
||||
host = window.datas.sslHost
|
||||
path = window.location.pathname
|
||||
url = "#{host}#{path}"
|
||||
console.log 'Call URL -> ', url
|
||||
$.ajax url,
|
||||
dataType: 'jsonp'
|
||||
# PE_BADCREDENTIALS
|
||||
statusCode:
|
||||
|
@ -20,5 +23,6 @@ tryssl = () ->
|
|||
$('#lform').submit()
|
||||
console.log 'Error'
|
||||
false
|
||||
|
||||
$(document).ready ->
|
||||
$('.sslclick').on 'click', tryssl
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Launch SSL request
|
||||
|
||||
tryssl = () ->
|
||||
console.log 'Call URL -> ', window.datas.sslHost
|
||||
$.ajax window.datas.sslHost,
|
||||
host = window.datas.sslHost
|
||||
path = window.location.pathname
|
||||
url = "#{host}#{path}"
|
||||
console.log 'Call URL -> ', url
|
||||
$.ajax url,
|
||||
dataType: 'jsonp'
|
||||
# PE_BADCREDENTIALS
|
||||
statusCode:
|
||||
|
@ -20,5 +23,6 @@ tryssl = () ->
|
|||
$('#lformSSL').submit()
|
||||
console.log 'Error'
|
||||
false
|
||||
|
||||
$(document).ready ->
|
||||
$('.sslclick').on 'click', tryssl
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// Generated by CoffeeScript 1.10.0
|
||||
// Generated by CoffeeScript 1.12.7
|
||||
(function() {
|
||||
var tryssl;
|
||||
|
||||
tryssl = function() {
|
||||
console.log('Call URL -> ', window.datas.sslHost);
|
||||
$.ajax(window.datas.sslHost, {
|
||||
var host, path, url;
|
||||
host = window.datas.sslHost;
|
||||
path = window.location.pathname;
|
||||
url = "" + host + path;
|
||||
console.log('Call URL -> ', url);
|
||||
$.ajax(url, {
|
||||
dataType: 'jsonp',
|
||||
statusCode: {
|
||||
401: function() {
|
||||
|
|
|
@ -1 +1 @@
|
|||
(function(){var tryssl;tryssl=function(){console.log("Call URL -> ",window.datas.sslHost);$.ajax(window.datas.sslHost,{dataType:"jsonp",statusCode:{401:function(){$("#lform").submit();return console.log("Error code 401")}},success:function(data){$("#lform").submit();return console.log("Success -> ",data)},error:function(){$("#lform").submit();return console.log("Error")}});return false};$(document).ready(function(){return $(".sslclick").on("click",tryssl)})}).call(this);
|
||||
(function(){var tryssl;tryssl=function(){var host,path,url;host=window.datas.sslHost;path=window.location.pathname;url=""+host+path;console.log("Call URL -> ",url);$.ajax(url,{dataType:"jsonp",statusCode:{401:function(){$("#lform").submit();return console.log("Error code 401")}},success:function(data){$("#lform").submit();return console.log("Success -> ",data)},error:function(){$("#lform").submit();return console.log("Error")}});return false};$(document).ready(function(){return $(".sslclick").on("click",tryssl)})}).call(this);
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// Generated by CoffeeScript 1.10.0
|
||||
// Generated by CoffeeScript 1.12.7
|
||||
(function() {
|
||||
var tryssl;
|
||||
|
||||
tryssl = function() {
|
||||
console.log('Call URL -> ', window.datas.sslHost);
|
||||
$.ajax(window.datas.sslHost, {
|
||||
var host, path, url;
|
||||
host = window.datas.sslHost;
|
||||
path = window.location.pathname;
|
||||
url = "" + host + path;
|
||||
console.log('Call URL -> ', url);
|
||||
$.ajax(url, {
|
||||
dataType: 'jsonp',
|
||||
statusCode: {
|
||||
401: function() {
|
||||
|
|
|
@ -1 +1 @@
|
|||
(function(){var tryssl;tryssl=function(){console.log("Call URL -> ",window.datas.sslHost);$.ajax(window.datas.sslHost,{dataType:"jsonp",statusCode:{401:function(){$("#lformSSL").submit();return console.log("Error code 401")}},success:function(data){$("#lformSSL").submit();return console.log("Success -> ",data)},error:function(){$("#lformSSL").submit();return console.log("Error")}});return false};$(document).ready(function(){return $(".sslclick").on("click",tryssl)})}).call(this);
|
||||
(function(){var tryssl;tryssl=function(){var host,path,url;host=window.datas.sslHost;path=window.location.pathname;url=""+host+path;console.log("Call URL -> ",url);$.ajax(url,{dataType:"jsonp",statusCode:{401:function(){$("#lformSSL").submit();return console.log("Error code 401")}},success:function(data){$("#lformSSL").submit();return console.log("Success -> ",data)},error:function(){$("#lformSSL").submit();return console.log("Error")}});return false};$(document).ready(function(){return $(".sslclick").on("click",tryssl)})}).call(this);
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"غير كلمة المرور الخاصة بك",
|
||||
"checkLastLogins":"تحقق من آخر تسجيلات دخول الخاصة بي",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Choose your second factor",
|
||||
"chooseApp":"اختر أحد التطبيقات المسموح لك بالدخول إليها",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Ändere dein Passwort",
|
||||
"checkLastLogins":"Überprüfe meine letzten Logins",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Wählen deinen Ihren zweiten Faktor",
|
||||
"chooseApp":"Wählen Sie eine Anwendung aus, auf die du zugreifen darfst",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Change your password",
|
||||
"checkLastLogins":"Check my last logins",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Choose your second factor",
|
||||
"chooseApp":"Choose an application your are allowed to access to",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Change your password",
|
||||
"checkLastLogins":"Check my last logins",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Choose your second factor",
|
||||
"chooseApp":"Choose an application your are allowed to access to",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Vaihda salasanasi",
|
||||
"checkLastLogins":"Tarkista viimeiset kirjautumiseni",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Choose your second factor",
|
||||
"chooseApp":"Choose an application your are allowed to access to",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Changez votre mot de passe",
|
||||
"checkLastLogins":"Voir mes dernières connexions",
|
||||
"checkUser":"Vérifier le profil SSO d'un utilisateur",
|
||||
"checkUserMerged":"Vérifier le profil SSO d'un utilisateur. Les groupes SSO réels et usurpés sont fusionnés !!!",
|
||||
"checkUserMerged":"Vérifier le profil SSO d'un utilisateur. Tout ou partie des groupes SSO réels et simulés sont fusionnés !!!",
|
||||
"checkUserComputeSession":"Données de session issues d'une évaluation !!!",
|
||||
"choose2f":"Choisissez votre second facteur",
|
||||
"chooseApp":"Choisissez une application à laquelle vous êtes autorisé à accéder",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Cambia la tua password",
|
||||
"checkLastLogins":"Controllare i miei ultimi accessi",
|
||||
"checkUser":"Controlla il profilo SSO dell'utente",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Scegli il tuo secondo fattore",
|
||||
"chooseApp":"Scegli un'applicazione alla quale ti è consentito l'accesso",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"changePwd":"Change your password",
|
||||
"checkLastLogins":"Check my last logins",
|
||||
"checkUser":"Check user SSO profile",
|
||||
"checkUserMerged":"Check user SSO profile. Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!!!",
|
||||
"checkUserComputeSession":"Computed session data!!!",
|
||||
"choose2f":"Choose your second factor",
|
||||
"chooseApp":"Choose an application your are allowed to access to",
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
<TMPL_INCLUDE NAME="mail_header.tpl">
|
||||
|
||||
<p>
|
||||
<span>
|
||||
<span trspan="hello">Hello</span> $cn,<br />
|
||||
<br />
|
||||
<span trspan="yourLoginCodeIs">Your login code is</span>
|
||||
<b>$code</b>
|
||||
</p>
|
||||
<b>$code</b><br/>
|
||||
</span>
|
||||
|
||||
<TMPL_INCLUDE NAME="mail_footer.tpl">
|
||||
|
||||
|
|
|
@ -331,6 +331,7 @@ sub issuer {
|
|||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'idp.com',
|
||||
pdataDomain => 'idp.com',
|
||||
portal => 'http://auth.idp.com',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
|
@ -464,6 +465,7 @@ sub sp {
|
|||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'sp.com',
|
||||
pdataDomain => 'sp.com',
|
||||
portal => 'http://auth.sp.com',
|
||||
authentication => 'SAML',
|
||||
userDB => 'Same',
|
||||
|
|
|
@ -0,0 +1,347 @@
|
|||
use lib 'inc';
|
||||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use JSON;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
use MIME::Base64 qw/encode_base64 decode_base64 decode_base64url/;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
}
|
||||
|
||||
my $debug = 'error';
|
||||
my ( $op, $rp, $res );
|
||||
my %handlerOR = ( op => [], rp => [] );
|
||||
|
||||
LWP::Protocol::PSGI->register(
|
||||
sub {
|
||||
my $req = Plack::Request->new(@_);
|
||||
ok( $req->uri =~ m#http://auth.((?:o|r)p).com(.*)#, ' REST request' );
|
||||
my $host = $1;
|
||||
my $url = $2;
|
||||
my ( $res, $client );
|
||||
count(1);
|
||||
if ( $host eq 'op' ) {
|
||||
pass(" Request from RP to OP, endpoint $url");
|
||||
$client = $op;
|
||||
}
|
||||
elsif ( $host eq 'rp' ) {
|
||||
pass(' Request from OP to RP');
|
||||
$client = $rp;
|
||||
}
|
||||
else {
|
||||
fail(' Aborting REST request (external)');
|
||||
return [ 500, [], [] ];
|
||||
}
|
||||
if ( $req->method =~ /^post$/i ) {
|
||||
my $s = $req->content;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
$url, IO::String->new($s),
|
||||
length => length($s),
|
||||
type => $req->header('Content-Type'),
|
||||
),
|
||||
' Execute request'
|
||||
);
|
||||
}
|
||||
else {
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
$url,
|
||||
custom => {
|
||||
HTTP_AUTHORIZATION => $req->header('Authorization'),
|
||||
}
|
||||
),
|
||||
' Execute request'
|
||||
);
|
||||
}
|
||||
ok( $res->[0] == 200, ' Response is 200' );
|
||||
ok( getHeader( $res, 'Content-Type' ) =~ m#^application/json#,
|
||||
' Content is JSON' )
|
||||
or explain( $res->[1], 'Content-Type => application/json' );
|
||||
count(4);
|
||||
return $res;
|
||||
}
|
||||
);
|
||||
|
||||
# Initialization
|
||||
ok( $op = op(), 'OP portal' );
|
||||
|
||||
ok( $res = $op->_get('/oauth2/jwks'), 'Get JWKS, endpoint /oauth2/jwks' );
|
||||
expectOK($res);
|
||||
my $jwks = $res->[2]->[0];
|
||||
|
||||
ok(
|
||||
$res = $op->_get('/.well-known/openid-configuration'),
|
||||
'Get metadata, endpoint /.well-known/openid-configuration'
|
||||
);
|
||||
expectOK($res);
|
||||
my $metadata = $res->[2]->[0];
|
||||
count(3);
|
||||
|
||||
switch ('rp');
|
||||
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
|
||||
ok( $rp = rp( $jwks, $metadata ), 'RP portal' );
|
||||
count(1);
|
||||
|
||||
# Query RP for auth
|
||||
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' );
|
||||
count(1);
|
||||
my ( $url, $query ) =
|
||||
expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# );
|
||||
|
||||
# Rewrite response_type to use implicit
|
||||
$query =~ s/response_type=code/response_type=id_token/;
|
||||
|
||||
# Push request to OP
|
||||
switch ('op');
|
||||
ok( $res = $op->_get( $url, query => $query, accept => 'text/html' ),
|
||||
"Push request to OP, endpoint $url" );
|
||||
count(1);
|
||||
expectOK($res);
|
||||
|
||||
# Try to authenticate to IdP
|
||||
$query = "user=dwho&password=dwho&$query&nonce=qwerty";
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
$url,
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
),
|
||||
"Post authentication, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
my $idpId = expectCookie($res);
|
||||
|
||||
## Consent required
|
||||
my ( $host, $tmp );
|
||||
( $host, $tmp, $query ) = expectForm( $res, '#', undef, 'confirm' );
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
$url,
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId",
|
||||
length => length($query),
|
||||
),
|
||||
"Post confirmation, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
|
||||
($query) = expectRedirection( $res,
|
||||
qr#^http://auth.rp.com/?\?openidconnectcallback=1\#(.*)$# );
|
||||
|
||||
my %prms = map { split /=/, $_ } split /&/, $query;
|
||||
ok( $prms{id_token}, ' id_token found' );
|
||||
ok( !$prms{token_type}, ' token_type must be missing' );
|
||||
ok( $prms{session_state}, ' session_state found' );
|
||||
ok( !$prms{access_token}, ' access_token must be missing' );
|
||||
ok( $prms{state}, ' state found' );
|
||||
count(5);
|
||||
|
||||
# Check attributes in ID Token
|
||||
my ( $id_token_header, $id_token_payload, $id_token_signature ) =
|
||||
split( /\./, $prms{id_token} );
|
||||
my $id_token_decoded = decode_json( decode_base64url($id_token_payload) );
|
||||
ok( $id_token_decoded->{sub} eq "dwho", 'Check sub value' );
|
||||
ok( $id_token_decoded->{name} eq "Doctor Who", 'Check name value' );
|
||||
count(2);
|
||||
|
||||
$op->logout($idpId);
|
||||
|
||||
# Query RP for auth
|
||||
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' );
|
||||
count(1);
|
||||
( $url, $query ) =
|
||||
expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# );
|
||||
|
||||
# Rewrite response_type to use implicit
|
||||
$query =~ s/response_type=code/response_type=id_token%20token/;
|
||||
|
||||
# Push request to OP
|
||||
ok( $res = $op->_get( $url, query => $query, accept => 'text/html' ),
|
||||
"Push request to OP, endpoint $url" );
|
||||
count(1);
|
||||
expectOK($res);
|
||||
|
||||
# Try to authenticate to IdP
|
||||
$query = "user=dwho&password=dwho&$query&nonce=qwerty";
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
$url,
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
),
|
||||
"Post authentication, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
$idpId = expectCookie($res);
|
||||
|
||||
# expectRedirection( $res,
|
||||
# qr#^http://auth.rp.com/?\?openidconnectcallback=1\#(.*)$# );
|
||||
|
||||
# ok(
|
||||
# $res = $op->_post(
|
||||
# $url,
|
||||
# IO::String->new($query),
|
||||
# accept => 'text/html',
|
||||
# cookie => "lemonldap=$idpId",
|
||||
# length => length($query),
|
||||
# ),
|
||||
# "Post confirmation, endpoint $url"
|
||||
# );
|
||||
#count(1);
|
||||
|
||||
#print STDERR Dumper($query);
|
||||
$op->logout($idpId);
|
||||
clean_sessions();
|
||||
done_testing( count() );
|
||||
|
||||
sub switch {
|
||||
my $type = shift;
|
||||
pass( '==> Switching to ' . uc($type) . ' <==' );
|
||||
count(1);
|
||||
@Lemonldap::NG::Handler::Main::_onReload = @{
|
||||
$handlerOR{$type};
|
||||
};
|
||||
}
|
||||
|
||||
sub op {
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'idp.com',
|
||||
portal => 'http://auth.op.com',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
issuerDBOpenIDConnectActivation => "1",
|
||||
oidcRPMetaDataExportedVars => {
|
||||
rp => {
|
||||
email => "mail",
|
||||
family_name => "cn",
|
||||
name => "cn"
|
||||
}
|
||||
},
|
||||
oidcServiceMetaDataIssuer => "http://auth.op.com",
|
||||
oidcServiceMetaDataAuthorizeURI => "authorize",
|
||||
oidcServiceMetaDataCheckSessionURI => "checksession",
|
||||
oidcServiceMetaDataJWKSURI => "jwks",
|
||||
oidcServiceMetaDataEndSessionURI => "logout",
|
||||
oidcServiceMetaDataRegistrationURI => "register",
|
||||
oidcServiceMetaDataTokenURI => "token",
|
||||
oidcServiceMetaDataUserInfoURI => "userinfo",
|
||||
oidcServiceAllowHybridFlow => 1,
|
||||
oidcServiceAllowImplicitFlow => 1,
|
||||
oidcServiceAllowDynamicRegistration => 1,
|
||||
oidcServiceAllowAuthorizationCodeFlow => 1,
|
||||
oidcRPMetaDataOptions => {
|
||||
rp => {
|
||||
oidcRPMetaDataOptionsDisplayName => "RP",
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsClientID => "rpid",
|
||||
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
|
||||
oidcRPMetaDataOptionsBypassConsent => 0,
|
||||
oidcRPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcRPMetaDataOptionsUserIDAttr => "",
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 3600
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataOptions => {},
|
||||
oidcOPMetaDataJSON => {},
|
||||
oidcOPMetaDataJWKS => {},
|
||||
oidcServiceMetaDataAuthnContext => {
|
||||
'loa-4' => 4,
|
||||
'loa-1' => 1,
|
||||
'loa-5' => 5,
|
||||
'loa-2' => 2,
|
||||
'loa-3' => 3
|
||||
},
|
||||
oidcServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAs2jsmIoFuWzMkilJaA8//5/T30cnuzX9GImXUrFR2k9EKTMt
|
||||
GMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8TrH1PHFmHpy8/qE/S5OhinIpIi7eb
|
||||
ABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH1caJ8lmiERFj7IvNKqEhzAk0pyDr
|
||||
8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdykX5rx0h5SslG3jVWYhZ/SOb2aIzO
|
||||
r0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO8093X5VVk9vaPRg0zxJQ0Do0YLyzkR
|
||||
isSAIFb0tdKuDnjRGK6y/N2j6At2HjkxntbtGQIDAQABAoIBADYq6LxJd977LWy3
|
||||
0HT9nboFPIf+SM2qSEc/S5Po+6ipJBA4ZlZCMf7dHa6znet1TDpqA9iQ4YcqIHMH
|
||||
6xZNQ7hhgSAzG9TrXBHqP+djDlrrGWotvjuy0IfS9ixFnnLWjrtAH9afRWLuG+a/
|
||||
NHNC1M6DiiTE0TzL/lpt/zzut3CNmWzH+t19X6UsxUg95AzooEeewEYkv25eumWD
|
||||
mfQZfCtSlIw1sp/QwxeJa/6LJw7KcPZ1wXUm1BN0b9eiKt9Cmni1MS7elgpZlgGt
|
||||
xtfGTZtNLQ7bgDiM8MHzUfPBhbceNSIx2BeCuOCs/7eaqgpyYHBbAbuBQex2H61l
|
||||
Lcc3Tz0CgYEA4Kx/avpCPxnvsJ+nHVQm5d/WERuDxk4vH1DNuCYBvXTdVCGADf6a
|
||||
F5No1JcTH3nPTyPWazOyGdT9LcsEJicLyD8vCM6hBFstG4XjqcAuqG/9DRsElpHQ
|
||||
yi1zc5DNP7Vxmiz9wII0Mjy0abYKtxnXh9YK4a9g6wrcTpvShhIcIb8CgYEAzGzG
|
||||
lorVCfX9jXULIznnR/uuP5aSnTEsn0xJeqTlbW0RFWLdj8aIL1peirh1X89HroB9
|
||||
GeTNqEJXD+3CVL2cx+BRggMDUmEz4hR59meZCDGUyT5fex4LIsceb/ESUl2jo6Sw
|
||||
HXwWbN67rQ55N4oiOcOppsGxzOHkl5HdExKidycCgYEAr5Qev2tz+fw65LzfzHvH
|
||||
Kj4S/KuT/5V6He731cFd+sEpdmX3vPgLVAFPG1Q1DZQT/rTzDDQKK0XX1cGiLG63
|
||||
NnaqOye/jbfzOF8Z277kt51NFMDYhRLPKDD82IOA4xjY/rPKWndmcxwdob8yAIWh
|
||||
efY76sMz6ntCT+xWSZA9i+ECgYBWMZM2TIlxLsBfEbfFfZewOUWKWEGvd9l5vV/K
|
||||
D5cRIYivfMUw5yPq2267jPUolayCvniBH4E7beVpuPVUZ7KgcEvNxtlytbt7muil
|
||||
5Z6X3tf+VodJ0Swe2NhTmNEB26uwxzLe68BE3VFCsbSYn2y48HAq+MawPZr18bHG
|
||||
ZfgMxwKBgHHRg6HYqF5Pegzk1746uH2G+OoCovk5ylGGYzcH2ghWTK4agCHfBcDt
|
||||
EYqYAev/l82wi+OZ5O8U+qjFUpT1CVeUJdDs0o5u19v0UJjunU1cwh9jsxBZAWLy
|
||||
PAGd6SWf4S3uQCTw6dLeMna25YIlPh5qPA6I/pAahe8e3nSu2ckl
|
||||
-----END RSA PRIVATE KEY-----
|
||||
",
|
||||
oidcServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2jsmIoFuWzMkilJaA8/
|
||||
/5/T30cnuzX9GImXUrFR2k9EKTMtGMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8T
|
||||
rH1PHFmHpy8/qE/S5OhinIpIi7ebABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH
|
||||
1caJ8lmiERFj7IvNKqEhzAk0pyDr8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdy
|
||||
kX5rx0h5SslG3jVWYhZ/SOb2aIzOr0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO80
|
||||
93X5VVk9vaPRg0zxJQ0Do0YLyzkRisSAIFb0tdKuDnjRGK6y/N2j6At2Hjkxntbt
|
||||
GQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
",
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub rp {
|
||||
my ( $jwks, $metadata ) = @_;
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'rp.com',
|
||||
portal => 'http://auth.rp.com',
|
||||
authentication => 'OpenIDConnect',
|
||||
userDB => 'Same',
|
||||
oidcOPMetaDataExportedVars => {
|
||||
op => {
|
||||
cn => "name",
|
||||
uid => "sub",
|
||||
sn => "family_name",
|
||||
mail => "email"
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataOptions => {
|
||||
op => {
|
||||
oidcOPMetaDataOptionsCheckJWTSignature => 1,
|
||||
oidcOPMetaDataOptionsJWKSTimeout => 0,
|
||||
oidcOPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcOPMetaDataOptionsScope => "openid profile",
|
||||
oidcOPMetaDataOptionsStoreIDToken => 0,
|
||||
oidcOPMetaDataOptionsDisplay => "",
|
||||
oidcOPMetaDataOptionsClientID => "rpid",
|
||||
oidcOPMetaDataOptionsConfigurationURI =>
|
||||
"https://auth.op.com/.well-known/openid-configuration"
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataJWKS => {
|
||||
op => $jwks,
|
||||
},
|
||||
oidcOPMetaDataJSON => {
|
||||
op => $metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
|
@ -2,9 +2,10 @@ use lib 'inc';
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use JSON;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
use MIME::Base64;
|
||||
use MIME::Base64 qw/encode_base64 decode_base64 decode_base64url/;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
|
@ -142,6 +143,14 @@ ok( $prms{access_token}, ' access_token found' );
|
|||
ok( $prms{state}, ' state found' );
|
||||
count(5);
|
||||
|
||||
# Check attributes in ID Token
|
||||
my ( $id_token_header, $id_token_payload, $id_token_signature ) =
|
||||
split( /\./, $prms{id_token} );
|
||||
my $id_token_decoded = decode_json( decode_base64url($id_token_payload) );
|
||||
ok( $id_token_decoded->{sub} eq "dwho", 'Check sub value' );
|
||||
ok( !$id_token_decoded->{name}, 'Claim name must not be in ID token' );
|
||||
count(2);
|
||||
|
||||
$op->logout($idpId);
|
||||
|
||||
# Query RP for auth
|
||||
|
|
|
@ -9,17 +9,18 @@ BEGIN {
|
|||
my $res;
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
loginHistoryEnabled => 0,
|
||||
brutForceProtection => 0,
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
requireToken => 0,
|
||||
securedCookie => 2,
|
||||
https => 0,
|
||||
checkUser => 1,
|
||||
handlerInternalCache => 0,
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
loginHistoryEnabled => 0,
|
||||
brutForceProtection => 0,
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
requireToken => 0,
|
||||
securedCookie => 2,
|
||||
https => 0,
|
||||
checkUser => 1,
|
||||
handlerInternalCache => 0,
|
||||
checkUserHiddenAttributes => '_loginHistory hGroups',
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package LocalApp;
|
||||
|
||||
use Mouse;
|
||||
|
||||
extends 'Lemonldap::NG::Handler::PSGI::Try';
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
$self->SUPER::init($_[1]) or return 0;
|
||||
$self->addAuthRouteWithRedirect('*' => 'my');
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub my {
|
||||
return [200,[],['OK']];
|
||||
}
|
||||
|
||||
package main;
|
||||
|
||||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_FIRSTACCESS
|
||||
);
|
||||
|
||||
require 't/test-lib.pm';
|
||||
|
||||
my $res;
|
||||
my %handlerOR = ( portal => [], app => [] );
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
useSafeJail => 1,
|
||||
cda => 1,
|
||||
logger => 'Lemonldap::NG::Common::Logger::Std',
|
||||
}
|
||||
}
|
||||
);
|
||||
$handlerOR{portal} = \@Lemonldap::NG::Handler::Main::_onReload;
|
||||
|
||||
# CDA with unauthentified user
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
query => 'url=aHR0cDovL3Rlc3QuZXhhbXBsZS5vcmcv',
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Unauth CDA request'
|
||||
);
|
||||
my ( $host, $url, $query ) = expectForm( $res, undef, undef, 'url' );
|
||||
ok( $query =~ /\burl=aHR0cDovL3Rlc3QuZXhhbXBsZS5vcmcv\b/, ' check url value' );
|
||||
count(2);
|
||||
|
||||
# Authentification
|
||||
$query .= '&user=dwho&password=dwho';
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/' => IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post credentials'
|
||||
);
|
||||
count(1);
|
||||
|
||||
($query) =
|
||||
expectRedirection( $res, qr#^http://test.example.org/\?(lemonldapcda=.*)$# );
|
||||
|
||||
# Handler part
|
||||
use_ok('Lemonldap::NG::Handler::PSGI');
|
||||
use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib');
|
||||
count(2);
|
||||
|
||||
my ( $cli, $app );
|
||||
switch ('app');
|
||||
ok( $app = LocalApp->run( $client->ini ), 'App' );
|
||||
count(1);
|
||||
|
||||
ok(
|
||||
$res = $app->( {
|
||||
'HTTP_ACCEPT' => 'text/html',
|
||||
'SCRIPT_NAME' => '/',
|
||||
'SERVER_NAME' => '127.0.0.1',
|
||||
'QUERY_STRING' => $query,
|
||||
'HTTP_CACHE_CONTROL' => 'max-age=0',
|
||||
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'PATH_INFO' => '/',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'REQUEST_URI' => "/?$query",
|
||||
'X_ORIGINAL_URI' => "/?$query",
|
||||
'SERVER_PORT' => '80',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'HTTP_USER_AGENT' =>
|
||||
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'HTTP_HOST' => 'test.example.org',
|
||||
'VHOSTTYPE' => 'CDA',
|
||||
}
|
||||
),
|
||||
'Push cda cookie'
|
||||
);
|
||||
count(1);
|
||||
expectRedirection( $res, 'http://test.example.org/' );
|
||||
my $cid = expectCookie($res);
|
||||
|
||||
ok(
|
||||
$res = $app->( {
|
||||
'HTTP_ACCEPT' => 'text/html',
|
||||
'SCRIPT_NAME' => '/',
|
||||
'SERVER_NAME' => '127.0.0.1',
|
||||
'HTTP_COOKIE' => "lemonldap=$cid",
|
||||
'HTTP_CACHE_CONTROL' => 'max-age=0',
|
||||
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'PATH_INFO' => '/',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'REQUEST_URI' => "/",
|
||||
'X_ORIGINAL_URI' => "/",
|
||||
'SERVER_PORT' => '80',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'HTTP_USER_AGENT' =>
|
||||
'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'HTTP_HOST' => 'test.example.org',
|
||||
'VHOSTTYPE' => 'CDA',
|
||||
}
|
||||
),
|
||||
'Authenticated query'
|
||||
);
|
||||
count(1);
|
||||
expectOK($res);
|
||||
expectAuthenticatedAs( $res, 'dwho' );
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
||||
|
||||
sub switch {
|
||||
my $type = shift;
|
||||
@Lemonldap::NG::Handler::Main::_onReload = @{
|
||||
$handlerOR{$type};
|
||||
};
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
totp2fSelfRegistration => 1,
|
||||
totp2fActivation => 1,
|
||||
totp2fDigits => 6,
|
||||
impersonationRule => 1,
|
||||
|
||||
#hiddenAttributes => 'test',
|
||||
}
|
||||
|
@ -338,7 +339,8 @@ ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
|
|||
count(11);
|
||||
|
||||
my @c = ( $res->[2]->[0] =~ /<td scope="row">rtyler<\/td>/gs );
|
||||
ok( @c == 3, ' -> Three entries found' );
|
||||
ok( @c == 6, ' -> Six entries found' )
|
||||
or explain( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
# Request with short VH url & user
|
||||
|
@ -456,4 +458,53 @@ count(2);
|
|||
$client->logout($id);
|
||||
clean_sessions();
|
||||
|
||||
## Try to authenticate
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$id = expectCookie($res);
|
||||
expectRedirection( $res, 'http://auth.example.com/' );
|
||||
|
||||
# CheckUser form -> granted
|
||||
# ------------------------
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/checkuser',
|
||||
cookie => "lemonldap=$id",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'CheckUser form',
|
||||
);
|
||||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/checkuser', 'user', 'url' );
|
||||
|
||||
# Request a user without SSO session
|
||||
$query =~ s/user=dwho/user=rtyler/;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/checkuser',
|
||||
IO::String->new($query),
|
||||
cookie => "lemonldap=$id",
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'POST checkuser'
|
||||
);
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">uid</td>%, 'Found uid' )
|
||||
or explain( $res->[2]->[0], 'Attribute Value uid' );
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">real_uid</td>%, 'Found real_uid' )
|
||||
or explain( $res->[2]->[0], 'Attribute Value real_uid' );
|
||||
count(4);
|
||||
|
||||
$client->logout($id);
|
||||
clean_sessions();
|
||||
done_testing( count() );
|
||||
|
|
|
@ -289,7 +289,7 @@ ok(
|
|||
count(3);
|
||||
expectAuthenticatedAs( $res, 'rtyler' );
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
|
||||
'Found trspan="contextSwitching_ON"' )
|
||||
'Found trspan="contextSwitching_OFF"' )
|
||||
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
|
@ -301,11 +301,11 @@ ok(
|
|||
);
|
||||
|
||||
# Refresh cookie value
|
||||
$id = expectCookie($res);
|
||||
my $id1 = expectCookie($res);
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
cookie => "lemonldap=$id",
|
||||
cookie => "lemonldap=$id1",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'Get Menu',
|
||||
|
@ -317,18 +317,87 @@ ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
|
|||
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
|
||||
count(1);
|
||||
|
||||
# ContextSwitching form -> PE_OK
|
||||
# ------------------------
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/switchcontext',
|
||||
cookie => "lemonldap=$id1",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'ContextSwitching form',
|
||||
);
|
||||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/switchcontext', 'spoofId' );
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
|
||||
'Found trspan="contextSwitching_ON"' )
|
||||
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
|
||||
$query =~ s/spoofId=/spoofId=rtyler/;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/switchcontext',
|
||||
IO::String->new($query),
|
||||
cookie => "lemonldap=$id1",
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'POST switchcontext'
|
||||
);
|
||||
|
||||
# Refresh cookie value
|
||||
my $id2 = expectCookie($res);
|
||||
$client->logout($id1);
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
cookie => "lemonldap=$id2",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'Get Menu',
|
||||
);
|
||||
|
||||
expectAuthenticatedAs( $res, 'rtyler' );
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
|
||||
'Found trspan="contextSwitching_OFF"' )
|
||||
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/switchcontext',
|
||||
cookie => "lemonldap=$id2",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'Stop context switching',
|
||||
);
|
||||
count(6);
|
||||
|
||||
ok( $res->[2]->[0] =~ m%<span trmsg="1"></span>%,
|
||||
'Found PE_SESSIONEXPIRED' )
|
||||
or explain( $res->[2]->[0], 'Sessuion expired' );
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
cookie => "lemonldap=$id2",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'Get Menu',
|
||||
);
|
||||
expectAuthenticatedAs( $res, 'rtyler' );
|
||||
count(2);
|
||||
|
||||
# Log out request
|
||||
# ------------------------
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
query => 'logout=1',
|
||||
cookie => "lemonldap=$id",
|
||||
cookie => "lemonldap=$id2",
|
||||
accept => 'text/html'
|
||||
),
|
||||
'Get Menu',
|
||||
);
|
||||
count(1);
|
||||
expectOK($res);
|
||||
|
||||
ok(
|
||||
|
@ -336,7 +405,7 @@ ok(
|
|||
m%<div class="message message-positive alert"><span trmsg="-7"></span></div>%,
|
||||
'Dwho has been well disconnected'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
count(2);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
checkUserDisplayPersistentInfo => 0,
|
||||
checkUserDisplayEmptyValues => 0,
|
||||
impersonationMergeSSOgroups => 0,
|
||||
checkUserHiddenAttributes => '_loginHistory hGroups',
|
||||
macros => {
|
||||
test_impersonation => '"$testPrefix__user/$_user"',
|
||||
_whatToTrace =>
|
||||
|
|
|
@ -26,6 +26,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
checkUserDisplayPersistentInfo => 0,
|
||||
checkUserDisplayEmptyValues => 0,
|
||||
impersonationMergeSSOgroups => 0,
|
||||
checkUserHiddenAttributes => '_loginHistory hGroups',
|
||||
macros => {
|
||||
test_impersonation => '"$testPrefix__user/$_user"',
|
||||
_whatToTrace =>
|
||||
|
|
|
@ -8,6 +8,7 @@ require 't/smtp.pm';
|
|||
|
||||
use_ok('Lemonldap::NG::Common::FormEncode');
|
||||
count(1);
|
||||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
|
@ -34,7 +35,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
# Try to authenticate
|
||||
# -------------------
|
||||
ok(
|
||||
my $res = $client->_post(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho&lmAuth=weak'),
|
||||
length => 35,
|
||||
|
@ -51,7 +52,7 @@ my $id = expectCookie($res);
|
|||
# --------------------------------------------
|
||||
|
||||
ok(
|
||||
my $res = $client->_get(
|
||||
$res = $client->_get(
|
||||
'/upgradesession',
|
||||
query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t',
|
||||
accept => 'text/html',
|
||||
|
@ -68,7 +69,7 @@ my ( $host, $url, $query ) =
|
|||
# ----------------------
|
||||
|
||||
ok(
|
||||
my $res = $client->_post(
|
||||
$res = $client->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
|
@ -81,7 +82,7 @@ count(1);
|
|||
|
||||
my $pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'upgrading', 'url' );
|
||||
( $host, $url, $query ) = expectForm( $res, '#', undef, 'upgrading', 'url' );
|
||||
|
||||
$query = $query . "&user=dwho&password=dwho&lmAuth=strong";
|
||||
|
||||
|
@ -90,7 +91,7 @@ $query = $query . "&user=dwho&password=dwho&lmAuth=strong";
|
|||
# -------------------------------------------
|
||||
|
||||
ok(
|
||||
my $res = $client->_post(
|
||||
$res = $client->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
|
@ -101,7 +102,7 @@ ok(
|
|||
);
|
||||
count(1);
|
||||
|
||||
my $pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/mail2fcheck', 'token', 'code' );
|
||||
|
@ -142,7 +143,7 @@ expectRedirection( $res, 'http://test1.example.com' );
|
|||
|
||||
# Make pdata was cleared and we aren't being redirected
|
||||
ok(
|
||||
my $res = $client->_get(
|
||||
$res = $client->_get(
|
||||
'/',
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id;lemonldappdata=$pdata",
|
||||
|
|
Loading…
Reference in New Issue