File Coverage

blib/lib/Lemonldap/NG/Handler/Initialization/GlobalInit.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Lemonldap::NG::Handler::Initialization::GlobalInit;
2              
3             #use Lemonldap::NG::Handler::Main qw(:all);
4 1     1   15678 use Lemonldap::NG::Common::Safelib; #link protected safe Safe object
  0            
  0            
5             use Safe;
6             use constant UNPROTECT => 1;
7             use constant SKIP => 2;
8              
9             use Mouse;
10              
11             use Lemonldap::NG::Handler::Main::Jail;
12             use Lemonldap::NG::Handler::Main::Logger;
13              
14             has customFunctions => ( is => 'rw', isa => 'Maybe[Str]' );
15              
16             has useSafeJail => ( is => 'rw', isa => 'Maybe[Int]' );
17              
18             has safe => ( is => 'rw' );
19              
20             BEGIN {
21             if ( exists $ENV{MOD_PERL} ) {
22             if ( $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ) {
23             eval 'use constant MP => 2;';
24             }
25             else {
26             eval 'use constant MP => 1;';
27             }
28             }
29             else {
30             eval 'use constant MP => 0;';
31             }
32             if ( MP() == 2 ) {
33             eval '
34             use constant OK => Apache2::Const::OK;
35             ';
36             }
37             else { # For Test or CGI
38             eval '
39             use constant OK => 1;
40             ';
41             }
42             }
43              
44             ## @imethod protected void defaultValuesInit(hashRef args)
45             # Set default values for non-customized variables
46             # @param $args reference to the configuration hash
47             sub defaultValuesInit {
48              
49             my (
50             $self, $cookieName, $securedCookie,
51             $whatToTrace, $https, $port,
52             $customFunctions, $timeoutActivity, $useRedirectOnError,
53             $useRedirectOnForbidden, $useSafeJail, $key,
54             $maintenance, $cda, $httpOnly,
55             $cookieExpiration, $cipher, $args,
56             ) = splice @_;
57             foreach my $t (qw(https port maintenance)) {
58              
59             # Skip Handler initialization (values not defined)
60             next unless defined $args->{$t};
61              
62             # Record default value in key '_'
63             $args->{$t} = { _ => $args->{$t} } unless ( ref( $args->{$t} ) );
64              
65             # Override with vhost options
66             if ( defined $args->{vhostOptions} ) {
67             my $n = 'vhost' . ucfirst($t);
68             foreach my $k ( keys %{ $args->{vhostOptions} } ) {
69             foreach my $alias (
70             @{ $self->getAliases( $k, $args->{vhostOptions} ) } )
71             {
72             my $v = $args->{vhostOptions}->{$k}->{$n};
73             Lemonldap::NG::Handler::Main::Logger->lmLog(
74             "Options $t for vhost $alias: $v", 'debug' );
75             $args->{$t}->{$alias} = $v
76             if ( $v >= 0 ); # Keep default value if $v is negative
77             }
78             }
79             }
80             }
81              
82             # Default values are defined in Common::Conf::Attributes
83             # These values should be erased by global configuration
84             $cookieName = $args->{cookieName} || $cookieName;
85             $securedCookie =
86             defined( $args->{securedCookie} )
87             ? $args->{securedCookie}
88             : $securedCookie;
89             $whatToTrace = $args->{whatToTrace} || $whatToTrace;
90             $https = defined($https) ? $https : $args->{https};
91             $port ||= $args->{port};
92             $customFunctions = $args->{customFunctions};
93             $self->customFunctions($customFunctions);
94             $cda = defined($cda) ? $cda : $args->{cda};
95             $httpOnly = defined($httpOnly) ? $httpOnly : $args->{httpOnly};
96             $cookieExpiration = $args->{cookieExpiration} || $cookieExpiration;
97             $timeoutActivity = $args->{timeoutActivity} || $timeoutActivity;
98             $useRedirectOnError =
99             defined($useRedirectOnError)
100             ? $useRedirectOnError
101             : $args->{useRedirectOnError};
102             $useRedirectOnForbidden =
103             defined($useRedirectOnForbidden)
104             ? $useRedirectOnForbidden
105             : $args->{useRedirectOnForbidden};
106             $useSafeJail =
107             defined($useSafeJail)
108             ? $useSafeJail
109             : $args->{useSafeJail};
110             $self->useSafeJail($useSafeJail);
111             $key ||= 'lemonldap-ng-key';
112             $cipher ||= Lemonldap::NG::Common::Crypto->new($key);
113              
114             if ( $args->{key} && ( $args->{key} ne $key ) ) {
115             $key = $args->{key};
116             $cipher = Lemonldap::NG::Common::Crypto->new($key);
117             }
118              
119             $maintenance = defined($maintenance) ? $maintenance : $args->{maintenance};
120              
121             return (
122             $cookieName, $securedCookie, $whatToTrace,
123             $https, $port, $customFunctions,
124             $timeoutActivity, $useRedirectOnError, $useRedirectOnForbidden,
125             $useSafeJail, $key, $maintenance,
126             $cda, $httpOnly, $cookieExpiration,
127             $cipher
128             );
129             1;
130             }
131              
132             ## @imethod protected void portalInit(hashRef args)
133             # Verify that portal variable exists. Die unless
134             # @param $args reference to the configuration hash
135             sub portalInit {
136             my ( $self, $mainClass, $args ) = splice @_;
137             die("portal parameter required") unless ( $args->{portal} );
138             if ( $args->{portal} =~ /[\$\(&\|"']/ ) {
139             my ($portal) = $self->conditionSub( $mainClass, $args->{portal} );
140             eval "sub portal {return &\$portal}";
141             }
142             else {
143             eval "sub portal {return '$args->{portal}'}";
144             }
145             die("Unable to read portal parameter ($@)") if ($@);
146             return ( \&portal, $self->{safe} );
147             1;
148             }
149              
150             ## @imethod void locationRulesInit(hashRef args)
151             # Compile rules.
152             # Rules are stored in $args->{locationRules}->{<virtualhost>} that contains
153             # regexp=>test expressions where :
154             # - regexp is used to test URIs
155             # - test contains an expression used to grant the user
156             #
157             # This function creates 2 hashRef containing :
158             # - one list of the compiled regular expressions for each virtual host
159             # - one list of the compiled functions (compiled with conditionSub()) for each
160             # virtual host
161             # @param $args reference to the configuration hash
162             sub locationRulesInit {
163             my (
164             $self, $mainClass, $locationCount,
165             $defaultCondition, $defaultProtection, $locationCondition,
166             $locationProtection, $locationRegexp, $locationConditionText,
167             $args
168             ) = splice @_;
169             foreach my $vhost ( keys %{ $args->{locationRules} } ) {
170             foreach
171             my $alias ( @{ $self->getAliases( $vhost, $args->{vhostOptions} ) } )
172             {
173             $locationCount->{$alias} = 0;
174             foreach ( sort keys %{ $args->{locationRules}->{$vhost} } ) {
175             if ( $_ eq 'default' ) {
176             (
177             $defaultCondition->{$alias},
178             $defaultProtection->{$alias}
179             )
180             = $self->conditionSub( $mainClass,
181             $args->{locationRules}->{$vhost}->{$_} );
182             }
183             else {
184             (
185             $locationCondition->{$alias}
186             ->[ $locationCount->{$alias} ],
187             $locationProtection->{$alias}
188             ->[ $locationCount->{$alias} ]
189             )
190             = $self->conditionSub( $mainClass,
191             $args->{locationRules}->{$vhost}->{$_} );
192             $locationRegexp->{$alias}->[ $locationCount->{$alias} ] =
193             qr/$_/;
194             $locationConditionText->{$alias}
195             ->[ $locationCount->{$alias} ] =
196             /^\(\?#(.*?)\)/ ? $1 : /^(.*?)##(.+)$/ ? $2 : $_;
197             $locationCount->{$alias}++;
198             }
199             }
200              
201             # Default police
202             ( $defaultCondition->{$alias}, $defaultProtection->{$alias} ) =
203             $self->conditionSub( $mainClass, 'accept' )
204             unless ( $defaultCondition->{$alias} );
205             }
206              
207             }
208              
209             return (
210             $locationCount, $defaultCondition, $defaultProtection,
211             $locationCondition, $locationProtection, $locationRegexp,
212             $locationConditionText, $self->{safe}
213             );
214             1;
215             }
216              
217             ## @imethod protected void globalStorageInit(hashRef args)
218             # Initialize the Apache::Session::* module choosed to share user's variables.
219             # @param $args reference to the configuration hash
220             sub globalStorageInit {
221             my ( $self, $globalStorage, $globalStorageOptions, $args ) = splice @_;
222             $globalStorage = $args->{globalStorage}
223             or die("globalStorage required");
224             eval "use $globalStorage;";
225             die($@) if ($@);
226             $globalStorageOptions = $args->{globalStorageOptions};
227             return ( $globalStorage, $globalStorageOptions );
228             }
229              
230             ## @imethod protected void localSessionStorageInit(hashRef args)
231             # Initialize the Cache::Cache module choosed to cache sessions.
232             # @param $args reference to the configuration hash
233             sub localSessionStorageInit {
234             my ( $self, $localSessionStorage, $localSessionStorageOptions, $args ) =
235             splice @_;
236             $localSessionStorage = $args->{localSessionStorage};
237             $localSessionStorageOptions = $args->{localSessionStorageOptions};
238             return ( $localSessionStorage, $localSessionStorageOptions );
239             }
240              
241             ## @imethod void headerListInit(hashRef args)
242             # Lists the exported HTTP headers into $headerList
243             # @param $args reference to the configuration hash
244             sub headerListInit {
245             my ( $self, $headerList, $args ) = splice @_;
246              
247             foreach my $vhost ( keys %{ $args->{exportedHeaders} } ) {
248             foreach
249             my $alias ( @{ $self->getAliases( $vhost, $args->{vhostOptions} ) } )
250             {
251             my @tmp = keys %{ $args->{exportedHeaders}->{$vhost} };
252             $headerList->{$alias} = \@tmp;
253             }
254             }
255             return $headerList;
256             1;
257             }
258              
259             ## @imethod void forgeHeadersInit(hashRef args)
260             # Create the &$forgeHeaders->{<virtualhost>} subroutines used to insert
261             # headers into the HTTP request.
262             # @param $args reference to the configuration hash
263             sub forgeHeadersInit {
264             my ( $self, $forgeHeaders, $args ) = splice @_;
265              
266             # Creation of the subroutine which will generate headers
267             foreach my $vhost ( keys %{ $args->{exportedHeaders} } ) {
268             foreach
269             my $alias ( @{ $self->getAliases( $vhost, $args->{vhostOptions} ) } )
270             {
271             my %tmp = %{ $args->{exportedHeaders}->{$vhost} };
272             foreach ( keys %tmp ) {
273             $tmp{$_} =~ s/\$(\w+)/\$datas->{$1}/g;
274             $tmp{$_} = $self->regRemoteIp( $tmp{$_} );
275             }
276              
277             my $sub;
278             foreach ( keys %tmp ) {
279             $sub .= "'$_' => join('',split(/[\\r\\n]+/,$tmp{$_})),";
280             }
281              
282             my $jail = Lemonldap::NG::Handler::Main::Jail->new(
283             'safe' => $self->safe,
284             'useSafeJail' => $self->useSafeJail,
285             'customFunctions' => $self->customFunctions
286             );
287             $self->safe( $jail->build_safe() );
288             $forgeHeaders->{$alias} = $jail->jail_reval("sub{$sub}");
289              
290             Lemonldap::NG::Handler::Main::Logger->lmLog(
291             "$self: Unable to forge headers: $@: sub {$sub}", 'error' )
292             if ($@);
293             }
294              
295             }
296             return $forgeHeaders;
297             1;
298             }
299              
300             ## @imethod protected void postUrlInit()
301             # Prepare methods to post form attributes
302             sub postUrlInit {
303             my ( $self, $transform, $args ) = splice @_;
304              
305             # Do nothing if no POST configured
306             return unless ( $args->{post} );
307              
308             # Load required modules
309             eval 'use Apache2::Filter;use URI';
310              
311             # Prepare transform sub
312             $transform = {};
313              
314             # Browse all vhost
315             foreach my $vhost ( keys %{ $args->{post} } ) {
316              
317             foreach
318             my $alias ( @{ $self->getAliases( $vhost, $args->{vhostOptions} ) } )
319             {
320              
321             my $mypost = $args->{post}->{$vhost};
322              
323             # Browse all POST URI
324             while ( my ( $url, $d ) = each( %{ $args->{post}->{$vhost} } ) ) {
325              
326             # Where to POST
327             $d->{postUrl} ||= $url;
328              
329             # Register POST form for POST URL
330             $transform->{$alias}->{$url} = sub {
331             Lemonldap::NG::Handler::Main::PostForm->buildPostForm(
332             $d->{postUrl} );
333             }
334             if ( $url ne $d->{postUrl} );
335              
336             # Get datas to POST
337             my $expr = $d->{expr};
338             my %postdata;
339              
340             # Manage old and new configuration format
341             # OLD: expr => 'param1 => value1, param2 => value2',
342             # NEW : expr => { param1 => value1, param2 => value2 },
343             if ( ref $expr eq 'HASH' ) {
344             %postdata = %$expr;
345             }
346             else {
347             %postdata = split /(?:\s*=>\s*|\s*,\s*)/, $expr;
348             }
349              
350             # Build string for URI::query_form
351             my $tmp;
352             foreach ( keys %postdata ) {
353             $postdata{$_} =~ s/\$(\w+)/\$datas->{$1}/g;
354             $postdata{$_} = "'$postdata{$_}'"
355             if ( $postdata{$_} =~ /^\w+$/ );
356             $tmp .= "'$_'=>$postdata{$_},";
357             }
358              
359             Lemonldap::NG::Handler::Main::Logger->lmLog(
360             "Compiling POST request for $url", 'debug' );
361             $transform->{$alias}->{ $d->{postUrl} } = sub {
362             return
363             Lemonldap::NG::Handler::Main::PostForm->buildPostForm(
364             $d->{postUrl} )
365             if (
366             $Lemonldap::NG::Handler::Main::apacheRequest->method ne
367             'POST' );
368             $Lemonldap::NG::Handler::Main::apacheRequest
369             ->add_input_filter(
370             sub {
371             Lemonldap::NG::Handler::Main::PostForm->postFilter(
372             $tmp, @_ );
373             }
374             );
375             OK;
376             };
377             }
378             }
379             }
380             return $transform;
381             }
382              
383             ## @imethod protected codeRef conditionSub(string cond)
384             # Returns a compiled function used to grant users (used by
385             # locationRulesInit(). The second value returned is a non null
386             # constant if URL is not protected (by "unprotect" or "skip"), 0 else.
387             # @param $cond The boolean expression to use
388             # @return array (ref(sub), int)
389             sub conditionSub {
390             my ( $self, $mainClass, $cond ) = splice @_;
391             my ( $OK, $NOK ) = ( sub { 1 }, sub { 0 } );
392              
393             # Simple cases : accept and deny
394             return ( $OK, 0 )
395             if ( $cond =~ /^accept$/i );
396             return ( $NOK, 0 )
397             if ( $cond =~ /^deny$/i );
398              
399             # Cases unprotect and skip : 2nd value is 1 or 2
400             return ( $OK, UNPROTECT )
401             if ( $cond =~ /^unprotect$/i );
402             return ( $OK, SKIP )
403             if ( $cond =~ /^skip$/i );
404              
405             # Case logout
406             if ( $cond =~ /^logout(?:_sso)?(?:\s+(.*))?$/i ) {
407             my $url = $1;
408             return (
409             $url
410             ? (
411             sub {
412             $Lemonldap::NG::Handler::Main::datas->{_logout} = $url;
413             return 0;
414             },
415             0
416             )
417             : (
418             sub {
419             $Lemonldap::NG::Handler::Main::datas->{_logout} =
420             $self->portal();
421             return 0;
422             },
423             0
424             )
425             );
426             }
427              
428             # Since filter exists only with Apache>=2, logout_app and logout_app_sso
429             # targets are available only for it.
430             # This error can also appear with Manager configured as CGI script
431             if ( $cond =~ /^logout_app/i and MP() < 2 ) {
432             Lemonldap::NG::Handler::Main::Logger->lmLog(
433             "Rules logout_app and logout_app_sso require Apache>=2", 'warn' );
434             return ( sub { 1 }, 0 );
435             }
436              
437             # logout_app
438             if ( $cond =~ /^logout_app(?:\s+(.*))?$/i ) {
439             my $u = $1 || $self->portal();
440             eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} );
441             return (
442             sub {
443             $Lemonldap::NG::Handler::Main::apacheRequest->add_output_filter(
444             sub {
445             return $mainClass->redirectFilter( $u, @_ );
446             }
447             );
448             1;
449             },
450             0
451             );
452             }
453             elsif ( $cond =~ /^logout_app_sso(?:\s+(.*))?$/i ) {
454             eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} );
455             my $u = $1 || $self->portal();
456             return (
457             sub {
458             $mainClass->localUnlog;
459             $Lemonldap::NG::Handler::Main::apacheRequest->add_output_filter(
460             sub {
461             return $mainClass->redirectFilter(
462             $self->portal() . "?url="
463             . $mainClass->encodeUrl($u)
464             . "&logout=1",
465             @_
466             );
467             }
468             );
469             1;
470             },
471             0
472             );
473             }
474              
475             # Replace some strings in condition
476             $cond =~ s/\$date/&POSIX::strftime("%Y%m%d%H%M%S",localtime())/e;
477             $cond =~ s/\$(\w+)/\$datas->{$1}/g;
478             $cond =~ s/\$datas->{vhost}/\$apacheRequest->hostname/g;
479              
480             my $jail = Lemonldap::NG::Handler::Main::Jail->new(
481             'safe' => $self->safe,
482             'useSafeJail' => $self->useSafeJail,
483             'customFunctions' => $self->customFunctions
484             );
485             $self->safe( $jail->build_safe() );
486             my $sub = $jail->jail_reval("sub{return($cond)}");
487              
488             # Return sub and protected flag
489             return ( $sub, 0 );
490             }
491              
492             ## @method arrayref getAliases(scalar vhost, hashref options)
493             # Check aliases of a vhost
494             # @param vhost vhost name
495             # @param options vhostOptions configuration item
496             # @return arrayref of vhost and aliases
497             sub getAliases {
498             my ( $self, $vhost, $options ) = splice @_;
499             my $aliases = [$vhost];
500              
501             if ( $options->{$vhost}->{vhostAliases} ) {
502             foreach ( split /\s+/, $options->{$vhost}->{vhostAliases} ) {
503             push @$aliases, $_;
504             Lemonldap::NG::Handler::Main::Logger->lmLog(
505             "$_ is an alias for $vhost", 'debug' );
506             }
507             }
508              
509             return $aliases;
510             }
511              
512             ## @ifn protected string protected regRemoteIp(string str)
513             # Replaces $ip by the client IP address in the string
514             # @param $str string
515             # @return string
516             sub regRemoteIp {
517             my ( $self, $str ) = splice @_;
518             $str =~ s/\$datas->\{ip\}/ip()/g;
519             return $str;
520             }
521              
522             1;