You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
11 KiB
391 lines
11 KiB
# Manager main component
|
|
#
|
|
# This package contains these parts:
|
|
# - Properties and private methods
|
|
# - Initialization method (launched by Lemonldap::NG::Common::PSGI)
|
|
# that declares routes
|
|
# - Upload methods (launched by Lemonldap::NG::Common::PSGI::Router)
|
|
#
|
|
# It inherits from Conf.pm to responds to display methods and from
|
|
# Sessions.pm to manage sessions
|
|
package Lemonldap::NG::Manager;
|
|
|
|
use strict;
|
|
use utf8;
|
|
use Mouse;
|
|
use JSON;
|
|
use Lemonldap::NG::Common::Conf::Constants;
|
|
use Lemonldap::NG::Common::PSGI::Constants;
|
|
|
|
our $VERSION = '2.0.11';
|
|
|
|
extends qw(
|
|
Lemonldap::NG::Handler::PSGI::Router
|
|
Lemonldap::NG::Common::Conf::AccessLib
|
|
);
|
|
|
|
has csp => ( is => 'rw' );
|
|
has loadedPlugins => ( is => 'rw', default => sub { [] } );
|
|
has hLoadedPlugins => ( is => 'rw', default => sub { {} } );
|
|
|
|
## @method boolean init($args)
|
|
# Launch initialization method
|
|
#
|
|
# @param $args hashref to merge with object
|
|
# @return 0 in case of error, 1 else
|
|
sub init {
|
|
my ( $self, $args ) = @_;
|
|
$args ||= {};
|
|
|
|
if ( my $localconf = $self->confAcc->getLocalConf(MANAGERSECTION) ) {
|
|
foreach ( keys %$localconf ) {
|
|
$args->{$_} //= $localconf->{$_};
|
|
$self->{$_} = $args->{$_} unless (/^(?:l|userL)ogger$/);
|
|
}
|
|
}
|
|
|
|
# Manager needs to keep new Ajax behaviour
|
|
$args->{noAjaxHook} = 0;
|
|
|
|
return 0
|
|
unless ( $self->Lemonldap::NG::Handler::PSGI::Router::init($args) );
|
|
|
|
# TODO: manage errors
|
|
unless ( -r $self->{templateDir} ) {
|
|
$self->error("Unable to read $self->{templateDir}");
|
|
return 0;
|
|
}
|
|
|
|
my $conf = $self->confAcc->getConf;
|
|
$conf->{$_} = $args->{$_} foreach ( keys %$args );
|
|
|
|
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, api";
|
|
my @links;
|
|
my @enabledModules =
|
|
map {
|
|
my @res = ( "Lemonldap::NG::Manager::" . ucfirst($_) );
|
|
if ( my $tmp = $self->loadPlugin( @res, $conf ) ) {
|
|
$self->logger->debug("Plugin $_ loaded");
|
|
push @links, $_;
|
|
push @{ $self->loadedPlugins }, $tmp;
|
|
$self->hLoadedPlugins->{$_} = $tmp;
|
|
}
|
|
else {
|
|
$self->logger->error("Unable to load $_, skipping");
|
|
@res = ();
|
|
}
|
|
(@res);
|
|
}
|
|
split( /[,\s]+/, $self->{enabledModules} );
|
|
unless (@enabledModules) {
|
|
$self->logger->error('No plugins loaded, aborting');
|
|
return 0;
|
|
}
|
|
unless ($conf) {
|
|
require Lemonldap::NG::Manager::Conf::Zero;
|
|
$conf = Lemonldap::NG::Manager::Conf::Zero::zeroConf();
|
|
}
|
|
|
|
# TODO: -> loadPlugin
|
|
#for ( my $i = 0 ; $i < @enabledModules ; $i++ ) {
|
|
# my $mod = $enabledModules[$i];
|
|
# no strict 'refs';
|
|
# if ( &{"${mod}::addRoutes"}( $self, $conf ) ) {
|
|
# $self->logger->debug("Module $mod enabled");
|
|
# push @working, $mod;
|
|
# }
|
|
# else {
|
|
# $links[$i] = undef;
|
|
# $self->logger->error(
|
|
# "Module $mod can not be enabled: " . $self->error );
|
|
# }
|
|
#}
|
|
$self->addRoute( links => 'links', ['GET'] );
|
|
$self->addRoute( 'psgi.js' => 'sendJs', ['GET'] );
|
|
|
|
my $portal = $conf->{portal};
|
|
$portal =~ s#https?://([^/]*).*#$1#;
|
|
$self->csp(
|
|
"default-src 'self' $portal;frame-ancestors 'none';form-action 'self';"
|
|
);
|
|
|
|
# Avoid restricted users to access configuration by default route
|
|
my $defaultMod = $self->{defaultModule} =
|
|
$self->{defaultModule} || $enabledModules[0];
|
|
$self->logger->debug("Default module -> $defaultMod");
|
|
my ($index) =
|
|
grep { $enabledModules[$_] eq $defaultMod } ( 0 .. $#enabledModules );
|
|
$index //= 0;
|
|
$self->logger->debug("Default index -> $index");
|
|
$self->defaultRoute( $self->loadedPlugins->[$index]->defaultRoute );
|
|
|
|
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
|
|
my $linksIcons = {
|
|
'conf' => 'cog',
|
|
'sessions' => 'duplicate',
|
|
'notifications' => 'bell',
|
|
'2ndFA' => 'wrench',
|
|
'viewer' => 'eye-open',
|
|
};
|
|
|
|
$self->links( [] );
|
|
for ( my $i = 0 ; $i < @links ; $i++ ) {
|
|
next unless ( defined $links[$i] );
|
|
push @{ $self->links },
|
|
{
|
|
target => $self->loadedPlugins->[$i]->defaultRoute,
|
|
title => $links[$i],
|
|
icon => $linksIcons->{ $links[$i] }
|
|
};
|
|
}
|
|
|
|
$self->menuLinks( [] );
|
|
if (
|
|
my $portal =
|
|
$conf->{cfgNum}
|
|
? Lemonldap::NG::Handler::PSGI::Main->tsv->{portal}->()
|
|
: $conf->{portal}
|
|
)
|
|
{
|
|
push @{ $self->menuLinks },
|
|
{
|
|
target => $portal,
|
|
title => 'backtoportal',
|
|
icon => 'home'
|
|
},
|
|
{
|
|
target => "$portal?logout=1",
|
|
title => 'logout',
|
|
icon => 'log-out'
|
|
};
|
|
}
|
|
1;
|
|
}
|
|
|
|
sub tplParams {
|
|
my ( $self, $req ) = @_;
|
|
my $res = eval {
|
|
$self->hLoadedPlugins->{viewer}->brwRule->( $req, $req->{userData} );
|
|
} || 0;
|
|
return ( VERSION => $VERSION, ALLOWBROWSER => $res );
|
|
}
|
|
|
|
sub javascript {
|
|
my ( $self, $req ) = @_;
|
|
my $res = eval {
|
|
$self->hLoadedPlugins->{viewer}
|
|
&& $self->hLoadedPlugins->{viewer}
|
|
->diffRule->( $req, $req->{userData} );
|
|
} || 0;
|
|
print STDERR $@ if $@;
|
|
my $impPrefix = $self->{impersonationPrefix} || 'real_';
|
|
my $ttl = $self->{timeout} || 72000;
|
|
|
|
return
|
|
'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";var viewPrefix=scriptname+"view/";'
|
|
. 'var allowDiff=' . "$res;"
|
|
. 'var impPrefix=' . "'"
|
|
. $impPrefix . "'" . ';'
|
|
. 'var sessionTTL=' . "$ttl;"
|
|
. ( $self->links ? 'var links=' . to_json( $self->links ) . ';' : '' )
|
|
. (
|
|
$self->menuLinks
|
|
? 'var menulinks=' . to_json( $self->menuLinks ) . ';'
|
|
: ''
|
|
);
|
|
}
|
|
|
|
sub sendHtml {
|
|
my ( $self, $req, $template, %args ) = @_;
|
|
my $res = $self->SUPER::sendHtml( $req, $template, %args );
|
|
push @{ $res->[1] },
|
|
'Content-Security-Policy' => $self->csp,
|
|
'X-Content-Type-Options' => 'nosniff',
|
|
'X-Frame-Options' => 'DENY',
|
|
'X-XSS-Protection' => '1; mode=block';
|
|
return $res;
|
|
}
|
|
|
|
sub loadPlugin {
|
|
my ( $self, $plugin, $conf ) = @_;
|
|
unless ($plugin) {
|
|
require Carp;
|
|
Carp::confess('Calling loadPugin without arg !');
|
|
}
|
|
my $obj;
|
|
$plugin = "Lemonldap::NG::Manager$plugin" if ( $plugin =~ /^::/ );
|
|
|
|
eval "require $plugin";
|
|
if ($@) {
|
|
$self->logger->error("$plugin load error: $@");
|
|
return 0;
|
|
}
|
|
eval {
|
|
$obj = $plugin->new( { p => $self, conf => $conf } );
|
|
$self->logger->debug("Module $plugin loaded");
|
|
};
|
|
if ($@) {
|
|
$self->error("Unable to build $plugin object: $@");
|
|
return 0;
|
|
}
|
|
( $obj and $obj->init($conf) ) or return 0;
|
|
return $obj;
|
|
}
|
|
|
|
1;
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
=encoding utf8
|
|
|
|
Lemonldap::NG::Manager - Perl extension for managing Lemonldap::NG Web-SSO
|
|
system.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
Use any of Plack launcher. Example:
|
|
|
|
#!/usr/bin/env plackup
|
|
|
|
use Lemonldap::NG::Manager;
|
|
|
|
# This must be the last instruction ! See PSGI for more
|
|
Lemonldap::NG::Manager->run($opts);
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Lemonldap::NG::Manager provides a web interface to manage Lemonldap::NG Web-SSO
|
|
system.
|
|
|
|
The Perl part of Lemonldap::NG::Manager is the REST server. Web interface is
|
|
written in Javascript, using AngularJS framework and can be found in `site`
|
|
directory. The REST API is described in REST-API.md file provided in source tree.
|
|
|
|
Lemonldap::NG Manager uses L<Plack> to be CGI, FastCGI and so on compatible.
|
|
It inherits of L<Lemonldap::NG::Handler::PSGI::Router>
|
|
|
|
=head1 ORGANIZATION
|
|
|
|
Lemonldap::NG Manager contains 6 parts:
|
|
|
|
=over
|
|
|
|
=item Configuration management
|
|
|
|
=item Session explorer
|
|
|
|
=item Notification explorer
|
|
|
|
=item Second Factors manager
|
|
|
|
=item Configuration builder (see L<Lemonldap::NG::Manager::Build>
|
|
|
|
=item Command line interface (see L<Lemonldap::NG::Manager::Cli>
|
|
|
|
=back
|
|
|
|
=head2 Static files generation
|
|
|
|
`scripts/jsongenerator.pl` file uses Lemonldap::NG::Manager::Build::Attributes,
|
|
Lemonldap::NG::Manager::Build::Tree and Lemonldap::NG::Manager::Build::CTrees to generate
|
|
|
|
=over
|
|
|
|
=item `site/htdocs/static/struct.json`:
|
|
|
|
main file containing the tree view;
|
|
|
|
=item `site/htdocs/static/js/conftree.js`:
|
|
|
|
generates Virtualhosts, SAML and OpenID-Connect partners sub-trees;
|
|
|
|
=item `Lemonldap::NG::Common::Conf::ReConstants`:
|
|
|
|
constants used by all Perl manager components;
|
|
|
|
=item `Lemonldap::NG::Common::Conf::DefaultValues`:
|
|
|
|
constants used to read configuration.
|
|
|
|
=back
|
|
|
|
=head1 PARAMETERS
|
|
|
|
You can use a hash ref to override any LemonLDAP::NG parameter. Currently, you
|
|
can specify where your lemonldap-ng.ini file is:
|
|
|
|
Lemonldap::NG::Manager->run( { confFile => '/path/to/lemonldap-ng.ini' } );
|
|
|
|
=head2 lemonldap-ng.ini parameters
|
|
|
|
You can override any configuration parameter in lemonldap-ng.ini, but some are
|
|
required and can't be set to global configuration (as any Lemonldap::NG module,
|
|
you can also fix them in $opts hash ref passed as argument to run() or new()).
|
|
|
|
[manager]
|
|
;protection: choose one of none, authenticate, manager as explain in
|
|
; Lemonldap::NG::Handler::PSGI::Router doc.
|
|
protection = manager
|
|
|
|
;enabledModules: Modules to display. Default to `conf, sessions, notifications, 2ndFA`
|
|
enabledModules = conf, sessions, notifications, 2ndFA
|
|
|
|
;logLevel: choose one of error, warn, notice, info, debug
|
|
; See Lemonldap::NG::Common::PSGI doc for more
|
|
logLevel = notice
|
|
|
|
;staticPrefix: set here the URI path to static content
|
|
; See Lemonldap::NG::Common::PSGI doc for more
|
|
staticPrefix = static/
|
|
|
|
;languages: Available interface languages
|
|
languages = en, fr
|
|
|
|
;templateDir: path to the directory containing HTML templates
|
|
; See Lemonldap::NG::Common::PSGI doc for more
|
|
templateDir = /usr/share/lemonldap-ng/manager/
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<http://lemonldap-ng.org/>
|
|
|
|
=head1 AUTHORS
|
|
|
|
=over
|
|
|
|
=item LemonLDAP::NG team L<http://lemonldap-ng.org/team>
|
|
|
|
=back
|
|
|
|
=head1 BUG REPORT
|
|
|
|
Use OW2 system to report bug or ask for features:
|
|
L<https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues>
|
|
|
|
Note that if you want to post a ticket for a conf upload problem, please
|
|
see L<Lemonldap::NG::Manager::Conf::Parser> before.
|
|
|
|
=head1 DOWNLOAD
|
|
|
|
Lemonldap::NG is available at
|
|
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
See COPYING file for details.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see L<http://www.gnu.org/licenses/>.
|
|
|
|
=cut
|
|
|