Manager.pm 23.6 KB
Newer Older
1 2 3 4 5
package Lemonldap::NG::Manager;

use strict;

use XML::Simple;
6

7 8
use Lemonldap::NG::Manager::Base;
use Lemonldap::NG::Manager::Conf;
9
use Lemonldap::NG::Manager::_HTML;
10 11
require Lemonldap::NG::Manager::_i18n;
require Lemonldap::NG::Manager::Help;
12
use Lemonldap::NG::Manager::Conf::Constants;
13
use LWP::UserAgent;
14 15
use Safe;
use MIME::Base64;
16 17 18

our @ISA = qw(Lemonldap::NG::Manager::Base);

19
our $VERSION = '0.7';
20 21

sub new {
22
    my ( $class, $args ) = @_;
23
    my $self = $class->SUPER::new();
24 25 26
    unless ($args) {
        print STDERR "parameters are required, I can't start so\n";
        return 0;
27
    }
28 29 30 31 32
    %$self = ( %$self, %$args );
    foreach (qw(configStorage dhtmlXTreeImageLocation)) {
        unless ( $self->{$_} ) {
            print STDERR qq/The "$_" parameter is required\n/;
            return 0;
33 34
        }
    }
35 36 37
    $self->{jsFile} ||= $self->_dir . "lemonldap-ng-manager.js";
    unless ( -r $self->{jsFile} ) {
        print STDERR qq#Unable to read $self->{jsFile}. You have to set "jsFile" parameter to /path/to/lemonldap-ng-manager.js\n#;
38
    }
39 40 41
    unless ( __PACKAGE__->can('ldapServer') ) {
        Lemonldap::NG::Manager::_i18n::import( $ENV{HTTP_ACCEPT_LANGUAGE} );
    }
42
    if ( $self->param('lmQuery') ) {
43
        my $tmp = "print_" . $self->param('lmQuery');
44
        $self->$tmp;
45 46
    }
    else {
47 48 49 50 51 52 53
        my $datas;
        if ( $datas = $self->param('POSTDATA') ) {
            $self->print_upload( \$datas );
        }
        else {
            return $self;
        }
54 55 56 57 58 59 60
    }
    exit;
}

# Subroutines to make all the work
sub doall {
    my $self = shift;
61 62 63
    # When using header_public here, Firefox does not load configuration
    # sometimes. Where is the bug ?
    print $self->header;
64 65 66 67 68 69 70 71
    print $self->start_html;
    print $self->main;
    print $self->end_html;
}

# CSS and Javascript export
sub print_css {
    my $self = shift;
72
    print $self->header_public( $ENV{SCRIPT_FILENAME}, -type => 'text/css' );
73 74 75 76 77
    $self->css;
}

sub print_libjs {
    my $self = shift;
78
    print $self->header_public( $self->{jsFile}, -type => 'application/x-javascript' );
79
    open F, $self->{jsFile};
80 81
    while (<F>) {
        print;
82 83 84 85 86 87
    }
    close F;
}

sub print_lmjs {
    my $self = shift;
88
    print $self->header_public( $ENV{SCRIPT_FILENAME}, -type => 'text/javascript' );
89 90 91 92 93 94 95
    $self->javascript;
}

# HELP subroutines

sub print_help {
    my $self = shift;
96
    print $self->header_public;
97 98 99 100
    Lemonldap::NG::Manager::Help::import( $ENV{HTTP_ACCEPT_LANGUAGE} )
      unless ( $self->can('help_groups') );
    my $chap = $self->param('help');
    eval { no strict "refs"; &{"help_$chap"} };
101 102
}

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
# Delete subroutine

sub print_delete {
    my $self = shift;
    print $self->header;
    Lemonldap::NG::Manager::Help::import( $ENV{HTTP_ACCEPT_LANGUAGE} )
      unless ( $self->can('help_groups') );
    if ( $self->config->delete ( $self->param ( 'cfgNum' ) ) ) {
        print &txt_configurationDeleted;
    }
    else {
        print &txt_configurationNotDeleted;
    }
    exit;
}

119 120 121 122
# Configuration download subroutines
sub print_conf {
    my $self = shift;
    print $self->header( -type => "text/xml", '-Cache-Control' => 'private' );
123
    $self->printXmlConf( { cfgNum => $self->param ( 'cfgNum' ), } );
124 125 126 127 128
    exit;
}

sub default {
    return {
129 130 131
        cfgNum   => 0,
        ldapBase => "dc=example,dc=com",
    };
132 133 134
}

sub printXmlConf {
135 136
    my $self   = shift;
    print XMLout(
137
        $self->buildTree( @_ ),
138 139 140 141 142 143 144 145
        #XMLDecl  => "<?xml version='1.0' encoding='iso-8859-1'?>",
        RootName => 'tree',
        KeyAttr  => { item => 'id', username => 'name' },
        NoIndent => 1
    );
}

sub buildTree {
146
    my $self   = shift;
147
    my $config = $self->config->getConf( @_ );
148 149
    $config = $self->default unless ($config);
    my $tree = {
150 151 152 153
        id   => '0',
        item => {
            id   => 'root',
            open => 1,
154
            text => &txt_configuration . " $config->{cfgNum}",
155 156
            item => {
                generalParameters => {
157
                    text => &txt_generalParameters,
158 159
                    item => {
                        exportedVars => {
160
                            text => &txt_exportedVars,
161 162
                            item => {},
                        },
Xavier Guimard's avatar
Xavier Guimard committed
163
                        macros => {
164
                            text => &txt_macros,
Xavier Guimard's avatar
Xavier Guimard committed
165
                        },
166
                        ldapParameters => {
167
                            text => &txt_ldapParameters,
168 169 170
                            item => {},
                        },
                        sessionStorage => {
171
                            text => &txt_sessionStorage,
172
                            item => {
173
                                globalStorageOptions =>
174
                                  { text => &txt_globalStorageOptions, }
175 176
                            },
                        },
177
                        authParams => {
178
                            text => &txt_authParams,
179 180
                            item => {},
                        },
181 182
                    },
                },
183
                groups       => { text => &txt_userGroups, },
184
                virtualHosts => {
185 186 187
                    text   => &txt_virtualHosts,
                    open   => 1,
                    select => 1,
188 189 190 191 192
                },
            },
        },
    };
    my $generalParameters = $tree->{item}->{item}->{generalParameters}->{item};
193
    my $exportedVars =
194
      $tree->{item}->{item}->{generalParameters}->{item}->{exportedVars}->{item};
195
    my $ldapParameters =
196
      $tree->{item}->{item}->{generalParameters}->{item}->{ldapParameters}->{item};
197
    my $sessionStorage =
198
      $tree->{item}->{item}->{generalParameters}->{item}->{sessionStorage}->{item};
199
    my $globalStorageOptions =
200
      $tree->{item}->{item}->{generalParameters}->{item}->{sessionStorage}->{item}->{globalStorageOptions}->{item};
201 202 203 204
    my $authParams =
      $tree->{item}->{item}->{generalParameters}->{item}->{authParams}->{item};
    $authParams->{authentication} =
      $self->xmlField( "value", $config->{authentication} || 'ldap',
205
        &txt_authenticationType, );
206
    $authParams->{portal} =
207
      $self->xmlField( "value", $config->{portal} || 'http://portal/',
208
        "Portail" );
209
    $authParams->{securedCookie} =
210 211 212
      $self->xmlField( "value", $config->{securedCookie} || 0, &txt_securedCookie );
    $generalParameters->{whatToTrace} =
      $self->xmlField( "value", $config->{whatToTrace} || '$uid', &txt_whatToTrace );
213

214
    $generalParameters->{domain} =
215
      $self->xmlField( "value", $config->{domain} || 'example.com', &txt_domain, );
216 217
    $generalParameters->{cookieName} =
      $self->xmlField( "value", $config->{cookieName} || 'lemonldap',
218
        &txt_cookieName, );
219

220 221
    $sessionStorage->{globalStorage} =
      $self->xmlField( "value",
222
        $config->{globalStorage} || 'Apache::Session::File',
223
        &txt_apacheSessionModule, );
224 225 226

    $ldapParameters->{ldapServer} =
      $self->xmlField( "value", $config->{ldapServer} || 'localhost',
227
        &txt_ldapServer, );
228
    $ldapParameters->{ldapPort} =
229
      $self->xmlField( "value", $config->{ldapPort} || 389, &txt_ldapPort, );
230
    $ldapParameters->{ldapBase} =
231
      $self->xmlField( "value", $config->{ldapBase} || ' ', &txt_ldapBase, );
232
    $ldapParameters->{managerDn} =
233
      $self->xmlField( "value", $config->{managerDn} || ' ', &txt_managerDn, );
234 235
    $ldapParameters->{managerPassword} =
      $self->xmlField( "value", $config->{managerPassword} || ' ',
236
        &txt_managerPassword, );
237 238 239 240 241 242 243 244 245 246 247 248

    if ( $config->{exportedVars} ) {
        while ( my ( $n, $att ) = each( %{ $config->{exportedVars} } ) ) {
            $exportedVars->{$n} = $self->xmlField( "both", $att, $n );
        }
    }
    else {
        foreach (qw(mail uid cn)) {
            $exportedVars->{$_} = $self->xmlField( 'both', $_, $_ );
        }
    }

249
    if ( $config->{globalStorageOptions} and %{ $config->{globalStorageOptions} } ) {
250
        $tree->{item}->{item}->{generalParameters}->{item}->{sessionStorage}->{item}->{globalStorageOptions}->{item} = {};
251
        $globalStorageOptions =
252
          $tree->{item}->{item}->{generalParameters}->{item}->{sessionStorage}->{item}->{globalStorageOptions}->{item};
253 254
        while ( my ( $n, $opt ) = each( %{ $config->{globalStorageOptions} } ) )
        {
255 256 257 258 259 260 261
            $globalStorageOptions->{$n} = $self->xmlField( "both", $opt, $n );
        }
    }
    else {
    }

    my $indice = 1;
262
    if ( $config->{locationRules} and %{ $config->{locationRules} } ) {
263 264
        $tree->{item}->{item}->{virtualHosts}->{item} = {};
        my $virtualHost = $tree->{item}->{item}->{virtualHosts}->{item};
265
        # TODO: split locationRules into 2 arrays
266 267
        while ( my ( $host, $rules ) = each( %{ $config->{locationRules} } ) ) {
            $virtualHost->{$host} = $self->xmlField( "text", 'i', $host );
268 269
            my ( $ih, $ir ) =
              ( "exportedHeaders_$indice", "locationRules_$indice" );
270
            $virtualHost->{$host}->{item} = {
271 272
                "$ih" => { text => &txt_httpHeaders, },
                "$ir" => { text => &txt_locationRules, },
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
            };
            while ( my ( $reg, $expr ) = each(%$rules) ) {
                my $type = ( $reg eq 'default' ) ? 'value' : 'both';
                $virtualHost->{$host}->{item}->{$ir}->{item}->{"r_$indice"} =
                  $self->xmlField( $type, $expr, $reg );
                $indice++;
            }
            my $headers = $config->{exportedHeaders}->{$host};
            while ( my ( $h, $expr ) = each(%$headers) ) {
                $virtualHost->{$host}->{item}->{$ih}->{item}->{"h_$indice"} =
                  $self->xmlField( "both", $expr, $h );
                $indice++;
            }
        }
    }
288
    if ( $config->{groups} and %{ $config->{groups} } ) {
289 290 291 292 293 294
        $tree->{item}->{item}->{groups}->{item} = {};
        my $groups = $tree->{item}->{item}->{groups}->{item};
        while ( my ( $group, $expr ) = each( %{ $config->{groups} } ) ) {
            $groups->{$group} = $self->xmlField( 'both', $expr, $group );
        }
    }
295
    if ( $config->{macros} and %{ $config->{macros} } ) {
Xavier Guimard's avatar
Xavier Guimard committed
296 297 298 299 300
        $tree->{item}->{item}->{generalParameters}->{item}->{macros}->{item} = {};
        my $macros = $tree->{item}->{item}->{generalParameters}->{item}->{macros}->{item};
        while ( my ( $macro, $expr ) = each( %{ $config->{macros} } ) ) {
            $macros->{$macro} = $self->xmlField( 'both', $expr, $macro );
        }
301
    }
302
    return $tree;
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
}

sub xmlField {
    my ( $self, $type, $value, $text ) = @_;
    $value =~ s/"/\&#34;/g;
    $text  =~ s/"/\&#34;/g;
    return {
        text     => $text,
        aCol     => "#000000",
        sCol     => "#0000FF",
        userdata => [
            { name => 'value', content => $value },
            { name => 'modif', content => $type },
        ],
    };
}

# Upload subroutines
sub print_upload {
322
    my $self  = shift;
323 324 325
    my $datas = shift;
    print $self->header( -type => "text/html" );
    my $tmp = $self->upload($datas);
326
    if ($tmp) {
327 328 329
        print $tmp;
    }
    else {
330
        print 0;
331 332 333 334
    }
}

sub upload {
335
    my $self = shift;
336
    my $config = $self->tree2conf(@_);
337
    return SYNTAX_ERROR unless( $self->checkConf($config) );
338 339 340 341
    return $self->config->saveConf($config);
}

sub tree2conf {
342 343
    my ( $self, $tree ) = @_;
    $tree = XMLin($$tree);
344
    my $config = {};
345 346 347
    # Load config number
    ($config->{cfgNum}) = ($tree->{text} =~ /(\d+)$/);
    # Load groups
348 349
    while ( my ( $g, $h ) = each( %{ $tree->{groups} } ) ) {
        next unless ( ref($h) );
350
        $config->{groups}->{ $h->{text} } = $h->{value};
351
    }
352
    # Load virtualHosts
353 354
    while ( my ( $vh, $h ) = each( %{ $tree->{virtualHosts} } ) ) {
        next unless ( ref($h) );
355 356 357 358 359 360
        my $lr;
        my $eh;
        foreach ( keys(%$h) ) {
            $lr = $h->{$_} if ( $_ =~ /locationRules/ );
            $eh = $h->{$_} if ( $_ =~ /exportedHeaders/ );
        }
361
        # TODO: split locationRules into 2 arrays
362 363 364 365 366 367 368 369 370
      LR: foreach my $r ( values(%$lr) ) {
            next LR unless ( ref($r) );
            $config->{locationRules}->{$vh}->{ $r->{text} } = $r->{value};
        }
      EH: foreach my $h ( values(%$eh) ) {
            next EH unless ( ref($h) );
            $config->{exportedHeaders}->{$vh}->{ $h->{text} } = $h->{value};
        }
    }
371
    # General parameters
372
    $config->{cookieName} = $tree->{generalParameters}->{cookieName}->{value};
373
    $config->{whatToTrace} = $tree->{generalParameters}->{whatToTrace}->{value};
374
    $config->{domain}     = $tree->{generalParameters}->{domain}->{value};
375 376
    $config->{globalStorage} = $tree->{generalParameters}->{sessionStorage}->{globalStorage}->{value};
    while ( my ( $v, $h ) = each( %{ $tree->{generalParameters}->{sessionStorage}->{globalStorageOptions} })) {
377
        next unless ( ref($h) );
378
        $config->{globalStorageOptions}->{ $h->{text} } = $h->{value};
379
    }
380 381
    while ( my ( $v, $h ) = each( %{ $tree->{generalParameters}->{macros} })) {
        next unless ( ref($h) );
Xavier Guimard's avatar
Xavier Guimard committed
382
        $config->{macros}->{ $h->{text} } = $h->{value};
383
    }
384
    foreach (qw(ldapBase ldapPort ldapServer managerDn managerPassword)) {
385 386 387 388
        $config->{$_} =
          $tree->{generalParameters}->{ldapParameters}->{$_}->{value};
        $config->{$_} = '' if ( ref( $config->{$_} ) );
        $config->{$_} =~ s/^\s*(.*?)\s*/$1/;
389 390
    }
    foreach (qw(authentication portal securedCookie)) {
391 392 393
        $config->{$_} = $tree->{generalParameters}->{authParams}->{$_}->{value};
        $config->{$_} = '' if ( ref( $config->{$_} ) );
        $config->{$_} =~ s/^\s*(.*?)\s*/$1/;
394
    }
395 396 397
    while ( my ( $v, $h ) =
        each( %{ $tree->{generalParameters}->{exportedVars} } ) )
    {
398
        next unless ( ref($h) );
399
        $config->{exportedVars}->{$h->{text}} = $h->{value};
400
    }
401
    return $config;
402 403
}

404 405 406 407 408 409 410
sub checkConf {
    my $self = shift;
    my $config = shift;
    my $expr = '';
    # Check cookie name
    return 0 unless( $config->{cookieName} =~ /^\w+$/ );
    # Check domain name
411
    return 0 unless( $config->{domain} =~ /^\w[\w\.\-]*\w$/ );
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    # Load variables
    foreach(keys %{ $config->{exportedVars} }) {
        # Reserved words
        if( $_ eq 'groups' ) {
            print STDERR "$_ is not authorized in attribute names. Change it!\n";
            return 0;
        }
        if( $_ !~ /^\w+$/ ) {
            print STDERR "$_ is not a valid attribute name\n";
            return 0;
        }
        $expr .= "my \$$_ = '1';";
    }
    # Load and check macros
    my $safe = new Safe;
    $safe->share( '&encode_base64' );
    while( my($k, $v) = each( %{ $config->{macros} } ) ) {
        # Reserved words
        if( $k eq 'groups' ) {
            print STDERR "$k is not authorized in macro names. Change it!\n";
            return 0;
        }
        if( $k !~ /^\w+$/ ) {
            print STDERR "$k is not a valid macro name\n";
            return 0;
        }
        $expr .= "my \$$k = $v;";
    }
    # Test macro values;
    $safe->reval( $expr );
    if( $@ ) {
        print STDERR "Error in macro syntax: $@\n";
        return 0;
    }
    # Test groups
    $expr .= 'my $groups;';
    while( my($k,$v) = each( %{ $config->{groups} } ) ) {
        if( $k !~ /^[\w-]+$/ ) {
            print STDERR "$k is not a valid group name\n";
            return 0;
        }
        $safe->reval( $expr . "\$groups = '$k' if($v);");
        if( $@ ) {
            print STDERR "Syntax error in group $k: $@\n";
            return 0;
        }
    }
    # Test rules
    while( my($vh, $rules) = each( %{ $config->{locationRules} } ) ) {
        unless( $vh =~ /^[-\w\.]+$/ ) {
            print STDERR "$vh is not a valid virtual host name\n";
            return 0;
        }
        while( my($reg, $v) = each( %{ $rules } ) ) {
            unless( $reg eq 'default' ) {
                $reg =~ s/#/\\#/g;
                $safe->reval( $expr . "my \$r = qr#$reg#;" );
                if( $@ ) {
                    print STDERR "Syntax error in regexp ($vh -> $reg)\n";
                    return 0;
                }
            }
            unless( $v eq 'deny' or $v eq 'accept' ) {
                $safe->reval( $expr . "my \$r=1 if($v);");
                if( $@ ) {
                    print STDERR "Syntax error in expression ($vh -> $reg)\n";
                    return 0;
                }
            }
        }
    }
    # Test exported headers
    while( my($vh, $headers) = each( %{ $config->{exportedHeaders} } ) ) {
        unless( $vh =~ /^[-\w\.]+$/ ) {
            print STDERR "$vh is not a valid virtual host name\n";
            return 0;
        }
        while( my($header, $v) = each( %{ $headers } ) ) {
            unless( $header =~ /^[\w][-\w]*$/ ) {
                print STDERR "$header is not a valid HTTP header name ($vh)\n";
                return 0;
            }
            $safe->reval( $expr . "my \$r = $v;" );
            if( $@ ) {
                print STDERR "Syntax error in header expression ($vh -> $header)\n";
                return 0;
            }
        }
    }
    1;
}

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
# Apply subroutines
# TODO: Credentials in applyConfFile

sub print_apply {
    my $self = shift;
    print $self->header( -type => "text/html" );
    unless(-r $self->{applyConfFile} ) {
        print "<h3>".&txt_canNotReadApplyConfFile."</h3>";
        return;
    }
    print '<h3>' . &txt_result . ' : </h3><ul>';
    open F, $self->{applyConfFile};
    my $ua = new LWP::UserAgent( requests_redirectable => [] );
    $ua->timeout(10);
    while(<F>) {
        local $| = 1;
        # pass blank lines and comments
        next if(/^$/ or /^\s*#/);
        chomp;
        s/\r//;
        # each line must be like:
        #    host  http(s)://vhost/request/
        my( $host, $request ) = (/^\s*([^\s]+)\s+([^\s]+)$/);
        unless( $host and $request ) {
            print "<li> ".&txt_invalidLine.": $_</li>";
            next;
        }
        my ( $method, $vhost, $uri ) = ( $request =~ /^(https?):\/\/([^\/]+)(.*)$/ );
        unless($vhost) {
            $vhost = $host;
            $uri = $request;
        }
        print "<li>$host ... ";
        my $r = HTTP::Request->new( 'GET', "$method://$host$uri", HTTP::Headers->new( Host => $vhost ) );#, {Host => $vhost} );
        my $response = $ua->request( $r );
        if ( $response->code != 200 ) {
            print join( ' ', &txt_error, ":", $response->code, $response->message, "</li>");
        }
        else {
            print "OK</li>";
        }
    }
    print "</ul><p>" . &txt_changesAppliedLater . "</p>";
}

549 550
# Internal subroutines
sub _dir {
551
    my $d = $ENV{SCRIPT_FILENAME};
552 553 554 555 556 557 558
    $d =~ s#[^/]*$##;
    return $d;
}

sub config {
    my $self = shift;
    return $self->{_config} if $self->{_config};
559 560 561 562
    $self->{_config} =
      Lemonldap::NG::Manager::Conf->new( $self->{configStorage} );
    unless ( $self->{_config} ) {
        die "Configuration not loaded\n";
563 564 565 566
    }
    return $self->{_config};
}

567 568
# Those sub are loaded en demand. With &header_public, they are not loaded each
# time.
569 570 571 572
*css        = *Lemonldap::NG::Manager::_HTML::css;
*javascript = *Lemonldap::NG::Manager::_HTML::javascript;
*main       = *Lemonldap::NG::Manager::_HTML::main;
*start_html = *Lemonldap::NG::Manager::_HTML::start_html;
573

574
__END__
575

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
=head1 NAME

Lemonldap::NG::Manager - Perl extension for managing Lemonldap::NG Web-SSO
system.

=head1 SYNOPSIS

  use Lemonldap::NG::Manager;
  my $h=new Lemonldap::NG::Manager(
      {
        configStorage=>{
            type=>'File',
            dirName=>"/tmp/",
        },
        dhtmlXTreeImageLocation=> "/devel/img/",
591 592
        # uncomment this only if lemonldap-ng-manager.js is not in the same
        # directory than your script.
593 594 595 596 597 598 599 600 601 602 603
        # jsFile => /path/to/lemonldap-ng-manager.js,
      }
    ) or die "Unable to start, see Apache logs";
  # Simple
  $h->doall();

You can also peersonalize the HTML code instead of using C<doall()>:

  print $self->header_public;
  print $self->start_html (  # See CGI(3) for more about start_html
        -style => "/location/to/my.css",
604
        -title => "Example.com SSO configuration",
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
        );
  # optional HTML code for the top of the page
  print "<img src=...";
  print $self->main;
  # optional HTML code for the footer of the page
  print "<img src=...";
  
  print $self->end_html;

=head1 DESCRIPTION

Lemonldap::NG::Manager provides a web interface to manage Lemonldap::NG Web-SSO
system.

=head2 SUBROUTINES

=over

=item * B<new> (constructor): new instanciates the manager object. It takes the
following arguments:

=over

=item * B<configStorage> (required): a hash reference to the description of the
configuration database system. the key 'type' must be set. Example:

  configStorage => {
      type => "DBI",
      dbiChain    => "DBI:mysql:database=session;host=1.2.3.4",
      dbiUser     => "lemonldap-ng",
      dbiPassword => "pass",
  }

See L<Lemonldap::Manager::NG::Manager::Conf::File> or
L<Lemonldap::Manager::NG::Manager::Conf::DBI> to know which keys are required.

=item * B<dhtmlXTreeImageLocation> (required): the location of the directory
containing dhtmlXTree images (provided in example/imgs). If this parameter
isn't correct, the tree will not appear and you will have sone error in Apache
error logs.

=item * B<jsFile> (optional): the path to the file C<lemonldap-ng-manager.js>.
It is required only if this file is not in the same directory than your script.

649 650 651 652 653 654 655 656 657
=item * B<applyConfFile> (optional): the path to a file containing parameters
to make configuration reloaded by handlers. See C<reload> function in
L<Lemonldap::NG::Handler>. The configuration file must contains lines like:

  # Comments if wanted
  host  http://virtual-host/reload-path

When this parameter is set, an "apply" button is added to the manager menu.

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
=back

=item * B<doall>: subroutine that provide headers and the full html code. Il
simply calls C<header_public>, C<start_html>, C<main> and C<end_html> in this
order.

=item * B<header>: print HTTP headers. See L<CGI> for more.

=item * B<header_public>: print HTTP headers and manage the
C<If-Modified-Since> HTTP header. If it match to the age of the file passed
in first argument, it returns C<HTTP 304 Not Modified> end exit. Else, it
calls C<header> with the other arguments. By default, all elements of the
manager use this mecanism except the configuration itself.

=item * B<start_html>: subroutine that print the HTML headers. you can add
parameters to it; example;

  print start_html(-title     => 'My SSO configuration',
676 677 678 679 680 681
                  -author     => 'fred@capricorn.org',
                  -target     => '_blank',
                  -meta       => {'keywords'=>'pharaoh secret mummy',
                  'copyright' => 'copyright 1996 King Tut'},
                  -style      => {'src'=>'/styles/style1.css'},
                  -BGCOLOR    => 'blue');
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699

See start_html description in L<CGI> for more. Bee carefull with C<-style>
argument. You have to call it like the example above or simply like this:
  -style=> '/styles/style1.css',
All other forms will not work.

=item * B<main>: il produce the main HTML code needed to build the
configuration interface.

=item * B<end_html>: close the HTML code by writing C<'E<lt>/bodyE<gt>E<lt>/htmlE<gt>'>

=back

Other subroutines manage the produce of CSS, Javascripts and of course the
configuration tree (called with AJAX).

=head1 SEE ALSO

700 701
L<Lemonldap::NG::Handler>, L<Lemonldap::NG::Portal>, L<CGI>,
http://wiki.lemonldap.objectweb.org/xwiki/bin/view/NG/Presentation
702 703 704 705 706

=head1 AUTHOR

Xavier Guimard, E<lt>x.guimard@free.frE<gt>

Xavier Guimard's avatar
Xavier Guimard committed
707 708 709 710 711 712 713 714 715 716
=head1 BUG REPORT

Use OW2 system to report bug or ask for features:
L<http://forge.objectweb.org/tracker/?group_id=274>

=head1 DOWNLOAD

Lemonldap::NG is available at
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>

717 718
=head1 COPYRIGHT AND LICENSE

719
Copyright (C) 2006-2007 by Xavier Guimard
720 721 722 723 724 725

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.8 or,
at your option, any later version of Perl 5 you may have available.

=cut
726