lemonldap-ng/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_Choice.pm

215 lines
6.5 KiB
Perl

## @file
# Common functions for authentication choice
## @class
# Common functions for authentication choice
package Lemonldap::NG::Portal::_Choice;
use Lemonldap::NG::Portal::Simple;
our $VERSION = '1.0.0';
## @cmethod Lemonldap::NG::Portal::_Choice new(Lemonldap::NG::Portal::Simple portal)
# Constructor
# @param $portal Lemonldap::NG::Portal::Simple object
# @return new Lemonldap::NG::Portal::_Choice object
sub new {
my ( $class, $portal ) = splice @_;
# Create object with portal parameter
my $self = bless { p => $portal }, $class;
# Recover authChoice from session
$portal->{_authChoice} ||= $portal->{sessionInfo}->{_authChoice};
# Test authChoice
unless ( $portal->{_authChoice}
and exists $portal->{authChoiceModules}->{ $portal->{_authChoice} } )
{
$portal->lmLog( "No authentication choice done, or wrong choice",
'debug' );
$portal->{_authChoice} = "";
}
# Special workaround for SAML
# because we cannot easily set SSO return URL
# and SLO URL with authChoice parameter
# Test authForce to see if URL is an SAML URL
unless ( $portal->{_authChoice} ) {
my $samlModule = 'Lemonldap::NG::Portal::AuthSAML';
my $samlForce = 0;
eval {
$portal->loadModule($samlModule);
$authForce = $samlModule . '::authForce';
$samlForce = $portal->$authForce;
};
if ($@) {
$portal->lmLog( "SAML choice force not tested: $@", 'debug' );
}
# Force SAML choice if needed
if ($samlForce) {
$portal->lmLog( "SAML is a forced choice", 'debug' );
$portal->{_authChoice} = 'forcedSAML';
$portal->{authChoiceModules}->{'forcedSAML'} = 'SAML|SAML|Null';
}
}
return $self unless $portal->{_authChoice};
# Find modules associated to authChoice
my ( $auth, $userDB, $passwordDB ) =
split( /\|/, $portal->{authChoiceModules}->{ $portal->{_authChoice} } );
if ( $auth and $userDB and $passwordDB ) {
my $modulePrefix = 'Lemonldap::NG::Portal::';
my $authModule = $modulePrefix . 'Auth' . $auth;
my $userDBModule = $modulePrefix . 'UserDB' . $userDB;
my $passwordDBModule = $modulePrefix . 'PasswordDB' . $passwordDB;
foreach my $module ( $authModule, $userDBModule, $passwordDBModule ) {
$portal->abort( 'Bad configuration', "Unable to load $module" )
unless $portal->loadModule($module);
}
$self->{modules} = [
{ m => $authModule, n => $auth },
{ m => $userDBModule, n => $userDB },
{ m => $passwordDBModule, n => $passwordDB }
];
$portal->lmLog( "Authentication module $auth selected", 'debug' );
$portal->lmLog( "User module $userDB selected", 'debug' );
$portal->lmLog( "Password module $passwordDB selected", 'debug' );
}
else {
$portal->abort( "Authentication choice "
. $self->{_authChoice}
. " value is invalid" );
}
return $self;
}
## @method int try(string sub,int type)
# Main method: try to call $sub method in the choosen module.
# If no choice, return default behavior
# @param sub name of the method to launch
# @param type 0 for authentication, 1 for userDB, 2 for passworDB
# @return Lemonldap::NG::Portal error code returned by method $sub
sub try {
my ( $self, $sub, $type ) = splice @_;
# Default behavior in no choice
unless ( defined $self->{modules} ) {
return PE_FORMEMPTY if ( $sub eq 'extractFormInfo' );
return PE_OK;
}
# Launch wanted subroutine
my $s = $self->{modules}->[$type]->{m} . "::$sub";
my $name = $self->{modules}->[$type]->{n};
$self->{p}
->lmLog( "Try to launch $sub on module $name (type $type)", 'debug' );
return $self->{p}->$s();
}
package Lemonldap::NG::Portal::Simple;
## @method private Lemonldap::NG::Portal::_Choice _choice()
# Return Lemonldap::NG::Portal::_Choice object and builds it if it was not build
# before. This method is used if authentication is set to "Choice".
# @return Lemonldap::NG::Portal::_Choice object
sub _choice {
my $self = shift;
return $self->{_choice} if ( $self->{_choice} );
return $self->{_choice} = Lemonldap::NG::Portal::_Choice->new($self);
}
## @method private Lemonldap::NG::Portal::_Choice _buildAuthLoop()
# Build authentication loop displayed in template
# @return authLoop rarray reference
sub _buildAuthLoop {
my $self = shift;
my @authLoop;
# Test authentication choices
unless ( ref $self->{authChoiceModules} eq 'HASH' ) {
$self->lmLog( "No authentication choices defined", 'warn' );
return [];
}
foreach ( sort keys %{ $self->{authChoiceModules} } ) {
my $name = $_;
# Ignore 'forcedSAML'
next if $name eq 'forcedSAML';
# Name can have a digit as first character
# for sorting purpose
# Remove it in displayed name
$name =~ s/^(\d*)?(\s*)?//;
# Find modules associated to authChoice
my ( $auth, $userDB, $passwordDB ) =
split( /\|/, $self->{authChoiceModules}->{$_} );
# What do display
# -> login/password form (LDAP, DBI, ...)
# -> OpenID form
# -> logo with link (ex: CAS, SSL, etc.)
my $displayType = {
'standardform' => [qw(LDAP DBI Proxy)],
'openidform' => [qw(OpenID)],
'logo' => [qw(CAS Twitter SAML SSL Apache Remote)],
};
if ( $auth and $userDB and $passwordDB ) {
# Options to store in the loop
my $optionsLoop = { name => $name, key => $_, module => $auth };
# Get displayType for this module
foreach my $type ( keys %$displayType ) {
foreach my $backend ( @{ $displayType->{$type} } ) {
if ( $auth eq $backend ) {
$self->lmLog( "Display type $type for module $auth",
'debug' );
$optionsLoop->{$type} = 1;
last;
}
}
}
# Register item in loop
push @authLoop, $optionsLoop;
$self->lmLog( "Authentication choice $name will be displayed",
'debug' );
}
else {
$self->abort("Authentication choice $_ value is invalid");
}
}
return \@authLoop;
}
1;