Modify Try.pm to handle authorizations (#1658)

environments/ppa-mbqj77/deployments/712^2
Xavier Guimard 6 years ago
parent 6ba5c4b530
commit 26b80873be
  1. 33
      _example/conf/lmConf-1.json
  2. 5
      e2e-tests/lmConf-1.json
  3. 1
      lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Try.pm
  4. 94
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm
  5. 15
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Plugin.pm
  6. 18
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CheckUser.pm
  7. 131
      lemonldap-ng-portal/t/67-CheckUser.t
  8. 5
      lemonldap-ng-portal/t/lmConf-1.json

@ -119,20 +119,25 @@
"namespace" : "lemonldap-ng-sessions"
},
"locationRules" : {
"manager.__DNSDOMAIN__" : {
"(?#Configuration)^/(manager\\.html|conf/)" : "$uid eq \"dwho\"",
"(?#Notifications)/notifications" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"(?#Sessions)/sessions" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"default" : "$uid eq \"dwho\" or $uid eq \"rtyler\""
},
"test1.__DNSDOMAIN__" : {
"^/logout" : "logout_sso",
"default" : "accept"
},
"test2.__DNSDOMAIN__" : {
"^/logout" : "logout_sso",
"default" : "accept"
}
"auth.__DNSDOMAIN__" : {
"(?#checkUser)^/checkuser": "$uid eq \"dwho\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
"manager.__DNSDOMAIN__" : {
"(?#Configuration)^/(manager\\.html|conf/)" : "$uid eq \"dwho\"",
"(?#Notifications)/notifications" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"(?#Sessions)/sessions" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"default" : "$uid eq \"dwho\" or $uid eq \"rtyler\""
},
"test1.__DNSDOMAIN__" : {
"^/logout" : "logout_sso",
"default" : "accept"
},
"test2.__DNSDOMAIN__" : {
"^/logout" : "logout_sso",
"default" : "accept"
}
},
"loginHistoryEnabled" : 1,
"macros" : {

@ -142,8 +142,9 @@
},
"locationRules": {
"auth.example.com" : {
"(?#checkUser)/checkuser" : "$uid eq \"dwho\"",
"default" : "deny"
"(?#checkUser)^/checkuser": "$uid eq \"dwho\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
"manager.example.com": {
"(?#Configuration)^/(manager\\.html|conf/)": "$uid eq \"dwho\"",

@ -51,7 +51,6 @@ sub defaultUnauthRoute {
sub _run {
my $self = shift;
$self->rule(1);
return sub {
my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );

@ -88,9 +88,9 @@ has csp => ( is => 'rw' );
sub init {
my ( $self, $args ) = @_;
$args ||= {};
$self->localConfig(
{ %{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
->getLocalConf('portal')
$self->localConfig( {
%{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
->getLocalConf('portal')
},
%$args
}
@ -101,8 +101,7 @@ sub init {
and -r $self->{localConfig}->{translations} )
{
open my $tr_file, '<', $self->{localConfig}->{translations}
or die "Can't open"
. $self->{localConfig}->{translations} . " : $!";
or die "Can't open" . $self->{localConfig}->{translations} . " : $!";
while (<$tr_file>) {
chomp;
$_ =~ /^([\w_]+)\s+=\s+(.+)$/;
@ -141,33 +140,33 @@ sub init {
# Handle requests (other path may be declared in enabled plugins)
$self
# "/" or undeclared paths
->addUnauthRoute( '*' => 'login', ['GET'] )
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
# "/" or undeclared paths
->addUnauthRoute( '*' => 'login', ['GET'] )
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
# Logout
->addAuthRoute( logout => 'logout', ['GET'] );
# Logout
->addAuthRoute( logout => 'logout', ['GET'] );
# Default routes must point to routines declared above
$self->defaultAuthRoute('');
@ -201,8 +200,8 @@ sub reloadConf {
$self->csp($csp);
# Initialize templateDir
$self->{templateDir}
= $self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
$self->{templateDir} =
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
unless ( -d $self->{templateDir} ) {
$self->error("Template dir $self->{templateDir} doesn't exist");
return $self->fail;
@ -222,8 +221,8 @@ sub reloadConf {
# Initialize persistent session DB
unless ( $self->conf->{persistentStorage} ) {
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
$self->conf->{persistentStorageOptions}
= $self->conf->{globalStorageOptions};
$self->conf->{persistentStorageOptions} =
$self->conf->{globalStorageOptions};
}
# Initialize cookie domain
@ -247,19 +246,19 @@ sub reloadConf {
return $self->fail;
}
$mod = $self->conf->{$type}
unless ( $self->conf->{$type} eq 'Same' );
unless ( $self->conf->{$type} eq 'Same' );
my $module = '::' . ucfirst($type) . '::' . $mod;
$module =~ s/Authentication/Auth/;
# Launch and initialize module
return $self->fail
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
}
# Load second-factor engine
return $self->fail
unless $self->{_sfEngine}
= $self->loadPlugin( $self->conf->{'sfEngine'} );
unless $self->{_sfEngine} =
$self->loadPlugin( $self->conf->{'sfEngine'} );
# Initialize trusted domain regexp
if ( $self->conf->{trustedDomains}
@ -282,8 +281,8 @@ sub reloadConf {
# - $domainlabel.$td
# $domainlabel is build looking RFC2396
# (see Regexp::Common::URI::RFC2396)
$_
=~ s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
$_ =~
s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
$re->add("$_");
}
}
@ -294,8 +293,8 @@ sub reloadConf {
$self->logger->debug("Vhost $vhost added in trusted domains");
$re->add( quotemeta($vhost) );
$self->conf->{vhostOptions} ||= {};
if ( my $tmp
= $self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
if ( my $tmp =
$self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
{
foreach my $alias ( split /\s+/, $tmp ) {
$self->logger->debug(
@ -313,22 +312,22 @@ sub reloadConf {
$self->{"_$type"} = {};
if ( $self->conf->{$type} ) {
for my $name ( sort keys %{ $self->conf->{$type} } ) {
my $sub = HANDLER->buildSub(
my $sub =
HANDLER->buildSub(
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
if ($sub) {
$self->{"_$type"}->{$name} = $sub;
}
else {
$self->logger->error( "$type $name returns an error: "
. HANDLER->tsv->{jail}->error );
. HANDLER->tsv->{jail}->error );
}
}
}
}
$self->{_jsRedirect}
= HANDLER->buildSub(
HANDLER->substitute( $self->conf->{jsRedirect} ) )
or $self->logger->error(
$self->{_jsRedirect} =
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
or $self->logger->error(
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
# Load plugins
@ -351,6 +350,9 @@ sub reloadConf {
return PE_OK;
}
};
my $portal = $self->conf->{portal};
$portal =~ s#^https?://(.*?)(?:/|$)/#$1#;
HANDLER->tsv->{defaultCondition}->{$portal} ||= sub {1};
1;
}
@ -364,7 +366,7 @@ sub loadPlugin {
}
my $obj;
return 0
unless ( $obj = $self->loadModule("$plugin") );
unless ( $obj = $self->loadModule("$plugin") );
return $self->findEP( $plugin, $obj );
}
@ -436,7 +438,7 @@ sub findEP {
if ( $obj->can('spRules') ) {
foreach my $k ( keys %{ $obj->{spRules} } ) {
$self->logger->info(
"$k is defined more than one time, it can have some bad effect on Menu display"
"$k is defined more than one time, it can have some bad effect on Menu display"
) if ( $self->spRules->{$k} );
$self->spRules->{$k} = $obj->{spRules}->{$k};
}

@ -57,21 +57,6 @@ sub loadTemplate {
return $self->p->loadTemplate(@_);
}
sub accessCtrl {
my ( $self, $req, $uri ) = @_;
my $url = $self->conf->{portal} . $uri;
$self->logger->debug("Plugin calls accessCtrl for URL: $url");
# Check access rule
my ( $vhost, $appuri ) = $url =~ m#^https?://([^/]*)(.*)#;
$vhost =~ s/:\d+$//;
$appuri ||= '/';
$self->logger->debug(
"grant function call with VH: $vhost and URI: $appuri");
return $self->p->HANDLER->grant( $req, $req->{userData}, $appuri,
undef, $vhost );
}
1;
__END__

@ -44,15 +44,6 @@ sub check {
my ( $attrs, $array_attrs, $array_hdrs ) = ( {}, [], [] );
my $msg = my $auth = '';
# Check access rule
unless ( $self->accessCtrl( $req, 'checkuser' ) ) {
$self->userLogger->error(
"user $req->{user} not allowed to access /checkuser");
return $self->p->lmError( $req, 403 );
}
$self->userLogger->notice(
"user $req->{user} is allowed to access /checkuser");
# Check token
if ( $self->conf->{requireToken} ) {
my $token = $req->param('token');
@ -183,15 +174,6 @@ sub check {
sub display {
my ( $self, $req ) = @_;
# Check access rule
unless ( $self->accessCtrl( $req, 'checkuser' ) ) {
$self->userLogger->error(
"user $req->{user} not allowed to access /checkuser");
return $self->p->lmError( $req, 403 );
}
$self->userLogger->notice(
"user $req->{user} is allowed to access /checkuser");
# Display form
return $self->p->sendHtml(
$req,

@ -8,16 +8,16 @@ 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',
checkUser => 1,
requireToken => 0,
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
loginHistoryEnabled => 0,
brutForceProtection => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
checkUser => 1,
requireToken => 0,
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
@ -25,7 +25,8 @@ my $client = LLNG::Manager::Test->new(
);
## Try to authenticate
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/',
IO::String->new('user=rtyler&password=rtyler'),
length => 27,
@ -38,7 +39,8 @@ count(1);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
ok( $res = $client->_get(
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
@ -47,17 +49,16 @@ ok( $res = $client->_get(
);
count(1);
ok( $res->[2]->[0] =~ m%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo' )
or explain( $res->[2]->[0], 'custom Main logo not found"' );
ok( $res->[2]->[0] =~ m%<span trspan="accessDenied">%,
'Found trspan="accessDenied"' )
or explain( $res->[2]->[0], 'trspan="accessDenied"' );
count(2);
ok( $res->[2]->[0] =~ m%An error occurs, you're going to be redirected to%,
'Found redirection page' )
or explain( $res->[2]->[0],
"An error occurs, you're going to be redirected to" );
count(1);
$client->logout($id);
## Try to authenticate
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
@ -72,7 +73,8 @@ expectRedirection( $res, 'http://auth.example.com/' );
# CheckUser form -> granted
# ------------------------
ok( $res = $client->_get(
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
@ -82,16 +84,16 @@ ok( $res = $client->_get(
count(1);
# Request with bad VH
my ( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
my ( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~ s/user=dwho/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftry.example.com/;
ok( $res = $client->_post(
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -102,22 +104,23 @@ ok( $res = $client->_post(
);
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="VHnotFound">%,
'Found trspan="VHnotFound"' )
or explain( $res->[2]->[0], 'trspan="VHnotFound"' );
or explain( $res->[2]->[0], 'trspan="VHnotFound"' );
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
$query =~ s/url=http%3A%2F%2Ftry.example.com/url=http%3A%2F%2Ftest1.example.com/;
ok( $res = $client->_post(
$query =~
s/url=http%3A%2F%2Ftry.example.com/url=http%3A%2F%2Ftest1.example.com/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -128,49 +131,48 @@ ok( $res = $client->_post(
);
count(1);
( $host, $url, $query )
= expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%,
'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok( $res->[2]->[0]
=~ m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
ok(
$res->[2]->[0] =~
m%<div class="alert alert-success"><b><span trspan="allowed"></span></b></div>%,
'Found trspan="allowed"'
) or explain( $res->[2]->[0], 'trspan="allowed"' );
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] =~ m%<span trspan="groups_sso">%,
'Found trspan="groups_sso"' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
'Found trspan="attributes"' )
or explain( $res->[2]->[0], 'trspan="attributes"' );
or explain( $res->[2]->[0], 'trspan="attributes"' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">Auth-User</td>%,
'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%,
'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler</td>%, 'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">su</td>%, 'Found su' )
or explain( $res->[2]->[0], 'SSO Groups: su' );
or explain( $res->[2]->[0], 'SSO Groups: su' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td class="text-left">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Attribute Value uid' );
or explain( $res->[2]->[0], 'Attribute Value uid' );
count(11);
$query =~ s/user=dwho/user=msmith/;
$query
=~ s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
ok( $res = $client->_post(
$query =~
s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
@ -179,8 +181,9 @@ ok( $res = $client->_post(
),
'POST checkuser'
);
ok( $res->[2]->[0]
=~ m%<div class="alert alert-danger"><b><span trspan="forbidden"></span></b></div>%,
ok(
$res->[2]->[0] =~
m%<div class="alert alert-danger"><b><span trspan="forbidden"></span></b></div>%,
'Found trspan="forbidden"'
) or explain( $res->[2]->[0], 'trspan="forbidden"' );
count(2);
@ -188,4 +191,4 @@ count(2);
$client->logout($id);
clean_sessions();
done_testing( count() );
done_testing( count() );

@ -35,8 +35,9 @@
"key": "qwertyui",
"locationRules": {
"auth.example.com" : {
"(?#checkUser)/checkuser" : "$uid eq \"dwho\"",
"default" : "deny"
"(?#checkUser)^/checkuser" : "$uid eq \"dwho\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
"manager.example.com": {
"(?#Configuration)^/(manager\\.html|conf/)": "$uid eq \"dwho\"",

Loading…
Cancel
Save