_Multi.pm 5.99 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
## @file
# Authentication and UserDB chaining mechanism

## @class
# Authentication and UserDB chaining mechanism.
# To use it set your authentication module like this :
#    authentication => 'Multi CAS;LDAP'
#
# If CAS failed, LDAP will be used. You can also add a condition. Example:
#    authentication => 'Multi Remote $ENV{REMOTE_ADDR}=~/^192/;LDAP $ENV{REMOTE_ADDR}!~/^192/'
package Lemonldap::NG::Portal::_Multi;

use Lemonldap::NG::Portal::Simple;
Yadd's avatar
Yadd committed
14
use Scalar::Util 'weaken';
15

16
our $VERSION = '1.4.6';
17

18
19
20
21
22
## @cmethod Lemonldap::NG::Portal::_Multi new(Lemonldap::NG::Portal::Simple portal)
# Constructor
# @param $portal Lemonldap::NG::Portal::Simple object
# @return new Lemonldap::NG::Portal::_Multi object
sub new {
Yadd's avatar
Yadd committed
23
    my ( $class, $portal ) = @_;
24
    my $self = bless { p => $portal, res => PE_NOSCHEME }, $class;
Yadd's avatar
Yadd committed
25
    weaken $self->{p};
26
27

    # Browse authentication and userDB configuration
Yadd's avatar
Yadd committed
28
    my @stack = ( $portal->{multiAuthStack}, $portal->{multiUserDBStack} );
29
30
31
    for ( my $i = 0 ; $i < 2 ; $i++ ) {
        $stack[$i] =~ s/^Multi\s*//;
        foreach my $l ( split /;/, $stack[$i] ) {
Clément OUDOT's avatar
Clément OUDOT committed
32
            $l =~ s/^\s+//;    # Remove first space
33
34
35
36
37
            $l =~ /^([\w#]+)(?:\s+(.*))?$/
              or $portal->abort( 'Bad configuration', "Unable to read $l" );
            my ( $mod, $cond ) = ( $1, $2 );
            my $name = $mod;
            $mod =~ s/#(.*)$//;
38
            my $shortname = $mod;
39
40
41
            $cond = 1 unless ( defined $cond );
            $mod = "Lemonldap::NG::Portal::" . [ 'Auth', 'UserDB' ]->[$i] . $mod
              unless ( $mod =~ /::/ );
Clément OUDOT's avatar
Clément OUDOT committed
42
43
44

            $portal->abort( 'Bad configuration', "Unable to load $mod" )
              unless $self->{p}->loadModule($mod);
45
            push @{ $self->{stack}->[$i] },
46
              { m => $mod, c => $cond, n => $name, s => $shortname };
47
        }
48
49
50
51
52
53
54

        # Override portal settings
        %{ $self->{p} } = (
            %{ $self->{p} },
            %{ $self->{p}->{multi}->{ $self->{stack}->[$i]->[0]->{n} } }
        ) if ( $self->{p}->{multi}->{ $self->{stack}->[$i]->[0]->{n} } );

55
    }
56
57

    # Return _Multi object
58
59
60
61
62
63
64
65
66
67
    return $self;
}

## @method int try(string sub,int type)
# Main method: try to call $sub method in the current authentication or
# userDB module. If it fails, call next() and replay()
# @param sub name of the method to launch
# @param type 0 for authentication, 1 for userDB
# @return Lemonldap::NG::Portal error code returned by method $sub
sub try {
Yadd's avatar
Yadd committed
68
    my ( $self, $sub, $type ) = @_;
69
70
71
72
    my $res;
    my $s   = $self->{stack}->[$type]->[0]->{m} . "::$sub";
    my $old = $self->{stack}->[$type]->[0]->{n};
    my $ci;
Clément OUDOT's avatar
Clément OUDOT committed
73

74
    # Store last module used
75
    $self->{last}->[$type] = $self->{stack}->[$type]->[0]->{m};
76

77
    if ( $ci = $self->{p}->safe->reval( $self->{stack}->[$type]->[0]->{c} ) ) {
Clément OUDOT's avatar
Clément OUDOT committed
78
79

        # Log used module
80
81
        $self->{p}
          ->lmLog( "Multi (type $type): trying $sub for module $old", 'debug' );
Clément OUDOT's avatar
Clément OUDOT committed
82
83

        # Run subroutine
84
        $res = $self->{p}->$s();
Clément OUDOT's avatar
Clément OUDOT committed
85

86
        return $res if $self->stop( $type, $res );
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    }
    unless ( $self->next($type) ) {
        return ( $ci ? $res : $self->{res} );
    }
    $self->{res} = $res if ( defined($res) );
    $self->{p}->lmLog(
        [ 'Authentication', 'Retriving user' ]->[$type]
          . " with $old failed, trying next",
        'info'
    ) if ($ci);
    $res = $self->replay( $sub, $type );
    return $res;
}

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
## @method protected boolean stop(int type, int res)
# Call specific backend to know if multi process should stop
# @param type 0 for authentication, 1 for userDB
# @param res return code of last executed sub
# return true if process should stop
sub stop {
    my ( $self, $type, $res ) = @_;

    # Stop if no error, or if confirmation needed, or if form not filled
    return 1
      if ( $res <= 0
        or $res == PE_CONFIRM
        or $res == PE_FIRSTACCESS
        or $res == PE_FORMEMPTY );

    # Check specific backend stop method
    my $stopSub = $self->{stack}->[$type]->[0]->{m} . "::stop";

    my $ret = 0;
    eval { $ret = $self->{p}->$stopSub($res); };
    if ($@) {
        $self->{p}->lmLog( $@, 'debug' );
        return 0;
    }

    return $ret;
}

129
130
131
132
133
134
135
## @method protected boolean next(int type)
# Set the next authentication or userDB module as current. If both
# authentication and userDB module have the same name, both are changed if
# possible.
# @param type 0 for authentication, 1 for userDB
# return true if an other module is available
sub next {
Yadd's avatar
Yadd committed
136
    my ( $self, $type ) = @_;
137

Yadd's avatar
Yadd committed
138
139
140
141
142
    if ( $self->{stack}->[$type]->[0]->{n} eq
            $self->{stack}->[ 1 - $type ]->[0]->{n}
        and $self->{stack}->[ 1 - $type ]->[1] )
    {
        shift @{ $self->{stack}->[ 1 - $type ] };
143
144
    }
    shift @{ $self->{stack}->[$type] };
145
146

    # Manage end of the stack
147
    return 0 unless ( @{ $self->{stack}->[$type] } );
148

149
150
    %{ $self->{p} } = (
        %{ $self->{p} },
Yadd's avatar
Yadd committed
151
152
        %{ $self->{p}->{multi}->{ $self->{stack}->[$type]->[0]->{n} } }
    ) if ( $self->{p}->{multi}->{ $self->{stack}->[$type]->[0]->{n} } );
153
154
155
156
157
158
159
160
    return 1;
}

## @method protected int replay(string sub)
# replay all methods since authInit() until method $sub with the new module.
# @param $sub name of the method who has failed
# @return Lemonldap::NG::Portal error code
sub replay {
Yadd's avatar
Yadd committed
161
    my ( $self, $sub ) = @_;
162
    my @subs = ();
163
164
    $self->{p}->lmLog( "Replay all methods until sub $sub", 'debug' );

165
    foreach (
166
        qw(authInit extractFormInfo userDBInit getUser setAuthSessionInfo
167
        setSessionInfo setGroups authenticate authFinish)
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
      )
    {
        push @subs, $_;
        last if ( $_ eq $sub );
    }
    return $self->{p}->_subProcess(@subs);
}

package Lemonldap::NG::Portal::Simple;

## @method private Lemonldap::NG::Portal::_Multi _multi()
# Return Lemonldap::NG::Portal::_Multi object and builds it if it was not build
# before. This method is used if authentication is set to "Multi".
# @return Lemonldap::NG::Portal::_Multi object
sub _multi {
    my $self = shift;
    return $self->{_multi} if ( $self->{_multi} );
    return $self->{_multi} = Lemonldap::NG::Portal::_Multi->new($self);
}

1;