Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2019-07-10 12:15:55 +02:00
commit b7c8d30b3f
65 changed files with 999 additions and 161 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,7 @@ sub run {
: ""
)
);
$req->data->{'noTry'} = 1;
return $class->REDIRECT;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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":"استخدام القاعدة",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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":"اختر أحد التطبيقات المسموح لك بالدخول إليها",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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