lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Choice.pm

309 lines
9.2 KiB
Perl
Raw Normal View History

2016-06-29 21:34:36 +02:00
package Lemonldap::NG::Portal::Lib::Choice;
use strict;
use Mouse;
2018-04-25 15:40:19 +02:00
use Safe;
2016-06-29 21:34:36 +02:00
extends 'Lemonldap::NG::Portal::Lib::Wrapper';
2018-10-03 22:01:13 +02:00
with 'Lemonldap::NG::Portal::Lib::OverConf';
2016-06-29 21:34:36 +02:00
2022-02-01 16:33:08 +01:00
our $VERSION = '2.0.14';
2016-06-29 21:34:36 +02:00
2022-02-16 17:43:29 +01:00
has modules => ( is => 'rw', default => sub { {} } );
has rules => ( is => 'rw', default => sub { {} } );
has type => ( is => 'rw' );
has catch => ( is => 'rw', default => sub { {} } );
has sessionKey => ( is => 'ro', default => '_choice' );
2018-04-26 12:01:40 +02:00
my $_choiceRules;
2018-04-26 07:46:49 +02:00
2016-06-29 21:34:36 +02:00
# INITIALIZATION
# init() must be called by module::init() with a number:
# - 0 for auth
# - 1 for userDB
# - 2 for passwordDB ?
sub init {
my ( $self, $type ) = @_;
2016-07-02 21:09:45 +02:00
$self->type($type);
2016-06-29 21:34:36 +02:00
2016-12-26 10:23:35 +01:00
unless ( $self->conf->{authChoiceModules}
and %{ $self->conf->{authChoiceModules} } )
{
2016-07-07 22:55:27 +02:00
$self->error("'authChoiceModules' is empty");
return 0;
}
2016-06-29 21:34:36 +02:00
foreach my $name ( keys %{ $self->conf->{authChoiceModules} } ) {
2018-11-26 14:40:21 +01:00
my @mods =
split( /[;\|]/, $self->conf->{authChoiceModules}->{$name} );
my $module = '::'
. [ 'Auth', 'UserDB', 'Password' ]->[$type] . '::'
. $mods[$type];
2018-10-08 16:57:24 +02:00
my $over;
if ( $mods[5] ) {
2018-10-15 20:58:16 +02:00
eval { $over = JSON::from_json( $mods[5] ) };
2018-10-08 16:57:24 +02:00
if ($@) {
$self->logger->error("Bad over value ($@), skipped");
}
}
if ( $module = $self->loadModule( $module, $over ) ) {
2016-06-29 21:34:36 +02:00
$self->modules->{$name} = $module;
2017-02-15 07:41:50 +01:00
$self->logger->debug(
2016-06-29 21:34:36 +02:00
[qw(Authentication User Password)]->[$type]
2018-11-26 14:40:21 +01:00
. " module $name selected" );
2016-06-29 21:34:36 +02:00
}
else {
2017-02-15 07:41:50 +01:00
$self->logger->error(
2018-11-26 14:40:21 +01:00
"Choice: unable to load $name, disabling it: " . $self->error );
2016-07-02 10:51:00 +02:00
$self->error('');
2016-06-29 21:34:36 +02:00
}
2018-04-26 07:38:17 +02:00
2018-06-18 23:19:33 +02:00
# Test if auth module wants to catch some path
unless ($type) {
if ( $module->can('catch') ) {
$self->catch->{$name} = $module->catch;
}
}
2018-04-26 07:38:17 +02:00
# Display conditions
my $safe = Safe->new;
2018-04-27 20:10:31 +02:00
my $cond = $mods[4];
if ( defined $cond and $cond !~ /^$/ ) {
$self->logger->debug("Found rule $cond for $name");
2018-11-26 14:40:21 +01:00
$_choiceRules->{$name} =
$safe->reval("sub{my(\$env)=\@_;return ($cond)}");
2018-04-26 07:38:17 +02:00
if ($@) {
$self->logger->error("Bad condition $cond: $@");
return 0;
}
}
else {
2018-04-26 12:01:40 +02:00
$self->logger->debug("No rule for $name");
2018-11-26 14:40:21 +01:00
$_choiceRules->{$name} = sub { 1 };
2018-04-26 07:38:17 +02:00
}
2016-06-29 21:34:36 +02:00
}
2016-07-02 21:09:45 +02:00
unless ( keys %{ $self->modules } ) {
2016-07-02 10:51:00 +02:00
$self->error('Choice: no available modules found, aborting');
return 0;
}
2016-06-30 22:42:50 +02:00
return 1;
2016-06-29 21:34:36 +02:00
}
# RUNNING METHODS
2016-06-29 21:34:36 +02:00
sub checkChoice {
my ( $self, $req ) = @_;
2018-06-18 23:19:33 +02:00
my $name;
2018-09-27 15:12:38 +02:00
# Check Choice from pdata
if ( defined $req->pdata->{_choice} ) {
$name = $req->pdata->{_choice};
$self->logger->debug("Choice $name selected from pdata");
}
unless ($name) {
# Check with catch method
foreach ( keys %{ $self->catch } ) {
if ( $req->path_info =~ $self->catch->{$_} ) {
$name = $_;
$self->logger->debug(
"Choice $name selected from " . $req->path_info );
last;
}
2018-06-18 23:19:33 +02:00
}
}
2018-09-27 15:12:38 +02:00
unless ($name) {
2021-04-22 17:48:18 +02:00
# Set by OAuth Resource Owner grant // RESTServer pwdCheck
2021-04-22 17:48:18 +02:00
if ( $req->data->{_pwdCheck} and $self->{conf}->{authChoiceAuthBasic} )
{
$name = $self->{conf}->{authChoiceAuthBasic};
}
}
2018-09-27 15:12:38 +02:00
unless ($name) {
# Check with other methods
2018-11-26 14:40:21 +01:00
$name ||=
2020-12-27 14:24:15 +01:00
$req->data->{findUserChoice}
|| $req->param( $self->conf->{authChoiceParam} )
2018-11-26 14:40:21 +01:00
|| $req->userData->{_choice}
|| $req->sessionInfo->{_choice}
or return 0;
2020-12-27 18:03:08 +01:00
my $from =
$req->data->{findUserChoice} ? 'findUser'
: $req->param( $self->conf->{authChoiceParam} ) ? 'param'
: $req->userData->{_choice} ? 'userData'
: 'sessionInfo';
$self->logger->debug("Choice $name selected from $from");
2018-09-27 15:12:38 +02:00
}
2016-07-01 17:56:16 +02:00
unless ( defined $self->modules->{$name} ) {
2017-02-15 07:41:50 +01:00
$self->logger->error("Unknown choice '$name'");
2016-07-01 17:56:16 +02:00
return 0;
}
2018-06-18 22:37:28 +02:00
2020-12-27 00:45:06 +01:00
unless ( $req->data->{findUserChoice} ) {
# Store choice if module loops
$req->pdata->{_choice} = $name;
$req->data->{_authChoice} = $name;
$req->sessionInfo->{_choice} = $name;
$self->p->_authentication->authnLevel("${name}AuthnLevel");
}
return $name if ( $req->data->{ "enabledMods" . $self->type } );
2020-12-27 00:45:06 +01:00
$req->data->{ "enabledMods" . $self->type } =
[ $self->modules->{$name} ];
2016-07-02 10:51:00 +02:00
return $name;
2016-06-29 21:34:36 +02:00
}
2016-07-01 18:59:59 +02:00
sub name {
my ( $self, $req, $type ) = @_;
2016-07-02 21:09:45 +02:00
unless ($req) {
2016-07-02 10:51:00 +02:00
return 'Choice';
}
my $n = ref( $req->data->{ "enabledMods" . $self->type }->[0] );
2016-07-01 18:59:59 +02:00
$n =~ s/^Lemonldap::NG::Portal::(?:(?:UserDB|Auth)::)?//;
return $n;
}
2016-07-02 21:09:45 +02:00
package Lemonldap::NG::Portal::Main;
2016-06-29 21:34:36 +02:00
# Build authentication loop displayed in template
2016-07-01 17:56:16 +02:00
# Return authLoop array reference
2016-06-29 21:34:36 +02:00
sub _buildAuthLoop {
my ( $self, $req ) = @_;
my @authLoop;
# Test authentication choices
unless ( ref $self->conf->{authChoiceModules} eq 'HASH' ) {
2017-02-15 07:41:50 +01:00
$self->logger->warn("No authentication choices defined");
2016-06-29 21:34:36 +02:00
return [];
}
foreach ( sort keys %{ $self->conf->{authChoiceModules} } ) {
my $name = $_;
# Name can have a digit as first character
# for sorting purpose
# Remove it in displayed name
$name =~ s/^(\d*)?(\s*)?//;
# Replace also _ by space for a nice display
$name =~ s/\_/ /g;
# Find modules associated to authChoice
2018-11-26 14:40:21 +01:00
my ( $auth, $userDB, $passwordDB, $url, $condition ) =
split( /[;\|]/, $self->conf->{authChoiceModules}->{$_} );
2016-06-29 21:34:36 +02:00
2018-04-27 13:54:06 +02:00
unless ( $_choiceRules->{$_} ) {
2019-07-04 07:24:50 +02:00
$self->logger->error("$_ has no rule");
2018-11-26 14:40:21 +01:00
$_choiceRules->{$_} = sub { 1 };
2018-04-26 12:01:40 +02:00
}
2018-04-27 13:54:06 +02:00
unless ( $_choiceRules->{$_}->( $req->env ) ) {
2018-04-25 15:40:19 +02:00
$self->logger->debug(
2018-11-26 14:40:21 +01:00
"Condition returns false, authentication choice $_ will not be displayed"
2018-04-25 15:40:19 +02:00
);
}
else {
2018-04-28 09:09:34 +02:00
$self->logger->debug("Displaying authentication choice $_");
2018-04-25 15:40:19 +02:00
if ( $auth and $userDB and $passwordDB ) {
# Default URL
2021-04-22 17:48:18 +02:00
$req->data->{cspFormAction} ||= {};
2018-11-26 14:40:21 +01:00
if (
defined $url
and not $self->checkXSSAttack( 'URI',
$req->env->{'REQUEST_URI'} )
and $url =~
q%^(https?://)?[^\s/.?#$].[^\s]+$% # URL must be well formatted
)
2018-04-25 15:40:19 +02:00
{
2018-10-31 22:46:03 +01:00
2021-04-22 17:48:18 +02:00
my $csp_uri = $self->cspGetHost($url);
$req->data->{cspFormAction}->{$csp_uri} = 1;
2018-04-25 15:40:19 +02:00
}
else {
$url .= '#';
}
$self->logger->debug("Use URL $url");
# Options to store in the loop
2018-10-29 22:14:51 +01:00
my $optionsLoop = {
name => $name,
key => $_,
module => $auth,
url => $url
};
2018-04-25 15:40:19 +02:00
# Get displayType for this module
no strict 'refs';
my $displayType = eval {
$self->_authentication->modules->{$_}
->can('getDisplayType')->( $self, $req );
} || 'logo';
2018-04-25 15:40:19 +02:00
$self->logger->debug(
"Display type $displayType for module $auth");
$optionsLoop->{$displayType} = 1;
2018-11-23 22:08:06 +01:00
my $logo = $_;
my $foundLogo = 0;
2016-06-29 21:34:36 +02:00
# If displayType is logo, check if key.png is available
if ( -e $self->conf->{templateDir}
. "/../htdocs/static/common/modules/"
2018-11-23 22:08:06 +01:00
. $logo
. ".png" )
{
2018-11-23 22:08:06 +01:00
$optionsLoop->{logoFile} = $logo . ".png";
$foundLogo = 1;
}
else {
$optionsLoop->{logoFile} = $auth . ".png";
}
2016-06-29 21:34:36 +02:00
# Compatibility, with Custom, try the module name if
# key was not found
if ( $auth eq 'Custom' and not $foundLogo ) {
$logo =
( ( $self->{conf}->{customAuth} || "" ) =~ /::(\w+)$/ )
[0];
if (
$logo
and ( -e $self->conf->{templateDir}
. "/../htdocs/static/common/modules/"
. $logo
. ".png" )
)
{
$optionsLoop->{logoFile} = $logo . ".png";
}
}
2018-04-25 15:40:19 +02:00
# Register item in loop
push @authLoop, $optionsLoop;
2016-06-29 21:34:36 +02:00
2018-04-25 15:40:19 +02:00
$self->logger->debug(
"Authentication choice $name will be displayed");
}
else {
$req->error("Authentication choice $_ value is invalid");
return 0;
}
2016-06-29 21:34:36 +02:00
}
}
return \@authLoop;
}
1;