Merge branch 'v2.0'

This commit is contained in:
Xavier 2019-09-02 23:16:37 +02:00
commit c312e16712
16 changed files with 243 additions and 45 deletions

View File

@ -397,47 +397,60 @@ sub getDBConf {
return $conf; return $conf;
} }
sub _launch {
my $self = shift;
my $sub = shift;
my $res;
eval {
local $SIG{ALRM} = sub { die "TIMEOUT\n" };
alarm ($self->{confTimeout} || 10);
$res = &{ $self->{type} . "::$sub" }( $self, @_ );
alarm 0;
};
$msg .= $@ if $@;
return $res;
}
## @method boolean prereq() ## @method boolean prereq()
# Call prereq() from the $self->{type} package. # Call prereq() from the $self->{type} package.
# @return True if succeed # @return True if succeed
sub prereq { sub prereq {
return &{ $_[0]->{type} . '::prereq' }(@_); return shift->_launch( 'prereq', @_ );
} }
## @method @ available() ## @method @ available()
# Call available() from the $self->{type} package. # Call available() from the $self->{type} package.
# @return list of available configuration numbers # @return list of available configuration numbers
sub available { sub available {
return &{ $_[0]->{type} . '::available' }(@_); return shift->_launch( 'available', @_ );
} }
## @method int lastCfg() ## @method int lastCfg()
# Call lastCfg() from the $self->{type} package. # Call lastCfg() from the $self->{type} package.
# @return Number of the last configuration available # @return Number of the last configuration available
sub lastCfg { sub lastCfg {
my $result = &{ $_[0]->{type} . '::lastCfg' }(@_) || "0"; return shift->_launch( 'lastCfg', @_ ) || 0;
return $result;
} }
## @method boolean lock() ## @method boolean lock()
# Call lock() from the $self->{type} package. # Call lock() from the $self->{type} package.
# @return True if succeed # @return True if succeed
sub lock { sub lock {
return &{ $_[0]->{type} . '::lock' }(@_); return shift->_launch( 'lock', @_ );
} }
## @method boolean isLocked() ## @method boolean isLocked()
# Call isLocked() from the $self->{type} package. # Call isLocked() from the $self->{type} package.
# @return True if database is locked # @return True if database is locked
sub isLocked { sub isLocked {
return &{ $_[0]->{type} . '::isLocked' }(@_); return shift->_launch( 'isLocked', @_ );
} }
## @method boolean unlock() ## @method boolean unlock()
# Call unlock() from the $self->{type} package. # Call unlock() from the $self->{type} package.
# @return True if succeed # @return True if succeed
sub unlock { sub unlock {
return &{ $_[0]->{type} . '::unlock' }(@_); return shift->_launch( 'unlock', @_ );
} }
## @method int store(hashRef conf) ## @method int store(hashRef conf)
@ -445,14 +458,14 @@ sub unlock {
# @param $conf Lemondlap configuration serialized # @param $conf Lemondlap configuration serialized
# @return Number of new configuration stored if succeed, 0 else. # @return Number of new configuration stored if succeed, 0 else.
sub store { sub store {
return &{ $_[0]->{type} . '::store' }(@_); return shift->_launch( 'store', @_ );
} }
## @method load(int cfgNum, arrayRef fields) ## @method load(int cfgNum, arrayRef fields)
# Call load() from the $self->{type} package. # Call load() from the $self->{type} package.
# @return Lemonldap::NG Configuration hashRef if succeed, 0 else. # @return Lemonldap::NG Configuration hashRef if succeed, 0 else.
sub load { sub load {
return &{ $_[0]->{type} . '::load' }(@_); return shift->_launch( 'load', @_ );
} }
## @method boolean delete(int cfgNum) ## @method boolean delete(int cfgNum)
@ -463,7 +476,7 @@ sub delete {
my ( $self, $c ) = @_; my ( $self, $c ) = @_;
my @a = $self->available(); my @a = $self->available();
if ( grep( /^$c$/, @a ) ) { if ( grep( /^$c$/, @a ) ) {
return &{ $self->{type} . '::delete' }( $self, $c ); return $self->_launch( 'delete', $self, $c );
} }
else { else {
return 0; return 0;
@ -471,7 +484,7 @@ sub delete {
} }
sub logError { sub logError {
return &{ $_[0]->{type} . '::logError' }(@_); return shift->_launch( 'logError', @_ );
} }
1; 1;

View File

@ -82,6 +82,8 @@ has 'error' => (
has info => ( is => 'rw' ); has info => ( is => 'rw' );
has timeout => ( is => 'rw', default => 5 );
sub BUILD { sub BUILD {
my ($self) = @_; my ($self) = @_;
@ -93,7 +95,9 @@ sub BUILD {
# Register options for common Apache::Session module # Register options for common Apache::Session module
my $moduleOptions = $self->storageModuleOptions || {}; my $moduleOptions = $self->storageModuleOptions || {};
my %options = ( $self->timeout( delete $moduleOptions->{timeout} )
if $moduleOptions->{timeout};
my %options = (
%$moduleOptions, %$moduleOptions,
backend => $self->storageModule, backend => $self->storageModule,
localStorage => $self->cacheModule, localStorage => $self->cacheModule,
@ -158,10 +162,12 @@ sub BUILD {
sub _tie_session { sub _tie_session {
my $self = $_[0]; my $self = $_[0];
my $options = $_[1] || {}; my $options = $_[1] || {};
my %h; my %h;
eval { eval {
local $SIG{ALRM} = sub { die "TIMEOUT\n" };
alarm $self->timeout;
# SOAP/REST session module must be directly tied # SOAP/REST session module must be directly tied
if ( $self->storageModule =~ /^Lemonldap::NG::Common::Apache::Session/ ) if ( $self->storageModule =~ /^Lemonldap::NG::Common::Apache::Session/ )
{ {
@ -172,8 +178,9 @@ sub _tie_session {
tie %h, 'Lemonldap::NG::Common::Apache::Session', $self->id, tie %h, 'Lemonldap::NG::Common::Apache::Session', $self->id,
{ %{ $self->options }, %$options }; { %{ $self->options }, %$options };
} }
}; alarm 0;
};
if ( $@ or not tied(%h) ) { if ( $@ or not tied(%h) ) {
my $msg = "Session cannot be tied"; my $msg = "Session cannot be tied";
$msg .= ": $@" if $@; $msg .= ": $@" if $@;

View File

@ -0,0 +1,11 @@
package Lemonldap::NG::Handler::Lib::Fail;
use base Lemonldap::NG::Handler::Main;
sub run {
return $_[0]->SERVER_ERROR;
}
our $VERSION = '2.0.6';
1;

View File

@ -21,7 +21,7 @@ sub init {
return 0; return 0;
} }
unless ( $self->api->checkConf($self) unless ( $self->api->checkConf($self)
or $self->{protection} eq 'none' ) or ( $self->{protection} and $self->{protection} eq 'none' ) )
{ {
$self->error( $self->error(
"Unable to protect this server ($Lemonldap::NG::Common::Conf::msg)" "Unable to protect this server ($Lemonldap::NG::Common::Conf::msg)"
@ -127,7 +127,8 @@ sub _authAndTrace {
$type = $tmp . $type; $type = $tmp . $type;
Lemonldap::NG::Handler::Main->buildAndLoadType($type); Lemonldap::NG::Handler::Main->buildAndLoadType($type);
my ( $res, $session ) = $type->run( $req, $self->{rule} ); my ( $res, $session ) = $type->run( $req, $self->{rule} );
$self->portal( $type->tsv->{portal}->() ); eval { $self->portal( $type->tsv->{portal}->() ) };
$self->logger->warn($@) if $@;
$req->userData($session) if ($session); $req->userData($session) if ($session);
if ( $res < 300 ) { if ( $res < 300 ) {
@ -145,7 +146,7 @@ sub _authAndTrace {
return [ $res, [ $req->spliceHdrs ], [] ]; return [ $res, [ $req->spliceHdrs ], [] ];
} }
else { else {
my $s = $type->tsv->{portal}->() . "/lmerror/$res"; my $s = ( $self->portal ? $self->portal . "/lmerror/$res" : '' );
$s = $s =
'<html><head><title>Redirection</title></head><body>' '<html><head><title>Redirection</title></head><body>'
. qq{<script type="text/javascript">window.location='$s'</script>} . qq{<script type="text/javascript">window.location='$s'</script>}

View File

@ -83,8 +83,11 @@ sub checkType {
my ( $class, $req ) = @_; my ( $class, $req ) = @_;
if ( time() - $class->lastCheck > $class->checkTime ) { if ( time() - $class->lastCheck > $class->checkTime ) {
die("$class: No configuration found") unless ( $class->checkConf ) {
unless ( $class->checkConf ); $class->logger->error("$class: No configuration found");
$req->data->{noTry} = 1;
return 'Fail';
}
} }
my $vhost = $class->resolveAlias($req); my $vhost = $class->resolveAlias($req);
return ( defined $class->tsv->{type}->{$vhost} ) return ( defined $class->tsv->{type}->{$vhost} )

View File

@ -60,6 +60,16 @@ schemes =
(t,v,q) -> (t,v,q) ->
q.replace(/\&groupBy.*$/, '') + "&ipAddr=#{v}" q.replace(/\&groupBy.*$/, '') + "&ipAddr=#{v}"
] ]
_session_uid: [
# First level: display 1 letter
(t,v) ->
"groupBy=substr(#{t},1)"
# Second level (if no overScheme), display usernames
(t,v) ->
"#{t}=#{v}*&groupBy=#{t}"
(t,v) ->
"#{t}=#{v}"
]
# When number of children nodes exceeds "max" value and if "overScheme.<type>" # When number of children nodes exceeds "max" value and if "overScheme.<type>"
# is available and does not return "null", a level is added. See # is available and does not return "null", a level is added. See
@ -86,6 +96,12 @@ overScheme =
"#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})" "#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})"
else else
null null
_session_uid: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over
if level == 1 and v.length > over
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else
null
hiddenAttributes = '_password' hiddenAttributes = '_password'

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.8 // Generated by CoffeeScript 1.12.7
/* /*
* Sessions explorer * Sessions explorer
@ -69,6 +69,15 @@
}, function(t, v, q) { }, function(t, v, q) {
return q.replace(/\&groupBy.*$/, '') + ("&ipAddr=" + v); return q.replace(/\&groupBy.*$/, '') + ("&ipAddr=" + v);
} }
],
_session_uid: [
function(t, v) {
return "groupBy=substr(" + t + ",1)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=" + t;
}, function(t, v) {
return t + "=" + v;
}
] ]
}; };
@ -96,6 +105,14 @@
} else { } else {
return null; return null;
} }
},
_session_uid: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over);
if (level === 1 && v.length > over) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else {
return null;
}
} }
}; };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -444,6 +444,8 @@ t/01-AuthDemo.t
t/01-CSP-and-CORS-headers.t t/01-CSP-and-CORS-headers.t
t/01-pdata.t t/01-pdata.t
t/02-Password-Demo.t t/02-Password-Demo.t
t/03-ConfTimeout.t
t/03-SessionTimeout.t
t/03-XSS-protection.t t/03-XSS-protection.t
t/04-language-selection.t t/04-language-selection.t
t/19-Auth-Null.t t/19-Auth-Null.t
@ -506,6 +508,7 @@ t/32-Auth-and-issuer-OIDC-implicit.t
t/32-Auth-and-issuer-OIDC-sorted.t t/32-Auth-and-issuer-OIDC-sorted.t
t/32-CAS-10.t t/32-CAS-10.t
t/32-OIDC-RP-rule.t t/32-OIDC-RP-rule.t
t/32-OIDC-Token-Introspection.t
t/32-OIDC-Token-Security.t t/32-OIDC-Token-Security.t
t/33-Auth-and-issuer-OpenID2.t t/33-Auth-and-issuer-OpenID2.t
t/34-Auth-Proxy-and-REST-Server.t t/34-Auth-Proxy-and-REST-Server.t
@ -623,6 +626,8 @@ t/gpghome/private-keys-v1.d/A076B0E7DB141A919271EE8B581CDFA8DA42F333.key
t/gpghome/private-keys-v1.d/B7219440BCCD85200121CFB89F94C8D98C0397B3.key t/gpghome/private-keys-v1.d/B7219440BCCD85200121CFB89F94C8D98C0397B3.key
t/gpghome/pubring.kbx t/gpghome/pubring.kbx
t/gpghome/trustdb.gpg t/gpghome/trustdb.gpg
t/lib/Apache/Session/Timeout.pm
t/lib/Lemonldap/NG/Common/Conf/Backends/Timeout.pm
t/lib/Lemonldap/NG/Handler/Test.pm t/lib/Lemonldap/NG/Handler/Test.pm
t/lib/Lemonldap/NG/Portal/Auth/LDAPPolicy.pm t/lib/Lemonldap/NG/Portal/Auth/LDAPPolicy.pm
t/lmConf-1.json t/lmConf-1.json

View File

@ -0,0 +1,44 @@
use Test::More;
use IO::String;
use lib 't/lib';
require 't/test-lib.pm';
my $res;
my $client = LLNG::Manager::Test->new( {
confFailure => 1,
ini => {
configStorage => {
type => 'Timeout',
dirName => 't',
confTimeout => 1,
},
logLevel => 'error',
useSafeJail => 1,
globalStorage => 'Apache::Session::Timeout',
globalStorageOptions => {
Directory => $tmpDir,
LockDirectory => "$tmpDir/lock",
timeout => 1,
},
}
}
);
diag "Waiting";
ok( !$client->{p}->init( $client->ini ) );
ok( $client->app( $client->{p}->run ) );
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
),
'Auth query'
);
ok( $res->[0] == 500 );
count(4);
clean_sessions();
done_testing( count() );

View File

@ -0,0 +1,41 @@
use Test::More;
use strict;
use IO::String;
use lib 't/lib';
require 't/test-lib.pm';
my $res;
my $client = LLNG::Manager::Test->new(
{
ini => {
logLevel => 'error',
useSafeJail => 1,
globalStorage => 'Apache::Session::Timeout',
globalStorageOptions => {
Directory => 't/sessions',
LockDirectory => 't/sessions/lock',
timeout => 1,
},
}
}
);
# Try to authenticate with good password
# --------------------------------------
diag 'Waiting';
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
),
'Auth query'
);
count(1);
expectReject( $res, 401, 8 );
clean_sessions();
done_testing( count() );

View File

@ -0,0 +1,14 @@
package Apache::Session::Timeout;
use strict;
use Apache::Session::File;
our @ISA = ('Apache::Session::File');
sub populate {
my $self = shift;
sleep 6;
return $self->SUPER::populate(@_);
}
1;

View File

@ -0,0 +1,17 @@
package Lemonldap::NG::Common::Conf::Backends::Timeout;
use Lemonldap::NG::Common::Conf::Backends::File;
our @ISA = ('Lemonldap::NG::Common::Conf::Backends::File');
sub load {
my $self = shift;
sleep 5;
return $self->SUPER::load(@_);
}
sub AUTOLOAD {
$AUTOLOAD =~ s/Lemonldap::NG::Common::Conf::Backends::Timeout:://;
return &{"Lemonldap::NG::Common::Conf::Backends::File::$AUTOLOAD"}(@_);
}
1;

View File

@ -140,7 +140,8 @@ sub count_sessions {
sub getCache { sub getCache {
require Cache::FileCache; require Cache::FileCache;
return Cache::FileCache->new( { return Cache::FileCache->new(
{
namespace => 'lemonldap-ng-session', namespace => 'lemonldap-ng-session',
cache_root => $tmpDir, cache_root => $tmpDir,
cache_depth => 0, cache_depth => 0,
@ -562,6 +563,8 @@ has p => ( is => 'rw' );
=cut =cut
has confFailure => ( is => 'rw' );
has ini => ( has ini => (
is => 'rw', is => 'rw',
lazy => 1, lazy => 1,
@ -573,27 +576,30 @@ has ini => (
} }
$self->{ini} = $ini; $self->{ini} = $ini;
main::ok( $self->{p} = $self->class->new(), 'Portal object' ); main::ok( $self->{p} = $self->class->new(), 'Portal object' );
main::ok( $self->{p}->init($ini), 'Init' ); main::count(1);
main::ok( $self->{app} = $self->{p}->run(), 'Portal app' ); unless ( $self->confFailure ) {
main::count(3); main::ok( $self->{p}->init($ini), 'Init' );
no warnings 'redefine'; main::ok( $self->{app} = $self->{p}->run(), 'Portal app' );
eval main::count(2);
no warnings 'redefine';
eval
'sub Lemonldap::NG::Common::Logger::Std::error {return $_[0]->warn($_[1])}'; 'sub Lemonldap::NG::Common::Logger::Std::error {return $_[0]->warn($_[1])}';
$Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{french} = { $Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{french} = {
uid => 'french', uid => 'french',
cn => 'Frédéric Accents', cn => 'Frédéric Accents',
mail => 'fa@badwolf.org', mail => 'fa@badwolf.org',
}; };
$Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{davros} = { $Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{davros} = {
uid => 'davros', uid => 'davros',
cn => 'Bad Guy', cn => 'Bad Guy',
mail => 'davros@badguy.org', mail => 'davros@badguy.org',
}; };
$Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{russian} = { $Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{russian} = {
uid => 'russian', uid => 'russian',
cn => 'Русский', cn => 'Русский',
mail => 'ru@badwolf.org', mail => 'ru@badwolf.org',
}; };
}
$self; $self;
} }
); );
@ -677,7 +683,8 @@ to test content I<(to launch a C<expectForm()> for example)>.
sub _get { sub _get {
my ( $self, $path, %args ) = @_; my ( $self, $path, %args ) = @_;
my $res = $self->app->( { my $res = $self->app->(
{
'HTTP_ACCEPT' => $args{accept} 'HTTP_ACCEPT' => $args{accept}
|| 'application/json, text/plain, */*', || 'application/json, text/plain, */*',
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3', 'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
@ -729,7 +736,8 @@ sub _post {
my ( $self, $path, $body, %args ) = @_; my ( $self, $path, $body, %args ) = @_;
die "$body must be a IO::Handle" die "$body must be a IO::Handle"
unless ( ref($body) and $body->can('read') ); unless ( ref($body) and $body->can('read') );
my $res = $self->app->( { my $res = $self->app->(
{
'HTTP_ACCEPT' => $args{accept} 'HTTP_ACCEPT' => $args{accept}
|| 'application/json, text/plain, */*', || 'application/json, text/plain, */*',
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3', 'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',

View File

@ -64,6 +64,7 @@ print <<EOF;
===== Configuration backend parameters ===== ===== Configuration backend parameters =====
^ Full name ^ Key name ^ Configuration backend ^ ^ Full name ^ Key name ^ Configuration backend ^
| Configuration load timeout | confTimeout | all backends (default: 10) |
| Directory | dirName | [[fileconfbackend|File]] | | Directory | dirName | [[fileconfbackend|File]] |
| DBI connection string | dbiChain | [[sqlconfbackend|CDBI / RDBI]] | | DBI connection string | dbiChain | [[sqlconfbackend|CDBI / RDBI]] |
| DBI user | dbiUser | ::: | | DBI user | dbiUser | ::: |