lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm

644 lines
20 KiB
Perl
Raw Normal View History

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
2022-02-01 16:33:08 +01:00
our $VERSION = '2.0.14';
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;
use Lemonldap::NG::Common::Util qw(getSameSite);
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' );
has _sfEngine => ( is => 'rw' );
has _captcha => ( 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
2019-07-02 20:03:40 +02:00
has trustedDomainsRe => ( is => 'rw' );
has additionalTrustedDomains => ( is => 'rw', default => sub { [] } );
2016-04-03 18:27:22 +02:00
2016-03-29 23:09:55 +02:00
# Lists to store plugins entry-points
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
2020-10-29 14:36:07 +01:00
# Issuer hooks
has 'hook' => ( is => 'rw', default => sub { {} } );
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 { {} } );
2019-05-31 17:00:39 +02:00
# Content-Security-Policy headers
has csp => ( is => 'rw' );
2019-05-31 17:00:39 +02:00
# Cross-Origine Resource Sharing headers
has cors => ( is => 'rw' );
# Cookie SameSite value
has cookieSameSite => ( is => 'rw' );
# Plugins may declare the session data they want to store in login history here
has pluginSessionDataToRemember =>
( is => 'rw', isa => "HashRef", default => sub { {} } );
2016-06-09 20:40:20 +02:00
# INITIALIZATION
2016-03-24 07:23:40 +01:00
sub init {
my ( $self, $args ) = @_;
$args ||= {};
my $confAcc = Lemonldap::NG::Common::Conf->new( $args->{configStorage} );
unless ($confAcc) {
die( 'Could not read configuration: '
. $Lemonldap::NG::Common::Conf::msg );
}
$self->localConfig( { %{ $confAcc->getLocalConf('portal') }, %$args } );
2019-02-09 20:48:46 +01:00
2019-03-19 15:38:36 +01:00
# Load override messages from lemonldap-ng.ini
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( {} );
2022-02-16 17:43:29 +01:00
$self->afterSub( {} );
$self->aroundSub( {} );
$self->hook( {} );
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
2020-03-05 22:14:02 +01:00
unless ( $self->SUPER::init( $self->localConfig ) ) {
$self->logger->error( 'Initialization failed: ' . $self->error );
$self->error(
"Initialization failed! Enable debug logs, reload your web server and catch main error..."
);
return 0;
}
if ( $self->error ) {
$self->logger->error( $self->error );
return 0;
}
2016-03-24 07:23:40 +01:00
2020-04-22 21:46:30 +02:00
# Default routes must point to routines declared above
$self->defaultAuthRoute('');
$self->defaultUnauthRoute('');
return 1;
}
sub setPortalRoutes {
2020-04-22 22:09:15 +02:00
my ($self) = @_;
2021-10-26 23:17:27 +02:00
$self->authRoutes( {
GET => {},
POST => {},
PUT => {},
PATCH => {},
DELETE => {},
OPTIONS => {}
}
);
$self->unAuthRoutes( {
GET => {},
POST => {},
PUT => {},
PATCH => {},
DELETE => {},
OPTIONS => {}
}
);
2016-03-31 07:27:59 +02:00
$self
2016-03-30 21:51:12 +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
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
2017-02-21 06:38:59 +01:00
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
2017-03-16 12:38:52 +01:00
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
2016-04-18 22:23:40 +02:00
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
->addAuthRoute( '*' => 'corsPreflight', ['OPTIONS'] )
2019-09-16 12:11:50 +02:00
->addUnauthRoute( '*' => 'corsPreflight', ['OPTIONS'] )
# Logout
->addAuthRoute( logout => 'logout', ['GET'] )
->addUnauthRoute( logout => 'unauthLogout', ['GET'] );
2020-04-22 22:09:15 +02:00
# Default routes must point to routines declared above
$self->defaultAuthRoute('');
$self->defaultUnauthRoute('');
return 1;
2016-03-30 07:47:38 +02:00
}
sub reloadConf {
my ( $self, $conf ) = @_;
2016-03-30 07:47:38 +02:00
2020-04-22 21:46:30 +02:00
# Handle requests (other path may be declared in enabled plugins)
$self->setPortalRoutes;
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
foreach ( qw(_macros _groups), @entryPoints ) {
2016-04-01 07:24:27 +02:00
$self->{$_} = [];
}
2022-02-16 17:43:29 +01:00
$self->afterSub( {} );
$self->aroundSub( {} );
2022-02-16 17:43:29 +01:00
$self->spRules( {} );
$self->hook( {} );
2016-04-01 07:24:27 +02:00
# Plugin history fields
$self->pluginSessionDataToRemember( {} );
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
2019-05-31 17:00:39 +02:00
# Initialize content-security-policy headers
my $csp = '';
2019-03-26 09:56:46 +01:00
foreach (qw(default img src style font connect script)) {
my $prm = $self->conf->{ 'csp' . ucfirst($_) };
$csp .= "$_-src $prm;" if ($prm);
}
$self->csp($csp);
2019-05-31 17:00:39 +02:00
$self->logger->debug( "Initialized CSP headers : " . $self->csp );
# Initialize Cross-Origin Resource Sharing headers
my $cors = '';
foreach (
qw(Allow_Origin Allow_Credentials Allow_Headers Allow_Methods Expose_Headers Max_Age)
)
{
my $header = $_;
my $prm = $self->conf->{ 'cors' . $_ };
2020-05-04 16:42:41 +02:00
if ( $header and $prm ) {
$header =~ s/_/-/;
$prm =~ s/\s+//;
$cors .= "Access-Control-$header;$prm;";
}
2019-05-31 17:00:39 +02:00
}
$self->cors($cors);
$self->logger->debug( "Initialized CORS headers : " . $self->cors );
2016-04-06 07:16:47 +02:00
# Initialize templateDir
$self->{templateDir} =
2020-11-21 18:19:10 +01:00
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin}
if ( $self->conf->{templateDir} and $self->conf->{portalSkin} );
unless ( -d $self->{templateDir} ) {
$self->error("Template dir $self->{templateDir} doesn't exist");
return $self->fail;
}
$self->templateDir(
[ $self->{templateDir}, $self->conf->{templateDir} . '/bootstrap' ] );
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};
$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
# Initialize cookie SameSite value
$self->cookieSameSite( getSameSite( $self->conf ) );
$self->logger->debug(
"Cookies will use SameSite=" . $self->cookieSameSite );
# Load menu
# ---------
$self->menu( $self->loadPlugin('::Main::Menu') );
$self->displayInit;
2016-03-30 07:47:38 +02:00
# Load authentication/userDB
# --------------------------
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
}
$mod = $self->conf->{$type}
unless ( $self->conf->{$type} eq 'Same' );
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
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
unless $self->{_sfEngine} =
$self->loadPlugin( $self->conf->{'sfEngine'} );
2018-03-08 16:33:34 +01:00
# Load Captcha module
return $self->fail
unless $self->_captcha(
$self->loadPlugin(
$self->conf->{'captcha'} || '::Captcha::SecurityImage'
)
);
# 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} } ) {
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 );
}
}
}
}
$self->{_jsRedirect} =
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
or $self->logger->error(
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
# Load plugins
foreach my $plugin ( $self->enabledPlugins ) {
$self->loadPlugin($plugin) or return $self->fail;
}
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)
$_ =~
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
}
}
foreach ( @{ $self->{additionalTrustedDomains} },
$self->conf->{portal} )
{
my $p = $_;
$p =~ s#https?://([^/]*).*$#$1#;
$re->add( quotemeta($p) );
}
2021-06-03 15:27:47 +02:00
2016-04-03 10:44:58 +02:00
foreach my $vhost ( keys %{ $self->conf->{locationRules} } ) {
2021-06-03 15:27:47 +02:00
my $expr = quotemeta($vhost);
# Handle wildcards
if ( $vhost =~ /[\%\*]/ ) {
2021-06-24 19:02:17 +02:00
$expr =~ s/\\\*/[A-Za-z0-9\.]\*/;
$expr =~ s/\\\%/[A-Za-z0-9]\*/;
2021-06-03 15:27:47 +02:00
}
$re->add($expr);
2017-02-15 07:41:50 +01:00
$self->logger->debug("Vhost $vhost added in trusted domains");
$self->conf->{vhostOptions} ||= {};
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) );
}
}
}
2021-06-25 08:59:36 +02:00
my $tmp = '^https?://' . $re->as_string . '(?::\d+)?(?:/|$)';
2016-05-23 13:53:09 +02:00
$self->trustedDomainsRe(qr/$tmp/);
2016-03-30 07:47:38 +02:00
}
2016-03-29 23:09:55 +02:00
# Clean $req->pdata after authentication
push @{ $self->endAuth }, sub {
my $tmp = $_[0]->pdata->{keepPdata} //= [];
foreach my $k ( keys %{ $_[0]->pdata } ) {
unless ( grep { $_ eq $k } @$tmp ) {
$self->logger->debug("Removing $k from pdata");
delete $_[0]->pdata->{$k};
}
}
my $user_log = $_[0]->{sessionInfo}->{ $self->conf->{whatToTrace} };
$self->userLogger->notice( $user_log . ' connected' ) if $user_log;
if (@$tmp) {
$self->logger->debug(
'Add ' . join( ',', @$tmp ) . ' in keepPdata' );
$_[0]->pdata->{keepPdata} = $tmp;
}
return PE_OK;
};
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;
}
};
my $portal = $self->conf->{portal};
$portal =~ s#^https?://(.*?)(?:[:/].*)?$#$1#;
2019-03-07 18:22:16 +01:00
HANDLER->tsv->{defaultCondition}->{$portal} ||= sub { 1 };
2016-03-24 07:23:40 +01:00
1;
}
# 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
unless ( $obj = $self->loadModule("$plugin") );
return $self->findEP( $plugin, $obj );
}
# Insert declared entry points into corresponding arrays
sub findEP {
my ( $self, $plugin, $obj ) = @_;
# Standards entry points
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 ) {
2019-07-02 20:03:40 +02:00
my $callback = $h->{$ep};
2018-12-13 06:57:10 +01:00
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
};
}
}
}
2020-10-29 14:36:07 +01:00
if ( $obj->can('hook') ) {
$self->logger->debug("Found hook in $plugin");
my $h = $obj->hook;
unless ( ref $h and ref($h) eq 'HASH' ) {
$self->logger->error('"hook" endpoint must be a hashref, skipped');
}
else {
foreach my $hookname ( keys %$h ) {
my $callback = $h->{$hookname};
push @{ $self->hook->{$hookname} }, sub {
eval {
$obj->logger->debug(
"Launching ${plugin}::$callback on hook $hookname");
};
$obj->$callback(@_);
};
}
}
}
2021-07-27 10:40:28 +02:00
$self->logger->debug("Plugin $plugin initialized");
# Rules for menu
if ( $obj->can('spRules') ) {
foreach my $k ( keys %{ $obj->{spRules} } ) {
$self->logger->info(
2021-07-27 10:40:28 +02:00
"$k is defined more than one time, it can have some bad effects on Menu display"
) if ( $self->spRules->{$k} );
$self->spRules->{$k} = $obj->{spRules}->{$k};
}
}
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 ) = @_;
$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 ($@) {
2021-12-19 16:54:54 +01:00
$self->logger->error("Unable to build $module object: $@");
2016-03-28 09:46:02 +02:00
return 0;
}
unless ($obj) {
2021-12-19 16:54:54 +01:00
$self->logger->error("$module new() method returned undef");
return 0;
}
if ( $obj->can("init") and ( !$obj->init ) ) {
2021-12-19 16:54:54 +01:00
$self->logger->error("$module init failed");
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 ) = @_;
return $self->sendError( $req,
'Portal error, contact your administrator', 500 );
2017-01-15 23:04:33 +01:00
}
# This helper method builds a rule from a string expression
# - $rule: rule text
# - $ruleDesc optional hint of what the rule is for, to display in error message
# returns undef if the rule syntax was invalid
sub buildRule {
my ( $self, $rule, $ruleDesc ) = @_;
if ($ruleDesc) {
$ruleDesc = " $ruleDesc ";
}
else {
$ruleDesc = " ";
}
my $compiledRule =
$self->HANDLER->buildSub( $self->HANDLER->substitute($rule) );
unless ($compiledRule) {
2022-03-03 23:29:48 +01:00
my $error = $self->HANDLER->tsv->{jail}->error || 'Unable to compile rule';
$self->logger->error( "Bad" . $ruleDesc . "rule: " . $error );
}
return $compiledRule,;
}
2016-03-24 07:23:40 +01:00
1;