PSGI.pm 6.31 KB
Newer Older
Yadd's avatar
Yadd committed
1
package Lemonldap::NG::Handler::Lib::PSGI;
2 3 4 5

use 5.10.0;
use Mouse;

Yadd's avatar
Yadd committed
6 7 8
#use Lemonldap::NG::Handler::Main qw(:jailSharedVars);

our $VERSION = '2.0.0';
9 10

has protection => ( is => 'rw', isa => 'Str' );
Yadd's avatar
Yadd committed
11
has rule       => ( is => 'rw', isa => 'Str' );
Yadd's avatar
Yadd committed
12
has api        => ( is => 'rw', isa => 'Str' );
13

Yadd's avatar
Yadd committed
14 15
## @method boolean init($args)
# Initalize main handler
16 17
sub init {
    my ( $self, $args ) = @_;
Yadd's avatar
Yadd committed
18
    eval { $self->api->init($args) };
Yadd's avatar
Yadd committed
19
    if ( $@ and not( $self->{protection} and $self->{protection} eq 'none' ) ) {
20 21 22
        $self->error($@);
        return 0;
    }
Yadd's avatar
Yadd committed
23
    unless ( $self->api->checkConf($self)
24 25 26
        or $self->{protection} eq 'none' )
    {
        $self->error(
Yadd's avatar
Typo  
Yadd committed
27 28
            "Unable to protect this server ($Lemonldap::NG::Common::Conf::msg)"
        );
29 30
        return 0;
    }
31
    eval { $self->portal( $self->api->tsv->{portal}->() ) };
Yadd's avatar
Yadd committed
32 33
    my $rule =
      $self->{protection} || $self->api->localConfig->{protection} || '';
Yadd's avatar
Yadd committed
34 35
    $self->rule(
        $rule eq 'authenticate' ? 1 : $rule eq 'manager' ? '' : $rule );
36
    return 1;
Yadd's avatar
Yadd committed
37
}
38

39
## @methodi void _run()
Yadd's avatar
Yadd committed
40
# Check if protecton is activated then return a code ref that will launch
41
# _authAndTrace() if protection in on or handler() else
42
#@return code-ref
43 44 45 46
sub _run {
    my $self = shift;

    # Override _run() only if protection != 'none'
Yadd's avatar
Yadd committed
47
    if ( !$self->rule or $self->rule ne 'none' ) {
Yadd's avatar
Yadd committed
48
        $self->logger->debug('PSGI app is protected');
49 50 51 52

        # Handle requests
        # Developers, be careful: Only this part is executed at each request
        return sub {
Yadd's avatar
Yadd committed
53 54
            return $self->_authAndTrace(
                Lemonldap::NG::Common::PSGI::Request->new( $_[0] ) );
55 56 57 58
        };
    }

    else {
Yadd's avatar
Yadd committed
59
        $self->logger->debug('PSGI app is not protected');
Yadd's avatar
Yadd committed
60 61

        # Check if main handler initialization has been done
Yadd's avatar
Yadd committed
62
        unless ( $self->api->tsv ) {
Yadd's avatar
Yadd committed
63
            $self->logger->debug('Checking conf');
Yadd's avatar
Yadd committed
64
            eval { $self->api->checkConf() };
Yadd's avatar
Yadd committed
65
            $self->logger->error($@) if ($@);
66 67 68 69
        }

        # Handle unprotected requests
        return sub {
Yadd's avatar
Yadd committed
70
            my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
71
            my $res = $self->handler($req);
Yadd's avatar
Yadd committed
72
            push @{ $res->[1] }, @{ $req->{respHeaders} };
Yadd's avatar
Yadd committed
73
            return $res;
74 75 76 77
        };
    }
}

Yadd's avatar
Yadd committed
78
sub status {
Yadd's avatar
Yadd committed
79 80 81
    my ( $class, $args ) = @_;
    $args //= {};
    my $self = $class->new($args);
82
    $self->init($args);
Yadd's avatar
Yadd committed
83 84

    # Check if main handler initialization has been done
85
    unless ( %{ $self->api->tsv } ) {
Yadd's avatar
Yadd committed
86
        eval { $self->api->checkConf() };
Yadd's avatar
Yadd committed
87
        $self->logger->error($@) if ($@);
Yadd's avatar
Yadd committed
88 89
    }
    return sub {
Yadd's avatar
Yadd committed
90
        my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
Yadd's avatar
Yadd committed
91
        $self->api->status($req);
Yadd's avatar
Yadd committed
92
        return [ 200, [ @{ $req->{respHeaders} } ], [ $req->{respBody} ] ];
Yadd's avatar
Yadd committed
93 94 95
    };
}

Yadd's avatar
Yadd committed
96 97 98 99
sub reload {
    my ( $class, $args ) = @_;
    $args //= {};
    my $self = $class->new($args);
100
    $self->init($args);
Yadd's avatar
Yadd committed
101 102

    # Check if main handler initialization has been done
103
    unless ( %{ $self->api->tsv } ) {
Yadd's avatar
Yadd committed
104
        eval { $self->api->checkConf() };
Yadd's avatar
Yadd committed
105
        $self->logger->error($@) if ($@);
Yadd's avatar
Yadd committed
106 107 108 109 110 111 112 113
    }
    return sub {
        my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] );
        $self->api->reload($req);
        return [ 200, [ @{ $req->{respHeaders} } ], [ $req->{respBody} ] ];
    };
}

Yadd's avatar
Yadd committed
114
## @method private PSGI-Response _authAndTrace($req)
Yadd's avatar
Yadd committed
115
# Launch $self->api::run() and then handler() if
Yadd's avatar
Yadd committed
116
# response is 200.
Yadd's avatar
Yadd committed
117
sub _authAndTrace {
Yadd's avatar
Yadd committed
118
    my ( $self, $req, $noCall ) = @_;
Yadd's avatar
Yadd committed
119 120

    # TODO: handle types
121 122 123 124 125 126 127 128 129
    my $type = $self->api->checkType($req);
    if ( my $t = $req->env->{VHOSTTYPE} ) {
        $type = $t;
    }
    my $tmp = $self->api;
    $tmp =~ s/::\w+$/::/;
    $type = $tmp . $type;
    eval "require $type";
    die $@ if ($@);
Yadd's avatar
Yadd committed
130 131
    my ( $res, $session ) = $type->run( $req, $self->{rule} );
    $self->portal( $type->tsv->{portal}->() );
Yadd's avatar
Yadd committed
132
    $req->userData($session) if ($session);
Yadd's avatar
Yadd committed
133 134

    if ( $res < 300 ) {
Yadd's avatar
Yadd committed
135 136 137 138
        if ($noCall) {
            return [ $res, $req->{respHeaders}, [] ];
        }
        else {
Yadd's avatar
Yadd committed
139
            $self->logger->debug('User authenticated, calling handler()');
Yadd's avatar
Yadd committed
140
            $res = $self->handler($req);
Yadd's avatar
Yadd committed
141 142 143 144 145 146 147 148 149

            # Insert respHeaders in response only if not already set
            my %hdr1 = @{ $res->[1] };
            my %hdr2 = @{ $req->{respHeaders} };
            foreach ( keys %hdr2 ) {
                unless ( $hdr1{$_} and $hdr2{$_} eq $hdr1{$_} ) {
                    push @{ $res->[1] }, ( $_ => $hdr2{$_} );
                }
            }
Yadd's avatar
Yadd committed
150
        }
Yadd's avatar
Yadd committed
151
        return $res;
Yadd's avatar
Yadd committed
152
    }
Yadd's avatar
Yadd committed
153 154 155
    elsif ( $res < 400 ) {
        return [ $res, $req->{respHeaders}, [] ];
    }
Yadd's avatar
Yadd committed
156
    else {
Yadd's avatar
Yadd committed
157
        my %h = $req->{respHeaders} ? @{ $req->{respHeaders} } : ();
158
        my $s = $type->tsv->{portal}->() . "/lmerror/$res";
Yadd's avatar
Yadd committed
159 160 161 162 163 164 165 166 167 168 169 170
        $s =
            '<html><head><title>Redirection</title></head><body>'
          . qq{<script type="text/javascript">window.location='$s'</script>}
          . '<h1>Please wait</h1>'
          . qq{<p>An error occurs, you're going to be redirected to <a href="$s">$s</a>.</p>}
          . '</body></html>';
        $h{'Content-Type'}   = 'text/html';
        $h{'Content-Length'} = length $s;
        return [ $res, [%h], [$s] ];
    }
}

171
## @method hashRef user()
Yadd's avatar
Yadd committed
172
# @return hash of user data
173 174
sub user {
    my ( $self, $req ) = @_;
Yadd's avatar
Typo  
Yadd committed
175 176 177
    return $req->userData
      || { $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
          || _whatToTrace => 'anonymous' };
178 179 180 181 182 183
}

## @method string userId()
# @return user identifier to log
sub userId {
    my ( $self, $req ) = @_;
Yadd's avatar
Typo  
Yadd committed
184 185 186
    return $req->userData->{ $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
          || '_whatToTrace' }
      || 'anonymous';
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

## @method boolean group(string group)
# @param $group name of the Lemonldap::NG group to test
# @return boolean : true if user is in this group
sub group {
    my ( $self, $req, $group ) = @_;
    return () unless ( $req->userData->{groups} );
    return ( $req->userData->{groups} =~ /\b$group\b/ );
}

## @method PSGI::Response sendError($req,$err,$code)
# Add user di to $err before calling Lemonldap::NG::Common::PSGI::sendError()
# @param $req Lemonldap::NG::Common::PSGI::Request
# @param $err String to push
# @code int HTTP error code (default to 500)
sub sendError {
    my ( $self, $req, $err, $code ) = @_;
    $err ||= $req->error;
Yadd's avatar
Yadd committed
206
    $self->userLogger->warn( '[' . $self->userId($req) . "] $err" );
207 208
    return $self->Lemonldap::NG::Common::PSGI::sendError( $req, $err, $code );
}
Yadd's avatar
Yadd committed
209

210
1;