lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Combination.pm

193 lines
5.1 KiB
Perl
Raw Normal View History

2017-02-05 14:11:14 +01:00
package Lemonldap::NG::Portal::Auth::Combination;
2017-02-05 13:24:26 +01:00
use strict;
use Mouse;
use Lemonldap::NG::Common::Combination::Parser;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_ERROR);
# TODO: See Lib::Wrapper
extends 'Lemonldap::NG::Portal::Auth::Base';
# PROPERTIES
has stackSub => ( is => 'rw' );
# INITIALIZATION
sub init {
my ($self) = @_;
# Check if expression exists
2017-02-05 14:11:14 +01:00
unless ( $self->conf->{combination} ) {
2017-02-05 13:24:26 +01:00
$self->error('No combination found');
return 0;
}
# Load all declared modules
2017-02-05 13:24:26 +01:00
my %mods;
2017-02-06 13:36:27 +01:00
foreach my $key ( keys %{ $self->conf->{combModules} } ) {
2017-02-05 13:24:26 +01:00
my @tmp = ( undef, undef );
2017-02-06 13:36:27 +01:00
my $mod = $self->conf->{combModules}->{$key};
unless ( $mod->{type} and defined $mod->{for} ) {
$self->error("Malformed combination module $key");
return 0;
}
2017-02-05 13:24:26 +01:00
# Override parameters
# "for" key can have 3 values:
# 0: this module will be used for Auth and UserDB
# 1: this module will be user for Auth only
# 2: this module will be user for UserDB only
# Load Auth module
2017-02-05 13:24:26 +01:00
if ( $mod->{for} < 2 ) {
2017-02-06 13:36:27 +01:00
$tmp[0] = $self->loadPlugin( "::Auth::$mod->{type}", $mod->{over} );
2017-02-05 13:24:26 +01:00
unless ( $tmp[0] ) {
$self->error("Unable to load Auth::$mod->{type}");
return 0;
}
}
# Load UserDB module
2017-02-05 13:24:26 +01:00
unless ( $mod->{for} == 1 ) {
2017-02-06 13:36:27 +01:00
$tmp[1] =
$self->loadPlugin( "::UserDB::$mod->{type}", $mod->{over} );
2017-02-05 13:24:26 +01:00
unless ( $tmp[1] ) {
$self->error("Unable to load UserDB::$mod->{type}");
return 0;
}
}
# Store modules as array
2017-02-06 13:36:27 +01:00
$mods{$key} = \@tmp;
2017-02-05 13:24:26 +01:00
}
# Compile expression
2017-02-05 13:24:26 +01:00
eval {
$self->stackSub(
Lemonldap::NG::Common::Combination::Parser->parse(
2017-02-05 14:11:14 +01:00
\%mods, $self->conf->{combination}
2017-02-05 13:24:26 +01:00
)
);
};
if ($@) {
$self->error("Bad combination: $@");
return 0;
}
return 1;
}
# Each first method must call getStack() to get the auth scheme available for
# the current user
2017-02-05 13:24:26 +01:00
sub extractFormInfo {
my ( $self, $req ) = @_;
# Get available authentication schemes for this user
2017-02-05 18:05:33 +01:00
$self->getStack( $req, 'extractFormInfo' ) or return PE_ERROR;
return $self->try( 0, 'extractFormInfo', $req );
2017-02-05 13:24:26 +01:00
}
# Note that UserDB::Combination use the same object.
2017-02-05 13:24:26 +01:00
sub getUser {
2017-02-05 18:05:33 +01:00
return $_[0]->try( 1, 'getUser', $_[1] );
2017-02-05 13:24:26 +01:00
}
sub authenticate {
2017-02-05 18:05:33 +01:00
return $_[0]->try( 0, 'authenticate', $_[1] );
2017-02-05 13:24:26 +01:00
}
sub setAuthSessionInfo {
2017-02-05 18:05:33 +01:00
return $_[0]->try( 0, 'setAuthSessionInfo', $_[1] );
2017-02-05 13:24:26 +01:00
}
sub setSessionInfo {
2017-02-05 18:05:33 +01:00
return $_[0]->try( 1, 'setSessionInfo', $_[1] );
2017-02-05 13:24:26 +01:00
}
sub setGroups {
2017-02-05 18:05:33 +01:00
return $_[0]->try( 1, 'setGroups', $_[1] );
2017-02-05 13:24:26 +01:00
}
sub getDisplayType {
2017-02-07 07:21:23 +01:00
my ( $self, $req ) = @_;
my ( $nb, $stack ) = ( $req->datas->{multiTry}, $req->datas->{multiStack} );
my ( $res, $name ) = $stack->[$nb]->[0]->( 'getDisplayType', $req );
return $res;
2017-02-05 13:24:26 +01:00
}
# TODO: authLogout
sub getStack {
2017-02-05 18:05:33 +01:00
my ( $self, $req, @steps ) = @_;
return $req->datas->{multiStack} if ( $req->datas->{multiStack} );
my $stack = $req->datas->{multiStack} = $self->stackSub->( $req->env );
2017-02-05 13:24:26 +01:00
unless ($stack) {
$self->lmLog( 'No authentication scheme for this user', 'error' );
}
2017-02-05 18:05:33 +01:00
@{ $req->datas->{multiSteps} } = ( @steps, @{ $req->steps } );
2017-02-05 13:24:26 +01:00
$req->datas->{multiTry} = 0;
return $stack;
}
# Main running method: launch the next scheme if the current fails
2017-02-05 13:24:26 +01:00
sub try {
my ( $self, $type, $subname, $req ) = @_;
my ( $nb, $stack ) = ( $req->datas->{multiTry}, $req->datas->{multiStack} );
# If more than 1 scheme is available
2017-02-05 18:05:33 +01:00
my ( $res, $name );
2017-02-05 22:12:06 +01:00
if ( $nb < @$stack - 1 ) {
2017-02-05 13:24:26 +01:00
# TODO: change logLevel for userLog()
2017-02-05 18:05:33 +01:00
( $res, $name ) = $stack->[$nb]->[$type]->( $subname, $req );
2017-02-05 13:24:26 +01:00
# On error, restart authentication with next scheme
if ( $res > PE_OK ) {
2017-02-05 18:05:33 +01:00
$self->lmLog( qq'Scheme "$name" has return $res, trying next',
'info' );
2017-02-05 13:24:26 +01:00
$req->datas->{multiTry}++;
$req->steps( [ @{ $req->datas->{multiSteps} } ] );
return PE_OK;
}
}
2017-02-05 18:05:33 +01:00
else {
2017-02-05 22:12:06 +01:00
( $res, $name ) = $stack->[$nb]->[$type]->( $subname, $req );
2017-02-05 18:05:33 +01:00
}
$req->sessionInfo->{ [ '_auth', '_userDB' ]->[$type] } = $name;
return $res;
2017-02-05 13:24:26 +01:00
}
# try() stores real Auth/UserDB module in sessionInfo
# This method reads them. It is called by getModule()
# (see Main::Run)
sub name {
my ( $self, $req, $type ) = @_;
return $req->sessionInfo->{ ( $type eq 'auth' ? '_auth' : '_userDB' ) }
|| 'Combination';
}
# To avoid "tied" destroy, tied configurations are kept here
our %overC;
# Override portal loadPlugin() to use a wrapped configuration
sub loadPlugin {
my ( $self, $plugin, $over ) = @_;
my $obj;
my $nc;
2017-02-06 13:36:27 +01:00
if ($over) {
require Lemonldap::NG::Common::Conf::Wrapper;
tie %$nc, 'Lemonldap::NG::Common::Conf::Wrapper', $self->conf, $over;
$overC{$plugin} = $nc;
}
else {
$nc = $self->conf;
}
return 0
unless ( $obj = $self->p->loadModule( "$plugin", $nc ) );
return $self->p->findEP( $plugin, $obj );
}
2017-02-05 13:24:26 +01:00
1;