2016-03-28 09:46:05 +02:00
|
|
|
##@class Lemonldap::NG::Portal::Main::Init
|
2016-03-29 23:09:55 +02:00
|
|
|
# Initialization part of Lemonldap::NG portal
|
2016-03-30 21:51:12 +02:00
|
|
|
#
|
2016-04-03 10:44:58 +02:00
|
|
|
# 2 public methods:
|
2016-03-30 21:51:12 +02:00
|
|
|
# - init(): launch at startup. Load 'portal' section of lemonldap-ng.ini,
|
|
|
|
# initialize default route and launch reloadConf()
|
|
|
|
# - reloadConf(): (re)load configuration using localConf (ie 'portal' section
|
|
|
|
# of lemonldap-ng.ini) and underlying handler configuration
|
2016-03-28 09:46:05 +02:00
|
|
|
package Lemonldap::NG::Portal::Main::Init;
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2019-01-09 13:42:30 +01:00
|
|
|
our $VERSION = '2.0.2';
|
2016-04-07 23:31:56 +02:00
|
|
|
|
|
|
|
package Lemonldap::NG::Portal::Main;
|
|
|
|
|
2016-03-24 07:23:40 +01:00
|
|
|
use strict;
|
|
|
|
use Mouse;
|
2016-04-03 18:27:22 +02:00
|
|
|
use Regexp::Assemble;
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2016-06-09 20:40:20 +02:00
|
|
|
# PROPERTIES
|
|
|
|
|
2016-03-28 09:46:02 +02:00
|
|
|
# Configuration storage
|
2016-03-27 20:10:36 +02:00
|
|
|
has localConfig => ( is => 'rw', default => sub { {} } );
|
|
|
|
has conf => ( is => 'rw', default => sub { {} } );
|
2016-04-13 23:06:04 +02:00
|
|
|
has menu => ( is => 'rw', default => sub { {} } );
|
2018-10-17 10:49:00 +02:00
|
|
|
has trOver => ( is => 'rw', default => sub { { all => {} } } );
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2016-03-28 09:46:02 +02:00
|
|
|
# Sub modules
|
|
|
|
has _authentication => ( is => 'rw' );
|
|
|
|
has _userDB => ( is => 'rw' );
|
2016-07-20 22:47:43 +02:00
|
|
|
has _passwordDB => ( is => 'rw' );
|
2018-03-08 20:36:32 +01:00
|
|
|
has _sfEngine => ( is => 'rw' );
|
2016-03-28 09:46:02 +02:00
|
|
|
|
2016-11-16 11:30:27 +01:00
|
|
|
has loadedModules => ( is => 'rw' );
|
|
|
|
|
2016-04-01 07:24:27 +02:00
|
|
|
# Macros and groups
|
2016-05-26 23:26:47 +02:00
|
|
|
has _macros => ( is => 'rw' );
|
|
|
|
has _groups => ( is => 'rw' );
|
|
|
|
has _jsRedirect => ( is => 'rw' );
|
2016-04-01 07:24:27 +02:00
|
|
|
|
2016-04-03 18:27:22 +02:00
|
|
|
# TrustedDomain regexp
|
2016-05-23 13:53:09 +02:00
|
|
|
has trustedDomainsRe => ( is => 'rw' );
|
2016-04-03 18:27:22 +02:00
|
|
|
|
2016-03-29 23:09:55 +02:00
|
|
|
# Lists to store plugins entry-points
|
2018-09-05 09:19:01 +02:00
|
|
|
my @entryPoints;
|
|
|
|
|
|
|
|
BEGIN {
|
|
|
|
@entryPoints = (
|
|
|
|
|
|
|
|
# Auth process entrypoints
|
|
|
|
qw(beforeAuth betweenAuthAndData afterData endAuth),
|
|
|
|
|
|
|
|
# Authenticated users entrypoint
|
|
|
|
'forAuthUser',
|
|
|
|
|
|
|
|
# Logout entrypoint
|
|
|
|
'beforeLogout',
|
|
|
|
|
|
|
|
# Special endpoint
|
|
|
|
'authCancel', # Clean pdata when user click on "cancel"
|
|
|
|
);
|
|
|
|
|
|
|
|
foreach (@entryPoints) {
|
|
|
|
has $_ => (
|
|
|
|
is => 'rw',
|
|
|
|
isa => 'ArrayRef',
|
|
|
|
default => sub { [] }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2016-03-28 09:46:02 +02:00
|
|
|
|
2018-12-12 22:09:27 +01:00
|
|
|
# Endpoints inserted after any main sub
|
2018-12-13 06:57:10 +01:00
|
|
|
has 'afterSub' => ( is => 'rw', default => sub { {} } );
|
|
|
|
has 'aroundSub' => ( is => 'rw', default => sub { {} } );
|
2018-12-12 22:09:27 +01:00
|
|
|
|
2017-11-11 21:16:52 +01:00
|
|
|
has spRules => (
|
|
|
|
is => 'rw',
|
|
|
|
default => sub { {} }
|
|
|
|
);
|
|
|
|
|
2016-05-23 23:52:29 +02:00
|
|
|
# Custom template parameters
|
|
|
|
has customParameters => ( is => 'rw', default => sub { {} } );
|
|
|
|
|
2017-01-18 23:36:03 +01:00
|
|
|
# Content-Security-Policy header
|
|
|
|
has csp => ( is => 'rw' );
|
|
|
|
|
2016-06-09 20:40:20 +02:00
|
|
|
# INITIALIZATION
|
|
|
|
|
2016-03-24 07:23:40 +01:00
|
|
|
sub init {
|
|
|
|
my ( $self, $args ) = @_;
|
|
|
|
$args ||= {};
|
2016-04-06 22:10:01 +02:00
|
|
|
$self->localConfig(
|
2018-10-12 10:04:03 +02:00
|
|
|
{
|
|
|
|
%{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
|
|
|
|
->getLocalConf('portal')
|
2016-04-06 22:10:01 +02:00
|
|
|
},
|
|
|
|
%$args
|
|
|
|
}
|
|
|
|
);
|
2016-05-24 07:05:51 +02:00
|
|
|
foreach my $k ( keys %{ $self->localConfig } ) {
|
2016-05-23 23:52:29 +02:00
|
|
|
if ( $k =~ /tpl_(.*)/ ) {
|
|
|
|
$self->customParameters->{$1} = $self->localConfig->{$k};
|
|
|
|
}
|
2018-10-17 10:49:00 +02:00
|
|
|
elsif ( $k =~ /error_(?:(\w+?)_)?(\d+)$/ ) {
|
|
|
|
my $lang = $1 || 'all';
|
|
|
|
$self->trOver->{$lang}->{"PE$2"} = $self->localConfig->{$k};
|
|
|
|
}
|
|
|
|
elsif ( $k =~ /msg_(?:(\w+?)_)?(\w+)$/ ) {
|
|
|
|
my $lang = $1 || 'all';
|
|
|
|
$self->trOver->{$lang}->{$2} = $self->localConfig->{$k};
|
|
|
|
}
|
2016-05-23 23:52:29 +02:00
|
|
|
}
|
2018-10-17 10:49:00 +02:00
|
|
|
$self->trOver( JSON::to_json( $self->trOver ) );
|
2016-11-16 11:30:27 +01:00
|
|
|
|
|
|
|
# Purge loaded module list
|
|
|
|
$self->loadedModules( {} );
|
2018-06-25 07:07:13 +02:00
|
|
|
|
|
|
|
# Insert `reloadConf` in handler reload stack
|
2017-02-08 23:18:52 +01:00
|
|
|
Lemonldap::NG::Handler::Main->onReload( $self, 'reloadConf' );
|
2018-06-25 07:07:13 +02:00
|
|
|
|
|
|
|
# Handler::PSGI::Try initialization
|
2017-02-15 07:41:50 +01:00
|
|
|
return 0 unless ( $self->SUPER::init( $self->localConfig ) );
|
2018-12-21 11:24:34 +01:00
|
|
|
if ( $self->error ) {
|
|
|
|
$self->logger->error( $self->error );
|
|
|
|
return 0;
|
|
|
|
}
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2016-03-31 07:27:59 +02:00
|
|
|
# Handle requests (other path may be declared in enabled plugins)
|
|
|
|
$self
|
2016-03-30 21:51:12 +02:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# "/" or undeclared paths
|
|
|
|
->addUnauthRoute( '*' => 'login', ['GET'] )
|
|
|
|
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
|
|
|
|
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
|
|
|
|
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
|
2016-03-31 07:27:59 +02:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# psgi.js
|
|
|
|
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
|
|
|
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
2017-02-21 06:38:59 +01:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# portal.css
|
|
|
|
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
|
|
|
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
2017-03-16 12:38:52 +01:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# lmerror
|
|
|
|
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
|
|
|
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
2017-03-16 22:33:13 +01:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# Core REST API
|
|
|
|
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
|
|
|
|
->addAuthRoute( ping => 'authenticated', ['GET'] )
|
2016-04-18 22:23:40 +02:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# Refresh session
|
|
|
|
->addAuthRoute( refresh => 'refresh', ['GET'] )
|
2017-02-16 17:11:12 +01:00
|
|
|
|
2018-10-12 10:04:03 +02:00
|
|
|
# Logout
|
|
|
|
->addAuthRoute( logout => 'logout', ['GET'] );
|
2016-03-31 07:27:59 +02:00
|
|
|
|
2016-04-01 07:24:27 +02:00
|
|
|
# Default routes must point to routines declared above
|
|
|
|
$self->defaultAuthRoute('');
|
2016-03-30 21:51:12 +02:00
|
|
|
$self->defaultUnauthRoute('');
|
2018-06-14 22:30:22 +02:00
|
|
|
return 1;
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sub reloadConf {
|
2016-04-04 23:22:25 +02:00
|
|
|
my ( $self, $conf ) = @_;
|
2016-03-30 07:47:38 +02:00
|
|
|
|
2016-04-06 07:16:47 +02:00
|
|
|
# Reinitialize $self->conf
|
|
|
|
%{ $self->{conf} } = %{ $self->localConfig };
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2016-04-01 07:24:27 +02:00
|
|
|
# Reinitialize arrays
|
2018-09-12 23:14:35 +02:00
|
|
|
foreach ( qw(_macros _groups), @entryPoints ) {
|
2016-04-01 07:24:27 +02:00
|
|
|
$self->{$_} = [];
|
|
|
|
}
|
2017-11-12 07:41:42 +01:00
|
|
|
$self->spRules( {} );
|
2016-04-01 07:24:27 +02:00
|
|
|
|
2016-03-30 07:47:38 +02:00
|
|
|
# Load conf in portal object
|
|
|
|
foreach my $key ( keys %$conf ) {
|
2016-04-06 07:16:47 +02:00
|
|
|
$self->{conf}->{$key} ||= $conf->{$key};
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2017-01-18 23:36:03 +01:00
|
|
|
# Initialize content-security-policy header
|
|
|
|
my $csp = '';
|
|
|
|
foreach (qw(default img src style font connect)) {
|
|
|
|
my $prm = $self->conf->{ 'csp' . ucfirst($_) };
|
2017-02-06 00:04:28 +01:00
|
|
|
$csp .= "$_-src $prm;" if ($prm);
|
2017-01-18 23:36:03 +01:00
|
|
|
}
|
|
|
|
$self->csp($csp);
|
|
|
|
|
2016-04-06 07:16:47 +02:00
|
|
|
# Initialize templateDir
|
2018-10-12 10:04:03 +02:00
|
|
|
$self->{templateDir} =
|
|
|
|
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
|
2017-12-20 22:48:12 +01:00
|
|
|
unless ( -d $self->{templateDir} ) {
|
|
|
|
$self->error("Template dir $self->{templateDir} doesn't exist");
|
|
|
|
return $self->fail;
|
|
|
|
}
|
2016-04-06 07:16:47 +02:00
|
|
|
|
2016-12-30 08:03:48 +01:00
|
|
|
$self->{staticPrefix} = $self->conf->{staticPrefix} || '/static';
|
2016-05-23 13:53:09 +02:00
|
|
|
$self->{languages} = $self->conf->{languages} || '/';
|
2016-04-13 07:32:10 +02:00
|
|
|
|
2016-03-30 07:47:38 +02:00
|
|
|
# Initialize session DBs
|
|
|
|
unless ( $self->conf->{globalStorage} ) {
|
|
|
|
$self->error(
|
|
|
|
'globalStorage not defined (perhaps configuration can not be read)'
|
|
|
|
);
|
2017-01-05 16:19:57 +01:00
|
|
|
return $self->fail;
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2016-04-05 22:46:11 +02:00
|
|
|
# Initialize persistent session DB
|
|
|
|
unless ( $self->conf->{persistentStorage} ) {
|
|
|
|
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
|
2018-10-12 10:04:03 +02:00
|
|
|
$self->conf->{persistentStorageOptions} =
|
|
|
|
$self->conf->{globalStorageOptions};
|
2016-04-05 22:46:11 +02:00
|
|
|
}
|
|
|
|
|
2016-03-30 07:47:38 +02:00
|
|
|
# Initialize cookie domain
|
|
|
|
unless ( $self->conf->{domain} ) {
|
|
|
|
$self->error('Configuration error: no domain');
|
2017-01-05 16:19:57 +01:00
|
|
|
return $self->fail;
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
|
|
|
$self->conf->{domain} =~ s/^([^\.])/.$1/;
|
2016-03-24 07:23:40 +01:00
|
|
|
|
2018-03-15 07:04:52 +01:00
|
|
|
# Load menu
|
|
|
|
# ---------
|
|
|
|
$self->menu( $self->loadPlugin('::Main::Menu') );
|
|
|
|
$self->displayInit;
|
|
|
|
|
2016-03-30 07:47:38 +02:00
|
|
|
# Load authentication/userDB
|
|
|
|
# --------------------------
|
2017-02-05 10:13:20 +01:00
|
|
|
my $mod;
|
2016-03-30 07:47:38 +02:00
|
|
|
for my $type (qw(authentication userDB)) {
|
|
|
|
unless ( $self->conf->{$type} ) {
|
|
|
|
$self->error("$type is not set");
|
2017-01-05 16:19:57 +01:00
|
|
|
return $self->fail;
|
2016-03-24 07:23:40 +01:00
|
|
|
}
|
2018-09-05 09:19:01 +02:00
|
|
|
$mod = $self->conf->{$type}
|
2018-10-12 10:04:03 +02:00
|
|
|
unless ( $self->conf->{$type} eq 'Same' );
|
2017-02-05 10:13:20 +01:00
|
|
|
my $module = '::' . ucfirst($type) . '::' . $mod;
|
2016-04-01 12:46:12 +02:00
|
|
|
$module =~ s/Authentication/Auth/;
|
2016-03-30 07:47:38 +02:00
|
|
|
|
|
|
|
# Launch and initialize module
|
2017-01-05 16:19:57 +01:00
|
|
|
return $self->fail
|
2018-10-12 10:04:03 +02:00
|
|
|
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
2016-03-29 23:09:55 +02:00
|
|
|
|
2018-03-08 16:33:34 +01:00
|
|
|
# Load second-factor engine
|
|
|
|
return $self->fail
|
2018-10-12 10:04:03 +02:00
|
|
|
unless $self->{_sfEngine} =
|
|
|
|
$self->loadPlugin( $self->conf->{'sfEngine'} );
|
2018-03-08 16:33:34 +01:00
|
|
|
|
2016-04-03 10:44:58 +02:00
|
|
|
# Initialize trusted domain regexp
|
2016-04-03 18:27:22 +02:00
|
|
|
if ( $self->conf->{trustedDomains}
|
|
|
|
and $self->conf->{trustedDomains} =~ /^\s*\*\s*$/ )
|
|
|
|
{
|
2016-05-23 13:53:09 +02:00
|
|
|
$self->trustedDomainsRe(qr#^https?://#);
|
2016-04-03 10:44:58 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
my $re = Regexp::Assemble->new();
|
|
|
|
if ( my $td = $self->conf->{trustedDomains} ) {
|
|
|
|
$td =~ s/^\s*(.*?)\s*/$1/;
|
|
|
|
foreach ( split( /\s+/, $td ) ) {
|
2016-05-23 23:52:29 +02:00
|
|
|
next unless ($td);
|
2016-04-03 10:44:58 +02:00
|
|
|
s#^\.#([^/]+\.)?#;
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug("Domain $_ added in trusted domains");
|
2016-05-23 13:53:09 +02:00
|
|
|
s/\./\\./g;
|
|
|
|
|
|
|
|
# This regexp is valid for the followings hosts:
|
|
|
|
# - $td
|
|
|
|
# - $domainlabel.$td
|
|
|
|
# $domainlabel is build looking RFC2396
|
|
|
|
# (see Regexp::Common::URI::RFC2396)
|
2018-10-12 10:04:03 +02:00
|
|
|
$_ =~
|
|
|
|
s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
|
2016-05-23 13:53:09 +02:00
|
|
|
$re->add("$_");
|
2016-04-03 10:44:58 +02:00
|
|
|
}
|
|
|
|
}
|
2017-03-20 19:14:10 +01:00
|
|
|
my $p = $self->conf->{portal};
|
|
|
|
$p =~ s#https?://([^/]*).*$#$1#;
|
|
|
|
$re->add( quotemeta($p) );
|
2016-04-03 10:44:58 +02:00
|
|
|
foreach my $vhost ( keys %{ $self->conf->{locationRules} } ) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug("Vhost $vhost added in trusted domains");
|
2016-04-03 10:44:58 +02:00
|
|
|
$re->add( quotemeta($vhost) );
|
2017-03-06 21:26:22 +01:00
|
|
|
$self->conf->{vhostOptions} ||= {};
|
2018-10-12 10:04:03 +02:00
|
|
|
if ( my $tmp =
|
|
|
|
$self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
|
2016-04-03 10:44:58 +02:00
|
|
|
{
|
|
|
|
foreach my $alias ( split /\s+/, $tmp ) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug(
|
|
|
|
"Alias $alias added in trusted domains");
|
2016-04-03 10:44:58 +02:00
|
|
|
$re->add( quotemeta($alias) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-23 13:53:09 +02:00
|
|
|
my $tmp = 'https?://' . $re->as_string . '(?::\d+)?(?:/|$)';
|
|
|
|
$self->trustedDomainsRe(qr/$tmp/);
|
2016-03-30 07:47:38 +02:00
|
|
|
}
|
2016-03-29 23:09:55 +02:00
|
|
|
|
2016-04-03 18:27:19 +02:00
|
|
|
# Compile macros in _macros, groups in _groups
|
|
|
|
foreach my $type (qw(macros groups)) {
|
|
|
|
$self->{"_$type"} = {};
|
|
|
|
if ( $self->conf->{$type} ) {
|
|
|
|
for my $name ( sort keys %{ $self->conf->{$type} } ) {
|
2018-10-12 10:04:03 +02:00
|
|
|
my $sub =
|
|
|
|
HANDLER->buildSub(
|
2016-08-02 15:52:29 +02:00
|
|
|
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
|
2016-04-03 18:27:19 +02:00
|
|
|
if ($sub) {
|
|
|
|
$self->{"_$type"}->{$name} = $sub;
|
|
|
|
}
|
|
|
|
else {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->error( "$type $name returns an error: "
|
2018-10-12 10:04:03 +02:00
|
|
|
. HANDLER->tsv->{jail}->error );
|
2016-04-03 18:27:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-12 10:04:03 +02:00
|
|
|
$self->{_jsRedirect} =
|
|
|
|
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
|
|
|
|
or $self->logger->error(
|
2017-02-15 07:41:50 +01:00
|
|
|
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
|
2016-04-01 07:24:27 +02:00
|
|
|
|
2016-03-30 07:47:38 +02:00
|
|
|
# Load plugins
|
|
|
|
foreach my $plugin ( $self->enabledPlugins ) {
|
2017-01-05 16:19:57 +01:00
|
|
|
$self->loadPlugin($plugin) or return $self->fail;
|
2016-03-24 07:23:40 +01:00
|
|
|
}
|
2016-04-13 07:32:10 +02:00
|
|
|
|
2018-07-04 17:23:57 +02:00
|
|
|
# Clean $req->pdata after authentication
|
2018-09-05 09:19:01 +02:00
|
|
|
push @{ $self->endAuth }, sub {
|
2018-07-05 18:45:29 +02:00
|
|
|
unless ( $_[0]->pdata->{keepPdata} ) {
|
2018-07-05 14:24:22 +02:00
|
|
|
$self->logger->debug('Cleaning pdata');
|
|
|
|
$_[0]->pdata( {} );
|
|
|
|
}
|
2018-07-04 17:23:57 +02:00
|
|
|
return PE_OK;
|
|
|
|
};
|
2018-07-10 07:11:08 +02:00
|
|
|
unshift @{ $self->beforeAuth }, sub {
|
|
|
|
if ( $_[0]->param('cancel') ) {
|
|
|
|
$self->logger->debug('Cancel called, push authCancel calls');
|
|
|
|
unshift @{ $_[0]->steps }, @{ $self->authCancel };
|
2018-07-10 11:43:36 +02:00
|
|
|
return PE_OK;
|
2018-07-10 07:11:08 +02:00
|
|
|
}
|
|
|
|
};
|
2018-07-04 17:23:57 +02:00
|
|
|
|
2016-03-24 07:23:40 +01:00
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
2017-11-11 17:39:24 +01:00
|
|
|
# Method used to load plugins
|
2016-03-29 23:09:55 +02:00
|
|
|
sub loadPlugin {
|
|
|
|
my ( $self, $plugin ) = @_;
|
2018-06-15 06:32:43 +02:00
|
|
|
unless ($plugin) {
|
|
|
|
require Carp;
|
|
|
|
Carp::confess('Calling loadPugin without arg !');
|
|
|
|
}
|
2016-03-29 23:09:55 +02:00
|
|
|
my $obj;
|
|
|
|
return 0
|
2018-10-12 10:04:03 +02:00
|
|
|
unless ( $obj = $self->loadModule("$plugin") );
|
2017-02-06 00:04:28 +01:00
|
|
|
return $self->findEP( $plugin, $obj );
|
|
|
|
}
|
|
|
|
|
2017-11-11 17:39:24 +01:00
|
|
|
# Insert declared entry points into corresponding arrays
|
2017-02-06 00:04:28 +01:00
|
|
|
sub findEP {
|
|
|
|
my ( $self, $plugin, $obj ) = @_;
|
2017-11-11 17:39:24 +01:00
|
|
|
|
|
|
|
# Standards entry points
|
2018-09-05 09:19:01 +02:00
|
|
|
foreach my $sub (@entryPoints) {
|
2016-03-29 23:09:55 +02:00
|
|
|
if ( $obj->can($sub) ) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug(" Found $sub entry point:");
|
2016-03-31 07:27:59 +02:00
|
|
|
if ( my $callback = $obj->$sub ) {
|
2018-06-12 17:56:42 +02:00
|
|
|
push @{ $self->{$sub} }, sub {
|
2018-06-15 06:32:43 +02:00
|
|
|
eval {
|
|
|
|
$obj->logger->debug("Launching ${plugin}::$callback");
|
|
|
|
};
|
2018-12-13 06:57:10 +01:00
|
|
|
$obj->$callback(@_);
|
2018-06-12 17:56:42 +02:00
|
|
|
};
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug(" -> $callback");
|
2016-03-31 07:27:59 +02:00
|
|
|
}
|
2016-03-29 23:09:55 +02:00
|
|
|
}
|
|
|
|
}
|
2018-12-12 22:09:27 +01:00
|
|
|
if ( $obj->can('afterSub') ) {
|
|
|
|
$self->logger->debug("Found afterSub in $plugin");
|
|
|
|
my $h = $obj->afterSub;
|
|
|
|
unless ( ref $h and ref($h) eq 'HASH' ) {
|
|
|
|
$self->logger->error(
|
|
|
|
'"afterSub" endpoint must be a hashref, skipped');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
foreach my $ep ( keys %$h ) {
|
|
|
|
my $callback = $h->{$ep};
|
|
|
|
push @{ $self->afterSub->{$ep} }, sub {
|
|
|
|
eval {
|
|
|
|
$obj->logger->debug(
|
|
|
|
"Launching ${plugin}::$callback afterSub $ep");
|
|
|
|
};
|
2018-12-13 06:57:10 +01:00
|
|
|
$obj->$callback(@_);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( $obj->can('aroundSub') ) {
|
|
|
|
$self->logger->debug("Found aroundSub in $plugin");
|
|
|
|
my $h = $obj->aroundSub;
|
|
|
|
unless ( ref $h and ref($h) eq 'HASH' ) {
|
|
|
|
$self->logger->error(
|
|
|
|
'"aroundSub" endpoint must be a hashref, skipped');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
foreach my $ep ( keys %$h ) {
|
|
|
|
my $callback = $h->{$ep};
|
|
|
|
my $previousSub = $self->aroundSub->{$ep} ||= sub {
|
|
|
|
$self->logger->debug(
|
|
|
|
"$ep launched inside ${plugin}::$callback");
|
|
|
|
$self->$ep(@_);
|
|
|
|
};
|
|
|
|
$self->aroundSub->{$ep} = sub {
|
|
|
|
$self->logger->debug(
|
|
|
|
"Launching ${plugin}::$callback instead of $ep");
|
|
|
|
$obj->$callback( $previousSub, @_ );
|
2018-12-12 22:09:27 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-11 21:16:52 +01:00
|
|
|
$self->logger->debug("Plugin $plugin initializated");
|
2017-11-11 17:39:24 +01:00
|
|
|
|
|
|
|
# Rules for menu
|
|
|
|
if ( $obj->can('spRules') ) {
|
|
|
|
foreach my $k ( keys %{ $obj->{spRules} } ) {
|
|
|
|
$self->logger->info(
|
2018-10-12 10:04:03 +02:00
|
|
|
"$k is defined more than one time, it can have some bad effect on Menu display"
|
2017-11-11 17:39:24 +01:00
|
|
|
) if ( $self->spRules->{$k} );
|
|
|
|
$self->spRules->{$k} = $obj->{spRules}->{$k};
|
|
|
|
}
|
|
|
|
}
|
2016-06-28 22:56:38 +02:00
|
|
|
return $obj;
|
2016-03-29 23:09:55 +02:00
|
|
|
}
|
|
|
|
|
2016-03-24 23:16:13 +01:00
|
|
|
sub loadModule {
|
2018-03-18 09:15:23 +01:00
|
|
|
my ( $self, $module, $conf, %args ) = @_;
|
2017-02-06 00:04:28 +01:00
|
|
|
$conf //= $self->conf;
|
2016-03-29 23:09:55 +02:00
|
|
|
my $obj;
|
2016-04-03 08:33:50 +02:00
|
|
|
$module = "Lemonldap::NG::Portal$module" if ( $module =~ /^::/ );
|
2016-03-24 23:16:13 +01:00
|
|
|
|
|
|
|
eval "require $module";
|
|
|
|
if ($@) {
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->error("$module load error: $@");
|
2016-03-24 23:16:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2016-03-28 09:46:02 +02:00
|
|
|
eval {
|
2018-03-18 09:15:23 +01:00
|
|
|
$obj = $module->new( { p => $self, conf => $conf, %args } );
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug("Module $module loaded");
|
2016-03-28 09:46:02 +02:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->error("Unable to build $module object: $@");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-03 22:31:28 +02:00
|
|
|
( $obj and $obj->init ) or return 0;
|
2016-11-16 11:30:27 +01:00
|
|
|
$self->loadedModules->{$module} = $obj;
|
2016-03-29 23:09:55 +02:00
|
|
|
return $obj;
|
2016-03-24 23:16:13 +01:00
|
|
|
}
|
|
|
|
|
2017-01-05 16:19:57 +01:00
|
|
|
sub fail {
|
2017-02-15 07:41:50 +01:00
|
|
|
$_[0]->userLogger->error( $_[0]->error );
|
2017-01-15 23:04:33 +01:00
|
|
|
$_[0]->addUnauthRoute( '*' => 'displayError' );
|
|
|
|
$_[0]->addAuthRoute( '*' => 'displayError' );
|
2017-01-05 16:19:57 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-15 23:04:33 +01:00
|
|
|
sub displayError {
|
|
|
|
my ( $self, $req ) = @_;
|
2018-09-05 09:19:01 +02:00
|
|
|
return $self->sendError( $req,
|
|
|
|
'Portal error, contact your administrator', 500 );
|
2017-01-15 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
2016-03-24 07:23:40 +01:00
|
|
|
1;
|