2017-02-11 08:47:22 +01:00
|
|
|
package Lemonldap::NG::Handler::Lib::PSGI;
|
2016-01-25 19:03:46 +01:00
|
|
|
|
2021-01-23 18:57:24 +01:00
|
|
|
use strict;
|
2016-01-25 19:03:46 +01:00
|
|
|
use Mouse;
|
|
|
|
|
2016-03-17 23:19:44 +01:00
|
|
|
#use Lemonldap::NG::Handler::Main qw(:jailSharedVars);
|
|
|
|
|
2022-02-01 16:33:08 +01:00
|
|
|
our $VERSION = '2.0.14';
|
2016-01-25 19:03:46 +01:00
|
|
|
|
|
|
|
has protection => ( is => 'rw', isa => 'Str' );
|
2016-02-09 23:17:39 +01:00
|
|
|
has rule => ( is => 'rw', isa => 'Str' );
|
2016-03-17 23:19:44 +01:00
|
|
|
has api => ( is => 'rw', isa => 'Str' );
|
2016-01-25 19:03:46 +01:00
|
|
|
|
2016-01-30 13:26:14 +01:00
|
|
|
## @method boolean init($args)
|
|
|
|
# Initalize main handler
|
2016-01-25 19:03:46 +01:00
|
|
|
sub init {
|
|
|
|
my ( $self, $args ) = @_;
|
2017-02-13 12:58:39 +01:00
|
|
|
eval { $self->api->init($args) };
|
2021-10-07 22:01:08 +02:00
|
|
|
if ( $@ and not( $args->{protection} and $args->{protection} eq 'none' ) ) {
|
2016-01-25 19:03:46 +01:00
|
|
|
$self->error($@);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-03-17 23:19:44 +01:00
|
|
|
unless ( $self->api->checkConf($self)
|
2021-10-07 22:01:08 +02:00
|
|
|
or ( $args->{protection} and $args->{protection} eq 'none' ) )
|
2016-01-25 19:03:46 +01:00
|
|
|
{
|
|
|
|
$self->error(
|
2016-07-25 11:49:55 +02:00
|
|
|
"Unable to protect this server ($Lemonldap::NG::Common::Conf::msg)"
|
|
|
|
);
|
2016-01-25 19:03:46 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2016-05-31 07:27:58 +02:00
|
|
|
eval { $self->portal( $self->api->tsv->{portal}->() ) };
|
2016-04-04 10:39:26 +02:00
|
|
|
my $rule =
|
2021-10-07 22:01:08 +02:00
|
|
|
$args->{protection} || $self->api->localConfig->{protection} || '';
|
2016-02-09 23:17:39 +01:00
|
|
|
$self->rule(
|
|
|
|
$rule eq 'authenticate' ? 1 : $rule eq 'manager' ? '' : $rule );
|
2016-01-25 19:03:46 +01:00
|
|
|
return 1;
|
2016-01-26 07:14:54 +01:00
|
|
|
}
|
2016-01-25 19:03:46 +01:00
|
|
|
|
2016-01-31 23:53:23 +01:00
|
|
|
## @methodi void _run()
|
2016-01-30 13:26:14 +01:00
|
|
|
# Check if protecton is activated then return a code ref that will launch
|
2021-10-08 11:06:32 +02:00
|
|
|
# _logAuthTrace() if protection in on or handler() else
|
2016-01-31 23:53:23 +01:00
|
|
|
#@return code-ref
|
2016-01-25 19:03:46 +01:00
|
|
|
sub _run {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Override _run() only if protection != 'none'
|
2016-03-17 23:19:44 +01:00
|
|
|
if ( !$self->rule or $self->rule ne 'none' ) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug('PSGI app is protected');
|
2016-01-25 19:03:46 +01:00
|
|
|
|
|
|
|
# Handle requests
|
|
|
|
# Developers, be careful: Only this part is executed at each request
|
|
|
|
return sub {
|
2021-10-08 11:06:32 +02:00
|
|
|
return $self->_logAuthTrace(
|
2016-01-26 07:14:54 +01:00
|
|
|
Lemonldap::NG::Common::PSGI::Request->new( $_[0] ) );
|
2016-01-25 19:03:46 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug('PSGI app is not protected');
|
2016-01-30 13:26:14 +01:00
|
|
|
|
|
|
|
# Check if main handler initialization has been done
|
2016-03-17 23:19:44 +01:00
|
|
|
unless ( $self->api->tsv ) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug('Checking conf');
|
2016-03-17 23:19:44 +01:00
|
|
|
eval { $self->api->checkConf() };
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->error($@) if ($@);
|
2016-01-25 19:03:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# Handle unprotected requests
|
|
|
|
return sub {
|
2016-02-01 10:59:07 +01:00
|
|
|
my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
|
2021-10-08 11:06:32 +02:00
|
|
|
my $res = $self->_logAndHandle($req);
|
2019-08-28 00:36:18 +02:00
|
|
|
push @{ $res->[1] }, $req->spliceHdrs;
|
2016-02-01 10:59:07 +01:00
|
|
|
return $res;
|
2016-01-25 19:03:46 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-11 13:09:53 +01:00
|
|
|
sub status {
|
2016-10-25 22:38:29 +02:00
|
|
|
my ( $class, $args ) = @_;
|
|
|
|
$args //= {};
|
|
|
|
my $self = $class->new($args);
|
2017-08-18 15:07:59 +02:00
|
|
|
$self->init($args);
|
2016-02-11 13:09:53 +01:00
|
|
|
|
|
|
|
# Check if main handler initialization has been done
|
2017-08-18 15:07:59 +02:00
|
|
|
unless ( %{ $self->api->tsv } ) {
|
2016-03-17 23:19:44 +01:00
|
|
|
eval { $self->api->checkConf() };
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->error($@) if ($@);
|
2016-02-11 13:09:53 +01:00
|
|
|
}
|
|
|
|
return sub {
|
2016-02-14 08:48:41 +01:00
|
|
|
my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
|
2016-03-17 23:19:44 +01:00
|
|
|
$self->api->status($req);
|
2019-08-28 00:36:18 +02:00
|
|
|
return [ 200, [ $req->spliceHdrs ], [ $req->{respBody} ] ];
|
2016-02-11 13:09:53 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-10-25 22:38:29 +02:00
|
|
|
sub reload {
|
|
|
|
my ( $class, $args ) = @_;
|
|
|
|
$args //= {};
|
|
|
|
my $self = $class->new($args);
|
2017-08-18 15:07:59 +02:00
|
|
|
$self->init($args);
|
2016-10-25 22:38:29 +02:00
|
|
|
|
|
|
|
# Check if main handler initialization has been done
|
2017-08-18 15:07:59 +02:00
|
|
|
unless ( %{ $self->api->tsv } ) {
|
2016-10-25 22:38:29 +02:00
|
|
|
eval { $self->api->checkConf() };
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->error($@) if ($@);
|
2016-10-25 22:38:29 +02:00
|
|
|
}
|
|
|
|
return sub {
|
|
|
|
my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
|
|
|
|
$self->api->reload($req);
|
2019-08-28 00:36:18 +02:00
|
|
|
return [ 200, [ $req->spliceHdrs ], [ $req->{respBody} ] ];
|
2016-10-25 22:38:29 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-10-08 11:06:32 +02:00
|
|
|
sub _logAuthTrace {
|
|
|
|
my ( $self, $req, $noCall ) = @_;
|
|
|
|
|
|
|
|
# register the request object to the logging system
|
|
|
|
if ( ref( $self->logger ) and $self->logger->can('setRequestObj') ) {
|
|
|
|
$self->logger->setRequestObj($req);
|
|
|
|
}
|
2022-02-16 17:43:29 +01:00
|
|
|
if ( ref( $self->userLogger ) and $self->userLogger->can('setRequestObj') )
|
|
|
|
{
|
2021-10-08 11:06:32 +02:00
|
|
|
$self->userLogger->setRequestObj($req);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Call the handler
|
|
|
|
my $res = $self->_authAndTrace( $req, $noCall );
|
|
|
|
|
|
|
|
# Clear the logging system before the next request
|
|
|
|
if ( ref( $self->logger ) and $self->logger->can('clearRequestObj') ) {
|
|
|
|
$self->logger->clearRequestObj($req);
|
|
|
|
}
|
2022-02-16 17:43:29 +01:00
|
|
|
if ( ref( $self->userLogger )
|
|
|
|
and $self->userLogger->can('clearRequestObj') )
|
|
|
|
{
|
2021-10-08 11:06:32 +02:00
|
|
|
$self->userLogger->clearRequestObj($req);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
2016-01-30 13:26:14 +01:00
|
|
|
## @method private PSGI-Response _authAndTrace($req)
|
2016-03-17 23:19:44 +01:00
|
|
|
# Launch $self->api::run() and then handler() if
|
2016-01-30 13:26:14 +01:00
|
|
|
# response is 200.
|
2016-01-26 07:14:54 +01:00
|
|
|
sub _authAndTrace {
|
2016-04-05 13:12:53 +02:00
|
|
|
my ( $self, $req, $noCall ) = @_;
|
2017-02-08 23:18:52 +01:00
|
|
|
|
|
|
|
# TODO: handle types
|
2017-02-11 17:19:25 +01:00
|
|
|
my $type = $self->api->checkType($req);
|
|
|
|
if ( my $t = $req->env->{VHOSTTYPE} ) {
|
|
|
|
$type = $t;
|
|
|
|
}
|
|
|
|
my $tmp = $self->api;
|
|
|
|
$tmp =~ s/::\w+$/::/;
|
|
|
|
$type = $tmp . $type;
|
|
|
|
eval "require $type";
|
|
|
|
die $@ if ($@);
|
2017-02-11 08:47:22 +01:00
|
|
|
my ( $res, $session ) = $type->run( $req, $self->{rule} );
|
2022-02-01 12:49:34 +01:00
|
|
|
eval { $self->portal( $type->tsv->{portal}->() ) } unless $self->portal;
|
2019-09-02 23:00:51 +02:00
|
|
|
$self->logger->warn($@) if $@;
|
2016-08-02 15:52:29 +02:00
|
|
|
$req->userData($session) if ($session);
|
2016-01-26 07:14:54 +01:00
|
|
|
|
|
|
|
if ( $res < 300 ) {
|
2016-04-05 13:12:53 +02:00
|
|
|
if ($noCall) {
|
2019-08-28 00:36:18 +02:00
|
|
|
return [ $res, [ $req->spliceHdrs ], [] ];
|
2016-04-05 13:12:53 +02:00
|
|
|
}
|
|
|
|
else {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug('User authenticated, calling handler()');
|
2021-10-08 11:06:32 +02:00
|
|
|
$res = $self->_logAndHandle($req);
|
2019-08-28 00:36:18 +02:00
|
|
|
push @{ $res->[1] }, $req->spliceHdrs;
|
|
|
|
return $res;
|
2016-04-05 13:12:53 +02:00
|
|
|
}
|
2016-01-26 07:14:54 +01:00
|
|
|
}
|
2016-12-16 17:21:20 +01:00
|
|
|
elsif ( $res < 400 ) {
|
2020-04-08 11:38:00 +02:00
|
|
|
if ( $req->wantJSON ) {
|
|
|
|
my %h = ( $req->spliceHdrs );
|
|
|
|
my $host = $req->env->{HTTP_HOST};
|
|
|
|
if ( $h{Location}
|
|
|
|
and $h{Location} =~ m#^\Q$self->{portal}\E#
|
|
|
|
and $h{Location} !~ m#^https?://$host# )
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
401, [ 'WWW-Authenticate' => 'SSO ' . $self->{portal} ], []
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
2019-08-28 00:36:18 +02:00
|
|
|
return [ $res, [ $req->spliceHdrs ], [] ];
|
2016-12-16 17:21:20 +01:00
|
|
|
}
|
2016-01-26 07:14:54 +01:00
|
|
|
else {
|
2019-09-02 23:00:51 +02:00
|
|
|
my $s = ( $self->portal ? $self->portal . "/lmerror/$res" : '' );
|
2016-01-26 07:14:54 +01:00
|
|
|
$s =
|
|
|
|
'<html><head><title>Redirection</title></head><body>'
|
|
|
|
. qq{<script type="text/javascript">window.location='$s'</script>}
|
|
|
|
. '<h1>Please wait</h1>'
|
|
|
|
. qq{<p>An error occurs, you're going to be redirected to <a href="$s">$s</a>.</p>}
|
|
|
|
. '</body></html>';
|
2019-08-28 00:36:18 +02:00
|
|
|
return [
|
|
|
|
$res,
|
|
|
|
[
|
|
|
|
$req->spliceHdrs,
|
|
|
|
'Content-Type' => 'text/html',
|
|
|
|
'Content-Length' => length $s
|
|
|
|
],
|
|
|
|
[$s]
|
|
|
|
];
|
2016-01-26 07:14:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-25 19:03:46 +01:00
|
|
|
## @method hashRef user()
|
2018-07-05 22:56:16 +02:00
|
|
|
# @return hash of user data
|
2016-01-25 19:03:46 +01:00
|
|
|
sub user {
|
|
|
|
my ( $self, $req ) = @_;
|
2016-07-25 11:49:55 +02:00
|
|
|
return $req->userData
|
|
|
|
|| { $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|
|
|
|
|| _whatToTrace => 'anonymous' };
|
2016-01-25 19:03:46 +01:00
|
|
|
}
|
|
|
|
|
2019-08-16 15:22:06 +02:00
|
|
|
## @method hashRef custom()
|
|
|
|
# @return hash of custom data
|
|
|
|
sub custom {
|
|
|
|
my ( $self, $req ) = @_;
|
|
|
|
return { $Lemonldap::NG::Handler::Main::tsv->{customToTrace} };
|
|
|
|
}
|
|
|
|
|
2016-01-25 19:03:46 +01:00
|
|
|
## @method string userId()
|
|
|
|
# @return user identifier to log
|
|
|
|
sub userId {
|
|
|
|
my ( $self, $req ) = @_;
|
2020-11-08 13:14:41 +01:00
|
|
|
my $userId =
|
|
|
|
$req->userData->{ $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|
2016-07-25 11:49:55 +02:00
|
|
|
|| '_whatToTrace' }
|
2022-02-01 12:49:34 +01:00
|
|
|
|| $req->userData->{'_user'} # Fix 2377
|
2016-07-25 11:49:55 +02:00
|
|
|
|| 'anonymous';
|
2020-11-08 13:14:41 +01:00
|
|
|
|
|
|
|
$self->logger->debug("Returned userId: $userId");
|
|
|
|
return $userId;
|
2016-01-25 19:03:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean group(string group)
|
|
|
|
# @param $group name of the Lemonldap::NG group to test
|
|
|
|
# @return boolean : true if user is in this group
|
|
|
|
sub group {
|
|
|
|
my ( $self, $req, $group ) = @_;
|
|
|
|
return () unless ( $req->userData->{groups} );
|
|
|
|
return ( $req->userData->{groups} =~ /\b$group\b/ );
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method PSGI::Response sendError($req,$err,$code)
|
2019-08-19 17:21:44 +02:00
|
|
|
# Add user id to $err before calling Lemonldap::NG::Common::PSGI::sendError()
|
2016-01-25 19:03:46 +01:00
|
|
|
# @param $req Lemonldap::NG::Common::PSGI::Request
|
|
|
|
# @param $err String to push
|
|
|
|
# @code int HTTP error code (default to 500)
|
|
|
|
sub sendError {
|
|
|
|
my ( $self, $req, $err, $code ) = @_;
|
|
|
|
$err ||= $req->error;
|
2018-03-18 14:26:45 +01:00
|
|
|
$self->userLogger->warn( '[' . $self->userId($req) . "] $err" );
|
2016-01-25 19:03:46 +01:00
|
|
|
return $self->Lemonldap::NG::Common::PSGI::sendError( $req, $err, $code );
|
|
|
|
}
|
2016-01-30 13:26:14 +01:00
|
|
|
|
2016-01-25 19:03:46 +01:00
|
|
|
1;
|