File Coverage

blib/lib/Lemonldap/NG/Handler/Main.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             ## @file
2             # Base file for Lemonldap::NG handlers
3              
4             ## @class
5             # Base class for Lemonldap::NG handlers.
6             # All methods in handler are class methods: in ModPerl environment, handlers
7             # are always launched without object created.
8             #
9             # The main method is run() who is called by Apache for each requests (using
10             # handler() wrapper).
11             #
12             # The main initialization subroutine is init() who launch localInit() and
13             # globalInit().
14             package Lemonldap::NG::Handler::Main;
15              
16             #use strict;
17              
18 8     8   50174 use MIME::Base64;
  8         8895  
  8         640  
19 8     8   52 use Exporter 'import';
  8         14  
  8         346  
20              
21             #use AutoLoader 'AUTOLOAD';
22 8     8   4806 use Lemonldap::NG::Common::Crypto;
  0            
  0            
23             use Lemonldap::NG::Common::Session;
24             require POSIX;
25             use CGI::Util 'expires';
26             use constant UNPROTECT => 1;
27             use constant SKIP => 2;
28             use constant MAINTENANCE_CODE => 503;
29              
30             #inherits Cache::Cache
31             #inherits Apache::Session
32             #link Lemonldap::NG::Common::Apache::Session::SOAP protected globalStorage
33              
34             our $VERSION = '1.4.1';
35              
36             our %EXPORT_TAGS;
37              
38             our @EXPORT_OK;
39              
40             our @EXPORT;
41              
42             # my @tSharedVar = qw(
43             # cookieName customFunctions defaultCondition
44             # defaultProtection forgeHeaders globalStorage
45             # globalStorageOptions headerList https
46             # key localStorage localStorageOptions
47             # locationCondition locationConditionText locationCount
48             # locationProtection locationRegexp maintenance
49             # port refLocalStorage securedCookie
50             # statusOut statusPipe timeoutActivity
51             # useRedirectOnError useRedirectOnForbidden useSafeJail
52             # whatToTrace
53             # );
54             #
55             # my @nontSharedVar = qw(
56             # safe
57             # cipher datasUpdate transform
58             # cda childInitDone httpOnly
59             # cookieExpiration
60             # );
61             #
62             # non threaded shared vars non being part of $ntsv hashref
63             # (because of share_from in Jail.pm):
64             # $apacheRequest
65             # $datas
66              
67             # Shared variables
68             our ( $apacheRequest, $datas, $tsv, $ntsv, );
69              
70             ##########################################
71             # COMPATIBILITY WITH APACHE AND APACHE 2 #
72             ##########################################
73              
74             BEGIN {
75              
76             # globalStorage and locationRules are set for Manager compatibility only
77             %EXPORT_TAGS = (
78             globalStorage => [qw( )],
79             locationRules => [qw( )],
80             jailSharedVars => [qw( $apacheRequest $datas )],
81             tsv => [qw( $tsv )],
82             ntsv => [qw( $ntsv )],
83             import => [qw( import @EXPORT_OK @EXPORT %EXPORT_TAGS )],
84             headers => [
85             qw(
86             lmHeaderIn lmSetHeaderIn lmHeaderOut
87             lmSetHeaderOut lmSetErrHeaderOut
88             )
89             ],
90             apache => [
91             qw( MP OK REDIRECT FORBIDDEN DONE DECLINED SERVER_ERROR
92             )
93             ],
94             post => [qw(postFilter)],
95             );
96             push( @EXPORT_OK, @{ $EXPORT_TAGS{$_} } ) foreach ( keys %EXPORT_TAGS );
97             $EXPORT_TAGS{all} = \@EXPORT_OK;
98             if ( exists $ENV{MOD_PERL} ) {
99             if ( $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ) {
100             eval 'use constant MP => 2;';
101             }
102             else {
103             eval 'use constant MP => 1;';
104             }
105             }
106             else {
107             eval 'use constant MP => 0;';
108             }
109             if ( MP() == 2 ) {
110             require Apache2::Log;
111             require Apache2::RequestUtil;
112             Apache2::RequestUtil->import();
113             require Apache2::RequestRec;
114             Apache2::RequestRec->import();
115             require Apache2::ServerUtil;
116             Apache2::ServerUtil->import();
117             require Apache2::Connection;
118             Apache2::Connection->import();
119             require Apache2::RequestIO;
120             Apache2::RequestIO->import();
121             require APR::Table;
122             APR::Table->import();
123             require Apache2::URI;
124             Apache2::URI->import();
125             require Apache2::Const;
126             Apache2::Const->import( '-compile', qw(:common :log) );
127             eval '
128             use constant FORBIDDEN => Apache2::Const::FORBIDDEN;
129             use constant REDIRECT => Apache2::Const::REDIRECT;
130             use constant OK => Apache2::Const::OK;
131             use constant DECLINED => Apache2::Const::DECLINED;
132             use constant DONE => Apache2::Const::DONE;
133             use constant SERVER_ERROR => Apache2::Const::SERVER_ERROR;
134             ';
135             eval {
136             require threads::shared;
137             threads::shared::share($tsv);
138             };
139             print "eval error: $@" if ($@);
140             }
141             elsif ( MP() == 1 ) {
142             require Apache;
143             require Apache::Log;
144             require Apache::Constants;
145             Apache::Constants->import(':common');
146             Apache::Constants->import(':response');
147             }
148             else { # For Test or CGI
149             eval '
150             use constant FORBIDDEN => 1;
151             use constant REDIRECT => 1;
152             use constant OK => 1;
153             use constant DECLINED => 1;
154             use constant DONE => 1;
155             use constant SERVER_ERROR => 1;
156             ';
157             }
158             *handler = ( MP() == 2 ) ? \&handler_mp2 : \&handler_mp1;
159             *logout = ( MP() == 2 ) ? \&logout_mp2 : \&logout_mp1;
160             }
161              
162             use Lemonldap::NG::Handler::Initialization::LocalInit;
163             use Lemonldap::NG::Handler::Initialization::GlobalInit;
164             use Lemonldap::NG::Handler::Main::Jail;
165             use Lemonldap::NG::Handler::Main::Headers;
166             use Lemonldap::NG::Handler::Main::PostForm;
167             use Lemonldap::NG::Handler::Main::Logger;
168              
169             ## @rmethod protected int handler_mp2()
170             # Launch run() when used under mod_perl version 2
171             # @return Apache constant
172             sub handler_mp2 : method {
173             shift->run(@_);
174             }
175              
176             ## @rmethod protected int logout_mp2()
177             # Launch unlog() when used under mod_perl version 2
178             # @return Apache constant
179             sub logout_mp2 : method {
180             shift->unlog(@_);
181             }
182              
183             ## @rmethod protected void lmSetApacheUser(Apache2::RequestRec r,string s)
184             # Inform Apache for the data to use as user for logs
185             # @param $r current request
186             # @param $s string to use
187             sub lmSetApacheUser {
188             my ( $class, $r, $s ) = splice @_;
189             return unless ($s);
190             if ( MP() == 2 ) {
191             $r->user($s);
192             }
193             else {
194             $r->connection->user($s);
195             }
196             }
197              
198             ## @rmethod protected void updateStatus(string user,string url,string action)
199             # Inform the status process of the result of the request if it is available.
200             sub updateStatus {
201             my ( $class, $user, $url, $action ) = splice @_;
202             my $statusPipe = $tsv->{statusPipe};
203             eval {
204             print $statusPipe "$user => "
205             . $apacheRequest->hostname
206             . "$url $action\n"
207             if ($statusPipe);
208             };
209             }
210              
211             ## @rmethod protected int forbidden(string uri)
212             # Used to reject non authorized requests.
213             # Inform the status processus and call logForbidden().
214             # @param uri URI requested
215             # @return Apache2::Const::REDIRECT or Apache2::Const::FORBIDDEN
216             sub forbidden {
217             my ( $class, $uri ) = splice @_;
218             if ( $datas->{_logout} ) {
219             $class->updateStatus( $datas->{ $tsv->{whatToTrace} }, $_[0],
220             'LOGOUT' );
221             my $u = $datas->{_logout};
222             $class->localUnlog;
223             return $class->goToPortal( $u, 'logout=1' );
224             }
225             $class->updateStatus( $datas->{ $tsv->{whatToTrace} }, $_[0], 'REJECT' );
226             $apacheRequest->push_handlers(
227             PerlLogHandler => sub {
228             $_[0]->status(FORBIDDEN);
229             $class->logForbidden( $uri, $datas );
230             DECLINED;
231             }
232             );
233              
234             # Redirect or Forbidden?
235             if ( $tsv->{useRedirectOnForbidden} ) {
236             Lemonldap::NG::Handler::Main::Logger->lmLog(
237             "Use redirect for forbidden access", 'debug' );
238             return $class->goToPortal( $uri, 'lmError=403' );
239             }
240             else {
241             Lemonldap::NG::Handler::Main::Logger->lmLog( "Return forbidden access",
242             'debug' );
243             return FORBIDDEN;
244             }
245             }
246              
247             ## @rmethod protected void logForbidden(string uri,hashref datas)
248             # Insert a log in Apache errors log system to inform that the user was rejected.
249             # This method has to be overloaded to use different logs systems
250             # @param $uri uri asked
251             # @param $datas hash re to user's datas
252             sub logForbidden {
253             my ( $class, $uri, $datas ) = splice @_;
254             Lemonldap::NG::Handler::Main::Logger->lmLog(
255             'User "'
256             . $datas->{ $tsv->{whatToTrace} }
257             . '" was reject when he tried to access to '
258             . $uri,
259             'notice'
260             );
261             }
262              
263             ## @rmethod protected void logGranted(string uri)
264             # Insert a log in Apache errors log system to inform that the user was
265             # authorizated. This method has to be overloaded to use different logs systems
266             # @param $uri uri asked
267             sub logGranted {
268             my ( $class, $uri, $datas ) = splice @_;
269             Lemonldap::NG::Handler::Main::Logger->lmLog(
270             'User "'
271             . $datas->{ $tsv->{whatToTrace} }
272             . '" was granted to access to '
273             . $uri,
274             'debug'
275             );
276             }
277              
278             ## @rmethod protected void hideCookie()
279             # Hide Lemonldap::NG cookie to the protected application.
280             sub hideCookie {
281             my $class = shift;
282             Lemonldap::NG::Handler::Main::Logger->lmLog( "removing cookie", 'debug' );
283             my $tmp = Lemonldap::NG::Handler::Main::Headers->lmHeaderIn( $apacheRequest,
284             'Cookie' );
285             $tmp =~ s/$tsv->{cookieName}(http)?=[^,;]*[,;\s]*//og;
286             if ($tmp) {
287             Lemonldap::NG::Handler::Main::Headers->lmSetHeaderIn( $apacheRequest,
288             'Cookie' => $tmp );
289             }
290             else {
291             Lemonldap::NG::Handler::Main::Headers->lmUnsetHeaderIn( $apacheRequest,
292             'Cookie' );
293             }
294             }
295              
296             ## @rmethod protected string encodeUrl(string url)
297             # Encode URl in the format used by Lemonldap::NG::Portal for redirections.
298             # @return Base64 encoded string
299             sub encodeUrl {
300             my ( $class, $url ) = splice @_;
301             $url = $class->_buildUrl($url) if ( $url !~ m#^https?://# );
302             return encode_base64( $url, '' );
303             }
304              
305             ## @rmethod protected int goToPortal(string url, string arg)
306             # Redirect non-authenticated users to the portal by setting "Location:" header.
307             # @param $url Url requested
308             # @param $arg optionnal GET parameters
309             # @return Apache2::Const::REDIRECT
310             sub goToPortal {
311             my ( $class, $url, $arg ) = splice @_;
312             Lemonldap::NG::Handler::Main::Logger->lmLog(
313             "Redirect " . $class->ip() . " to portal (url was $url)", 'debug' );
314             my $urlc_init = $class->encodeUrl($url);
315             Lemonldap::NG::Handler::Main::Headers->lmSetHeaderOut( $apacheRequest,
316             'Location' => $class->portal()
317             . "?url=$urlc_init"
318             . ( $arg ? "&$arg" : "" ) );
319             return REDIRECT;
320             }
321              
322             ## @rmethod protected $ fetchId()
323             # Get user cookies and search for Lemonldap::NG cookie.
324             # @return Value of the cookie if found, 0 else
325             sub fetchId {
326             my $t = Lemonldap::NG::Handler::Main::Headers->lmHeaderIn( $apacheRequest,
327             'Cookie' );
328             my $vhost = $apacheRequest->hostname;
329             my $lookForHttpCookie = $tsv->{securedCookie} =~ /^(2|3)$/
330             && !(
331             defined( $tsv->{https}->{$vhost} )
332             ? $tsv->{https}->{$vhost}
333             : $tsv->{https}->{_}
334             );
335             my $value =
336             $lookForHttpCookie
337             ? ( $t =~ /$tsv->{cookieName}http=([^,; ]+)/o ? $1 : 0 )
338             : ( $t =~ /$tsv->{cookieName}=([^,; ]+)/o ? $1 : 0 );
339              
340             $value = $ntsv->{cipher}->decryptHex( $value, "http" )
341             if ( $value && $lookForHttpCookie && $tsv->{securedCookie} == 3 );
342             return $value;
343             }
344              
345             ## @rmethod protected boolean retrieveSession(id)
346             # Tries to retrieve the session whose index is id
347             # @return true if the session was found, false else
348             sub retrieveSession {
349             my ( $class, $id ) = @_;
350              
351             # 1. Search if the user was the same as previous (very efficient in
352             # persistent connection).
353             return 1
354             if ( defined $datas->{_session_id}
355             and $id eq $datas->{_session_id}
356             and ( time() - $ntsv->{datasUpdate} < 60 ) );
357              
358             # 2. Get the session from cache or backend
359             my $apacheSession = Lemonldap::NG::Common::Session->new(
360             {
361             storageModule => $tsv->{globalStorage},
362             storageModuleOptions => $tsv->{globalStorageOptions},
363             cacheModule => $tsv->{localSessionStorage},
364             cacheModuleOptions => $tsv->{localSessionStorageOptions},
365             id => $id,
366             kind => "SSO",
367             }
368             );
369              
370             unless ( $apacheSession->error ) {
371              
372             $datas = $apacheSession->data;
373              
374             # Update the session to notify activity, if necessary
375             if ( $tsv->{timeoutActivity} ) {
376             $apacheSession->update( { '_lastSeen' => time } );
377              
378             if ( $apacheSession->error ) {
379             Lemonldap::NG::Handler::Main::Logger->lmLog(
380             "Cannot update session $id", 'error' );
381             Lemonldap::NG::Handler::Main::Logger->lmLog(
382             $apacheSession->error, 'error' );
383             }
384             }
385              
386             $datasUpdate = time();
387             return 1;
388             }
389             else {
390             Lemonldap::NG::Handler::Main::Logger->lmLog(
391             "Session $id can't be retrieved", 'info' );
392             Lemonldap::NG::Handler::Main::Logger->lmLog( $apacheSession->error,
393             'info' );
394              
395             return 0;
396             }
397             }
398              
399             sub ip {
400             my $ip = 'unknownIP';
401             eval {
402             $ip =
403             ( MP() == 2 )
404             ? $apacheRequest->connection->remote_ip
405             : $apacheRequest->remote_ip;
406             };
407             return $ip;
408             }
409              
410             # MAIN SUBROUTINE called by Apache (using PerlHeaderParserHandler option)
411              
412             ## @rmethod int run(Apache2::RequestRec apacheRequest)
413             # Main method used to control access.
414             # Calls :
415             # - fetchId()
416             # - retrieveSession()
417             # - lmSetApacheUser()
418             # - grant()
419             # - forbidden() if user is rejected
420             # - sendHeaders() if user is granted
421             # - hideCookie()
422             # - updateStatus()
423             # @param $apacheRequest Current request
424             # @return Apache2::Const value (OK, FORBIDDEN, REDIRECT or SERVER_ERROR)
425             sub run ($$) {
426             my $class;
427             ( $class, $apacheRequest ) = splice @_;
428             return DECLINED unless ( $apacheRequest->is_initial_req );
429             my $args = $apacheRequest->args;
430              
431             # Direct return if maintenance mode is active
432             if ( $class->checkMaintenanceMode() ) {
433              
434             if ( $tsv->{useRedirectOnError} ) {
435             Lemonldap::NG::Handler::Main::Logger->lmLog(
436             "Got to portal with maintenance error code", 'debug' );
437             return $class->goToPortal( '/', 'lmError=' . MAINTENANCE_CODE );
438             }
439             else {
440             Lemonldap::NG::Handler::Main::Logger->lmLog(
441             "Return maintenance error code", 'debug' );
442             return MAINTENANCE_CODE;
443             }
444             }
445              
446             # Cross domain authentication
447             if ( $ntsv->{cda}
448             and $args =~ s/[\?&]?($tsv->{cookieName}(http)?=\w+)$//oi )
449             {
450             my $str = $1;
451             Lemonldap::NG::Handler::Main::Logger->lmLog( 'CDA request', 'debug' );
452             $apacheRequest->args($args);
453             my $redirectUrl = $class->_buildUrl( $apacheRequest->uri );
454             my $redirectHttps = ( $redirectUrl =~ m/^https/ );
455             Lemonldap::NG::Handler::Main::Headers->lmSetErrHeaderOut(
456             $apacheRequest,
457             'Location' => $redirectUrl . ( $args ? "?" . $args : "" ) );
458             Lemonldap::NG::Handler::Main::Headers->lmSetErrHeaderOut(
459             $apacheRequest,
460             'Set-Cookie' => "$str; path=/"
461             . ( $redirectHttps ? "; secure" : "" )
462             . ( $ntsv->{httpOnly} ? "; HttpOnly" : "" )
463             . (
464             $ntsv->{cookieExpiration}
465             ? "; expires=" . expires( $ntsv->{cookieExpiration}, 'cookie' )
466             : ""
467             )
468             );
469             return REDIRECT;
470             }
471             my $uri = $apacheRequest->unparsed_uri();
472             my $uri_orig = $uri;
473             Apache2::URI::unescape_url($uri);
474              
475             my $protection = $class->isUnprotected($uri);
476              
477             if ( $protection == SKIP ) {
478             Lemonldap::NG::Handler::Main::Logger->lmLog( "Access control skipped",
479             "debug" );
480             $class->updateStatus( $class->ip(), $apacheRequest->uri, 'SKIP' );
481             $class->hideCookie;
482             Lemonldap::NG::Handler::Main::Headers->cleanHeaders( $apacheRequest,
483             $tsv->{forgeHeaders}, $tsv->{headerList} );
484             return OK;
485             }
486              
487             my $id;
488              
489             # Try to recover cookie and user session
490             if ( $id = $class->fetchId and $class->retrieveSession($id) ) {
491              
492             # AUTHENTICATION done
493              
494             # Local macros
495             my $kc = keys %$datas; # in order to detect new local macro
496              
497             # ACCOUNTING (1. Inform Apache)
498             $class->lmSetApacheUser( $apacheRequest,
499             $datas->{ $tsv->{whatToTrace} } );
500              
501             # AUTHORIZATION
502             return $class->forbidden($uri)
503             unless ( $class->grant($uri) );
504             $class->updateStatus( $datas->{ $tsv->{whatToTrace} },
505             $apacheRequest->uri, 'OK' );
506              
507             # ACCOUNTING (2. Inform remote application)
508             Lemonldap::NG::Handler::Main::Headers->sendHeaders( $apacheRequest,
509             $tsv->{forgeHeaders} );
510              
511             # Store local macros
512             if ( keys %$datas > $kc and $tsv->{refLocalStorage} ) {
513             Lemonldap::NG::Handler::Main::Logger->lmLog( "Update local cache",
514             "debug" );
515             $tsv->{refLocalStorage}->set( $id, $datas, "10 minutes" );
516             }
517              
518             # Hide Lemonldap::NG cookie
519             $class->hideCookie;
520              
521             # Log
522             $apacheRequest->push_handlers( PerlLogHandler =>
523             sub { $class->logGranted( $uri, $datas ); DECLINED }, );
524              
525             # Catch POST rules
526             Lemonldap::NG::Handler::Main::PostForm->transformUri($uri);
527              
528             return OK;
529             }
530              
531             elsif ( $protection == UNPROTECT ) {
532              
533             # Ignore unprotected URIs
534             Lemonldap::NG::Handler::Main::Logger->lmLog(
535             "No valid session but unprotected access", "debug" );
536             $class->updateStatus( $class->ip(), $apacheRequest->uri, 'UNPROTECT' );
537             $class->hideCookie;
538             Lemonldap::NG::Handler::Main::Headers->cleanHeaders( $apacheRequest,
539             $tsv->{forgeHeaders}, $tsv->{headerList} );
540             return OK;
541             }
542              
543             else {
544              
545             # Redirect user to the portal
546             Lemonldap::NG::Handler::Main::Logger->lmLog( "$class: No cookie found",
547             'info' )
548             unless ($id);
549              
550             # if the cookie was fetched, a log is sent by retrieveSession()
551             $class->updateStatus( $class->ip(), $apacheRequest->uri,
552             $id ? 'EXPIRED' : 'REDIRECT' );
553             return $class->goToPortal($uri_orig);
554             }
555             }
556              
557             ## @rmethod protected boolean checkMaintenanceMode
558             # Check if we are in maintenance mode
559             # @return true if maintenance mode
560             sub checkMaintenanceMode {
561             my ($class) = splice @_;
562             my $vhost = $apacheRequest->hostname;
563             my $_maintenance =
564             ( defined $tsv->{maintenance}->{$vhost} )
565             ? $tsv->{maintenance}->{$vhost}
566             : $tsv->{maintenance}->{_};
567              
568             if ($_maintenance) {
569             Lemonldap::NG::Handler::Main::Logger->lmLog(
570             "Maintenance mode activated", 'debug' );
571             return 1;
572             }
573              
574             return 0;
575             }
576              
577             ## @rmethod int abort(string mess)
578             # Logs message and exit or redirect to the portal if "useRedirectOnError" is
579             # set to true.
580             # @param $mess Message to log
581             # @return Apache2::Const::REDIRECT or Apache2::Const::SERVER_ERROR
582             sub abort {
583             my ( $class, $mess ) = splice @_;
584              
585             # If abort is called without a valid request, fall to die
586             eval {
587             my $args = $apacheRequest->args;
588             my $uri = $apacheRequest->unparsed_uri();
589              
590             # Set error 500 in logs even if "useRedirectOnError" is set
591             $apacheRequest->push_handlers(
592             PerlLogHandler => sub { $_[0]->status(SERVER_ERROR); DECLINED; } );
593             Lemonldap::NG::Handler::Main::Logger->lmLog( $mess, 'error' );
594              
595             # Redirect or die
596             if ( $tsv->{useRedirectOnError} ) {
597             Lemonldap::NG::Handler::Main::Logger->lmLog(
598             "Use redirect for error", 'debug' );
599             return $class->goToPortal( $uri, 'lmError=500' );
600             }
601             else {
602             return SERVER_ERROR;
603             }
604             };
605             die $mess if ($@);
606             }
607              
608             ## @rmethod protected int handler_mp1()
609             # Launch run() when used under mod_perl version 1
610             # @return Apache constant
611             sub handler_mp1 ($$) { shift->run(@_); }
612              
613             ## @rmethod protected int logout_mp1()
614             # Launch unlog() when used under mod_perl version 1
615             # @return Apache constant
616             sub logout_mp1 ($$) { shift->unlog(@_); }
617              
618             ## @imethod void localInit(hashRef args)
619             # instanciate a LocalInit object with variables:
620             # localStorage, localStorageOptions, refLocalStorage, childInitDone
621             # launch localInit method:
622             # - calls purgeCache() to purge the local cache,
623             # - launch the status processus,
624             # - launch childInit (to init / clean local storage)
625             # @param $args reference to the initialization hash
626             sub localInit($$) {
627             my ( $class, $args ) = splice @_;
628              
629             my $localinit = Lemonldap::NG::Handler::Initialization::LocalInit->new(
630             localStorage => $tsv->{localStorage},
631             refLocalStorage => $tsv->{refLocalStorage},
632             localStorageOptions => $tsv->{localStorageOptions},
633             childInitDone => $tsv->{childInitDone},
634             );
635             (
636             @$tsv{
637             qw( localStorage refLocalStorage localStorageOptions statusPipe statusOut )
638             },
639             $ntsv->{childInitDone}
640             ) = $localinit->localInit($args);
641              
642             }
643              
644             ## @imethod void globalInit(hashRef args)
645             # instanciate a GlobalInit object with variables:
646             # customFunctions, useSafeJail, and safe
647             # Global initialization process launches :
648             # - defaultValuesInit()
649             # - portalInit()
650             # - locationRulesInit()
651             # - globalStorageInit()
652             # - localSessionStorageInit()
653             # - headerListInit()
654             # - forgeHeadersInit()
655             # - postUrlInit()
656             # @param $args reference to the configuration hash
657             sub globalInit {
658             my $class = shift;
659              
660             my $globalinit = Lemonldap::NG::Handler::Initialization::GlobalInit->new(
661             customFunctions => $tsv->{customFunctions},
662             useSafeJail => $tsv->{useSafeJail},
663             safe => $ntsv->{safe},
664             );
665              
666             (
667             @$tsv{
668             qw( cookieName securedCookie whatToTrace
669             https port customFunctions
670             timeoutActivity useRedirectOnError useRedirectOnForbidden
671             useSafeJail key maintenance )
672             },
673             @$ntsv{
674             qw( cda httpOnly cookieExpiration
675             cipher
676             )
677             }
678             )
679             = $globalinit->defaultValuesInit(
680             @$tsv{
681             qw( cookieName securedCookie whatToTrace
682             https port customFunctions
683             timeoutActivity useRedirectOnError useRedirectOnForbidden
684             useSafeJail key maintenance )
685             },
686             @$ntsv{
687             qw( cda httpOnly cookieExpiration
688             cipher )
689             },
690             @_
691             );
692              
693             ( *portal, $ntsv->{safe} ) = $globalinit->portalInit( $class, @_ );
694              
695             (
696             @$tsv{
697             qw( locationCount defaultCondition
698             defaultProtection locationCondition
699             locationProtection locationRegexp
700             locationConditionText )
701             },
702             $ntsv->{safe}
703             )
704             = $globalinit->locationRulesInit(
705             $class,
706             @$tsv{
707             qw( locationCount defaultCondition
708             defaultProtection locationCondition
709             locationProtection locationRegexp
710             locationConditionText )
711             },
712             @_
713             );
714              
715             @$tsv{qw( globalStorage globalStorageOptions )} =
716             $globalinit->globalStorageInit(
717             @$tsv{qw( globalStorage globalStorageOptions )}, @_ );
718              
719             @$tsv{qw( localSessionStorage localSessionStorageOptions )} =
720             $globalinit->localSessionStorageInit(
721             @$tsv{qw( localSessionStorage localSessionStorageOptions )}, @_ );
722              
723             $tsv->{headerList} = $globalinit->headerListInit( $tsv->{headerList}, @_ );
724              
725             $tsv->{forgeHeaders} =
726             $globalinit->forgeHeadersInit( $tsv->{forgeHeaders}, @_ );
727              
728             $ntsv->{transform} = $globalinit->postUrlInit( $ntsv->{transform}, @_ );
729              
730             }
731              
732             ## @rmethod boolean grant()
733             # Grant or refuse client using compiled regexp and functions
734             # @return True if the user is granted to access to the current URL
735             sub grant {
736             my ( $class, $uri ) = splice @_;
737             my $vhost = $apacheRequest->hostname;
738             for ( my $i = 0 ; $i < $tsv->{locationCount}->{$vhost} ; $i++ ) {
739             if ( $uri =~ $tsv->{locationRegexp}->{$vhost}->[$i] ) {
740             Lemonldap::NG::Handler::Main::Logger->lmLog(
741             'Regexp "'
742             . $tsv->{locationConditionText}->{$vhost}->[$i]
743             . '" match',
744             'debug'
745             );
746             return &{ $tsv->{locationCondition}->{$vhost}->[$i] }($datas);
747             }
748             }
749             unless ( $tsv->{defaultCondition}->{$vhost} ) {
750             Lemonldap::NG::Handler::Main::Logger->lmLog(
751             "User rejected because VirtualHost \"$vhost\" has no configuration",
752             'warn'
753             );
754             return 0;
755             }
756             Lemonldap::NG::Handler::Main::Logger->lmLog( "$vhost: Apply default rule",
757             'debug' );
758             return &{ $tsv->{defaultCondition}->{$vhost} }($datas);
759             }
760              
761             ## @cmethod private string _buildUrl(string s)
762             # Transform / into http(s?)://:/s
763             # @param $s path
764             # @return URL
765             sub _buildUrl {
766             my ( $class, $s ) = splice @_;
767             my $vhost = $apacheRequest->hostname;
768             my $portString =
769             $tsv->{port}->{$vhost}
770             || $tsv->{port}->{_}
771             || $apacheRequest->get_server_port();
772             my $_https = (
773             defined( $tsv->{https}->{$vhost} )
774             ? $tsv->{https}->{$vhost}
775             : $tsv->{https}->{_}
776             );
777             $portString =
778             ( $_https && $portString == 443 ) ? ''
779             : ( !$_https && $portString == 80 ) ? ''
780             : ':' . $portString;
781             my $url = "http"
782             . ( $_https ? "s" : "" ) . "://"
783             . $apacheRequest->get_server_name()
784             . $portString
785             . $s;
786             Lemonldap::NG::Handler::Main::Logger->lmLog( "Build URL $url", 'debug' );
787             return $url;
788             }
789              
790             ## @rmethod int unprotect()
791             # Used to unprotect an area.
792             # To use it, set "PerlHeaderParserHandler My::Package->unprotect" Apache
793             # configuration file.
794             # It replace run() by doing nothing.
795             # @return Apache2::Const::OK
796             sub unprotect {
797             OK;
798             }
799              
800             ## @rmethod protected void localUnlog()
801             # Delete current user from local cache entry.
802             sub localUnlog {
803             my $class = shift;
804             if ( my $id = $class->fetchId ) {
805              
806             # Delete Apache thread datas
807             if ( $id eq $datas->{_session_id} ) {
808             $datas = {};
809             }
810              
811             # Delete Apache local cache
812             if ( $tsv->{refLocalStorage} and $tsv->{refLocalStorage}->get($id) ) {
813             $tsv->{refLocalStorage}->remove($id);
814             }
815             }
816             }
817              
818             ## @rmethod protected int unlog(Apache::RequestRec apacheRequest)
819             # Call localUnlog() then goToPortal() to unlog the current user.
820             # @return Apache2::Const value returned by goToPortal()
821             sub unlog ($$) {
822             my $class;
823             ( $class, $apacheRequest ) = splice @_;
824             $class->localUnlog;
825             $class->updateStatus( $class->ip(), $apacheRequest->uri, 'LOGOUT' );
826             return $class->goToPortal( '/', 'logout=1' );
827             }
828              
829             ## @rmethod int status(Apache2::RequestRec $r)
830             # Get the result from the status process and launch a PerlResponseHandler to
831             # display it.
832             # @param $r Current request
833             # @return Apache2::Const::OK
834             sub status($$) {
835             my ( $class, $r ) = splice @_;
836              
837             my $statusOut = $tsv->{statusOut};
838             my $statusPipe = $tsv->{statusPipe};
839              
840             Lemonldap::NG::Handler::Main::Logger->lmLog( "$class: request for status",
841             'debug' );
842             return $class->abort("$class: status page can not be displayed")
843             unless ( $statusPipe and $statusOut );
844             $r->handler("perl-script");
845             print $statusPipe "STATUS" . ( $r->args ? " " . $r->args : '' ) . "\n";
846             my $buf;
847             while (<$statusOut>) {
848             last if (/^END$/);
849             $buf .= $_;
850             }
851             if ( MP() == 2 ) {
852             $r->push_handlers(
853             'PerlResponseHandler' => sub {
854             my $r = shift;
855             $r->content_type('text/html; charset=UTF-8');
856             $r->print($buf);
857             OK;
858             }
859             );
860             }
861             else {
862             $r->push_handlers(
863             'PerlHandler' => sub {
864             my $r = shift;
865             $r->content_type('text/html; charset=UTF-8');
866             $r->send_http_header;
867             $r->print($buf);
868             OK;
869             }
870             );
871             }
872             return OK;
873             }
874              
875             ## @rmethod protected int redirectFilter(string url, Apache2::Filter f)
876             # Launch the current HTTP request then redirects the user to $url.
877             # Used by logout_app and logout_app_sso targets
878             # @param $url URL to redirect the user
879             # @param $f Current Apache2::Filter object
880             # @return Apache2::Const::OK
881             sub redirectFilter {
882             my $class = shift;
883             my $url = shift;
884             my $f = shift;
885             unless ( $f->ctx ) {
886              
887             # Here, we can use Apache2 functions instead of lmSetHeaderOut because
888             # this function is used only with Apache2.
889             $f->r->status(REDIRECT);
890             $f->r->status_line("303 See Other");
891             $f->r->headers_out->unset('Location');
892             $f->r->err_headers_out->set( 'Location' => $url );
893             $f->ctx(1);
894             }
895             while ( $f->read( my $buffer, 1024 ) ) {
896             }
897             $class->updateStatus(
898             (
899             $datas->{ $tsv->{whatToTrace} }
900             ? $datas->{ $tsv->{whatToTrace} }
901             : $f->r->connection->remote_ip
902             ),
903             'filter',
904             'REDIRECT'
905             );
906             return OK;
907             }
908              
909             ## @rmethod protected int isUnprotected()
910             # @return 0 if URI is protected,
911             # UNPROTECT if it is unprotected by "unprotect",
912             # SKIP if is is unprotected by "skip"
913             sub isUnprotected {
914             my ( $class, $uri ) = splice @_;
915             my $vhost = $apacheRequest->hostname;
916             for ( my $i = 0 ; $i < $tsv->{locationCount}->{$vhost} ; $i++ ) {
917             if ( $uri =~ $tsv->{locationRegexp}->{$vhost}->[$i] ) {
918             return $tsv->{locationProtection}->{$vhost}->[$i];
919             }
920             }
921             return $tsv->{defaultProtection}->{$vhost};
922             }
923              
924             1;