PE_CONFIRM
PE_ERROR
PE_FIRSTACCESS
PE_FORMEMPTY
PE_PASSWORD_OK
PE_OK
)
;
our
$VERSION
=
'2.0.14'
;
has
stackSub
=> (
is
=>
'rw'
);
has
wrapUserLogger
=> (
is
=>
'rw'
,
lazy
=> 1,
default
=>
sub
{
Lemonldap::NG::Portal::Lib::Combination::UserLogger->new(
$_
[0]->userLogger );
}
);
sub
init {
my
$self
=
shift
;
unless
(
$self
->conf->{combination} ) {
$self
->error(
'No combination found'
);
return
0;
}
my
%mods
;
foreach
my
$key
(
keys
%{
$self
->conf->{combModules} } ) {
my
@tmp
= (
undef
,
undef
);
my
$mod
=
$self
->conf->{combModules}->{
$key
};
unless
(
$mod
->{type} and
defined
$mod
->{
for
} ) {
$self
->error(
"Malformed combination module $key"
);
return
0;
}
if
(
$mod
->{
for
} < 2 ) {
$tmp
[0] =
$self
->loadPlugin(
"::Auth::$mod->{type}"
,
$mod
->{over} );
unless
(
$tmp
[0] ) {
$self
->error(
"Unable to load Auth::$mod->{type}"
);
return
0;
}
$tmp
[0]->{userLogger} =
$self
->wrapUserLogger;
weaken
$tmp
[0]->{userLogger};
}
unless
(
$mod
->{
for
} == 1 ) {
$tmp
[1] =
$self
->loadPlugin(
"::UserDB::$mod->{type}"
,
$mod
->{over} );
unless
(
$tmp
[1] ) {
$self
->error(
"Unable to load UserDB::$mod->{type}"
);
return
0;
}
$tmp
[1]->{userLogger} =
$self
->wrapUserLogger;
weaken
$tmp
[1]->{userLogger};
}
$mods
{
$key
} = \
@tmp
;
}
eval
{
$self
->stackSub(
Lemonldap::NG::Common::Combination::Parser->parse(
\
%mods
,
$self
->conf->{combination}
)
);
};
if
($@) {
$self
->error(
"Bad combination: $@"
);
return
0;
}
return
1;
}
sub
extractFormInfo {
my
$self
=
shift
;
return
$self
->
try
( 0,
'extractFormInfo'
,
@_
);
}
sub
authenticate {
my
$self
=
shift
;
return
$self
->
try
( 0,
'authenticate'
,
@_
);
}
sub
setAuthSessionInfo {
my
$self
=
shift
;
return
$self
->
try
( 0,
'setAuthSessionInfo'
,
@_
);
}
sub
getDisplayType {
my
$self
=
shift
;
my
(
$req
) =
@_
;
return
$self
->conf->{combinationForms}
if
(
$self
->conf->{combinationForms} );
my
(
$nb
,
$stack
) = (
$req
->data->{dataKeep}->{combinationTry},
$req
->data->{combinationStack}
);
my
$res
=
$stack
->[
$nb
]->[0]->(
'getDisplayType'
,
@_
);
return
$res
;
}
sub
authLogout {
my
$self
=
shift
;
my
(
$req
) =
@_
;
$self
->getStack(
$req
,
'extractFormInfo'
) or
return
PE_ERROR;
$req
->userData->{_combinationTry} ||= 0;
my
$sub
=
$req
->data->{combinationStack}->[
$req
->userData->{_combinationTry} ]
->[0];
unless
(
$sub
) {
$self
->logger->
warn
(
"Condition changed between login and logout for "
.
$req
->user
.
", unable to select good backend"
);
return
PE_OK;
}
my
(
$res
,
$name
) =
$sub
->(
'authLogout'
,
@_
);
$self
->logger->debug(
qq'User disconnected using scheme "$name"'
);
return
$res
;
}
sub
authFinish {
return
PE_OK;
}
sub
authForce {
return
0;
}
sub
setSecurity {
my
$self
=
shift
;
my
(
$req
) =
@_
;
$self
->getStack(
$req
,
'extractFormInfo'
) or
return
;
eval
{
$req
->data->{combinationStack}
->[
$req
->data->{dataKeep}->{combinationTry} ]->[0]
->(
'setSecurity'
,
@_
);
};
$self
->logger->debug($@)
if
($@);
}
sub
getUser {
my
$self
=
shift
;
return
$self
->
try
( 1,
'getUser'
,
@_
);
}
sub
findUser {
my
$self
=
shift
;
return
$self
->
try
( 1,
'findUser'
,
@_
);
}
sub
setSessionInfo {
my
$self
=
shift
;
return
$self
->
try
( 1,
'setSessionInfo'
,
@_
);
}
sub
setGroups {
my
$self
=
shift
;
return
$self
->
try
( 1,
'setGroups'
,
@_
);
}
sub
getStack {
my
(
$self
,
$req
,
@steps
) =
@_
;
return
$req
->data->{combinationStack}
if
(
$req
->data->{combinationStack} );
my
$stack
=
$req
->data->{combinationStack} =
$self
->stackSub->(
$req
->env );
unless
(
$stack
) {
$self
->logger->error(
'No authentication scheme for this user'
);
}
@{
$req
->data->{combinationSteps} } = (
@steps
, @{
$req
->steps } );
$req
->data->{dataKeep}->{combinationTry} ||= 0;
return
$stack
;
}
sub
try
{
my
(
$self
,
$type
,
$subname
,
$req
,
@args
) =
@_
;
unless
(
defined
$req
->data->{combinationStack} ) {
$self
->getStack(
$req
,
$subname
) or
return
PE_ERROR;
}
my
(
$nb
,
$stack
) = (
$req
->data->{dataKeep}->{combinationTry},
$req
->data->{combinationStack}
);
my
(
$res
,
$name
);
unless
(
ref
$stack
->[
$nb
]->[
$type
] ) {
$self
->logger->error(
'Something went wrong in combination, unable to find any auth scheme (try == '
. (
$nb
+ 1 )
.
')'
);
return
PE_ERROR;
}
my
$stop
= 0;
if
(
$nb
<
@$stack
- 1 ) {
(
$res
,
$name
) =
$stack
->[
$nb
]->[
$type
]->(
$subname
,
$req
,
@args
);
unless
(
$stop
=
$self
->stop(
$stack
->[
$nb
]->[
$type
],
$res
) ) {
$self
->logger->info(
qq'Scheme "$name" returned $res, trying next'
);
$req
->data->{dataKeep}->{combinationTry}++;
$req
->steps( [ @{
$req
->data->{combinationSteps} } ] );
$req
->
continue
(1);
return
PE_OK;
}
}
else
{
(
$res
,
$name
) =
$stack
->[
$nb
]->[
$type
]->(
$subname
,
$req
,
@args
);
}
$req
->sessionInfo->{ [
'_auth'
,
'_userDB'
]->[
$type
] } =
$name
;
$req
->sessionInfo->{_combinationTry} =
$req
->data->{dataKeep}->{combinationTry};
if
(
$res
> 0 ) {
if
(
$stop
) {
$self
->userLogger->info(
"Combination stopped by plugin $name (code $res)"
);
}
elsif
(
$res
!= PE_FIRSTACCESS ) {
$self
->userLogger->
warn
(
'All schemes failed'
. (
$req
->user ?
' for user '
.
$req
->user :
''
) .
' ('
.
$req
->address
.
')'
);
}
}
return
$res
;
}
sub
name {
my
(
$self
,
$req
,
$type
) =
@_
;
return
$req
->sessionInfo->{ (
$type
eq
'auth'
?
'_auth'
:
'_userDB'
) }
||
'Combination'
;
}
sub
stop {
my
(
$self
,
$mod
,
$res
) =
@_
;
return
1
if
(
$res
<= 0
or
$res
== PE_CONFIRM
or
$res
== PE_PASSWORD_OK
);
my
(
$ret
,
$name
);
$ret
=
$mod
->(
'can'
,
'stop'
);
if
(
$ret
) {
eval
{ (
$ret
,
$name
) =
$mod
->(
'stop'
,
$res
) };
if
($@) {
$self
->logger->error(
"Optional ${name}::stop() method failed: "
. $@ );
return
0;
}
}
return
$ret
;
}
sub
new {
my
(
$class
,
$realLogger
) =
@_
;
return
bless
{
logger
=>
$realLogger
},
$class
;
}
sub
warn
{
my
(
$auth
) =
caller
(0);
$_
[0]->{logger}->notice(
"Combination ($auth): $_[1]"
);
}
sub
AUTOLOAD {
no
strict;
return
$_
[0]->{logger}->
$AUTOLOAD
(
$_
[1] )
if
(
$AUTOLOAD
=~ /^(?:notice|debug|error|info)$/ );
}
1;