Simple.pm 19.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
package Lemonldap::NG::Portal::Simple;

use strict;
use warnings;

use Exporter 'import';

use Net::LDAP;
use warnings;
use MIME::Base64;
use CGI;
12
use CGI::Cookie;
13
use Lemonldap::NG::Portal::_i18n;
14

15
our $VERSION = '0.63';
16 17 18 19

our @ISA = qw(CGI Exporter);

# Constants
20
sub PE_DONE                { -1 }
21 22 23 24 25 26 27 28 29 30 31 32
sub PE_OK                  { 0 }
sub PE_SESSIONEXPIRED      { 1 }
sub PE_FORMEMPTY           { 2 }
sub PE_WRONGMANAGERACCOUNT { 3 }
sub PE_USERNOTFOUND        { 4 }
sub PE_BADCREDENTIALS      { 5 }
sub PE_LDAPCONNECTFAILED   { 6 }
sub PE_LDAPERROR           { 7 }
sub PE_APACHESESSIONERROR  { 8 }
sub PE_FIRSTACCESS         { 9 }
sub PE_BADCERTIFICATE      { 10 }

Xavier Guimard's avatar
Xavier Guimard committed
33
# EXPORTER PARAMETERS
34 35
our %EXPORT_TAGS = (
    'all' => [
36
        qw( PE_DONE PE_OK PE_SESSIONEXPIRED PE_FORMEMPTY PE_WRONGMANAGERACCOUNT PE_USERNOTFOUND PE_BADCREDENTIALS
37 38 39
          PE_LDAPCONNECTFAILED PE_LDAPERROR PE_APACHESESSIONERROR PE_FIRSTACCESS PE_BADCERTIFICATE import )
    ],
    'constants' => [
40
        qw( PE_DONE PE_OK PE_SESSIONEXPIRED PE_FORMEMPTY PE_WRONGMANAGERACCOUNT PE_USERNOTFOUND PE_BADCREDENTIALS
41 42 43 44 45 46
          PE_LDAPCONNECTFAILED PE_LDAPERROR PE_APACHESESSIONERROR PE_FIRSTACCESS PE_BADCERTIFICATE )
    ],
);

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

47
our @EXPORT =
48
  qw( PE_DONE PE_OK PE_SESSIONEXPIRED PE_FORMEMPTY PE_WRONGMANAGERACCOUNT PE_USERNOTFOUND PE_BADCREDENTIALS
49 50
  PE_LDAPCONNECTFAILED PE_LDAPERROR PE_APACHESESSIONERROR PE_FIRSTACCESS PE_BADCERTIFICATE import );

Xavier Guimard's avatar
Xavier Guimard committed
51
# CONSTRUCTOR
52 53 54 55 56 57 58 59 60 61 62 63 64
sub new {
    my $class = shift;
    my $self  = $class->SUPER::new();
    $self->getConf(@_) or die "Unable to get configuration";
    die("You've to indicate a an Apache::Session storage module !")
      unless ( $self->{globalStorage} );
    eval "require " . $self->{globalStorage};
    die( "Module " . $self->{globalStorage} . " not found in \@INC" ) if ($@);
    die("You've to indicate a domain for cookies") unless ( $self->{domain} );
    $self->{domain} =~ s/^([^\.])/.$1/;
    $self->{ldapServer}    ||= 'localhost';
    $self->{ldapPort}      ||= 389;
    $self->{securedCookie} ||= 0;
65
    $self->{cookieName}    ||= "lemonldap";
66

67 68
    if ( $self->{authentication} ne "ldap" ) {
        require "Lemonldap::NG::Portal::Auth".$self->{authentication};
Xavier Guimard's avatar
Xavier Guimard committed
69 70
        # $Lemonldap::NG::Portal::AuthSSL::OVERRIDE does not overload $self
        # variables: if the administrator has defined a sub, we respect it
71
        %$self = ( %${"Lemonldap::NG::Portal::Auth".$self->{authentication}."::OVERRIDE"}, %$self );
72 73 74 75
    }
    return $self;
}

Xavier Guimard's avatar
Xavier Guimard committed
76
# getConf basic, copy all parameters in $self. Overloaded in SharedConf.pm
77 78 79 80 81 82 83 84 85 86 87 88 89
sub getConf {
    my ($self) = shift;
    my %args;
    if ( ref( $_[0] ) ) {
        %args = %{ $_[0] };
    }
    else {
        %args = @_;
    }
    %$self = ( %$self, %args );
    1;
}

90
# error calls i18n.pm to dysplay error in the wanted language
91 92
sub error {
    my $self = shift;
93
    return &Lemonldap::NG::Portal::_i18n::error( $self->{error}, $ENV{HTTP_ACCEPT_LANGUAGE} );
94 95
}

Xavier Guimard's avatar
Xavier Guimard committed
96 97
# Private sub used to bind to LDAP server both with Lemonldap account and user
# credentials if LDAP authentication is used
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
sub _bind {
    my ( $ldap, $dn, $password ) = @_;
    my $mesg;
    if ( $dn and $password ) {    # named bind
        $mesg = $ldap->bind( $dn, password => $password );
    }
    else {                        # anonymous bind
        $mesg = $ldap->bind();
    }
    if ( $mesg->code() != 0 ) {
        return 0;
    }
    return 1;
}

Xavier Guimard's avatar
Xavier Guimard committed
113
# CGI.pm overload to add Lemonldap::NG cookie
114 115 116 117 118 119 120 121 122 123
sub header {
    my $self = shift;
    if ( $self->{cookie} ) {
        $self->SUPER::header( @_, -cookie => $self->{cookie} );
    }
    else {
        $self->SUPER::header(@_);
    }
}

Xavier Guimard's avatar
Xavier Guimard committed
124
# CGI.pm overload to add Lemonldap::NG cookie
125 126 127 128 129 130 131 132 133 134
sub redirect {
    my $self = shift;
    if ( $_[0]->{cookie} ) {
        $self->SUPER::redirect( @_, -cookie => $_[0]->{cookie} );
    }
    else {
        $self->SUPER::redirect(@_);
    }
}

135 136 137 138
###############################################################
# MAIN subroutine: call all steps until one returns something #
#                  different than PE_OK                       #
###############################################################
Xavier Guimard's avatar
Xavier Guimard committed
139 140 141 142
sub process {
    my ($self) = @_;
    $self->{error} = PE_OK;
    foreach my $sub
Xavier Guimard's avatar
Xavier Guimard committed
143 144 145
      qw(controlUrlOrigin controlExistingSession extractFormInfo formateParams
      formateFilter connectLDAP bind search setSessionInfo setMacros setGroups
      authenticate store unbind buildCookie log autoRedirect) {
Xavier Guimard's avatar
Xavier Guimard committed
146 147 148 149 150 151 152
        if ( $self->{$sub} )
        {
            last if ( $self->{error} = &{ $self->{$sub} }($self) );
        }
        else {
            last if ( $self->{error} = $self->$sub );
        }
153 154
      }
      return ( ( $self->{error} > 0 ) ? 0 : 1 );
Xavier Guimard's avatar
Xavier Guimard committed
155 156 157
}

# 1. If the user was redirected here, we have to load 'url' parameter
158 159 160 161 162 163 164 165
sub controlUrlOrigin {
    my $self = shift;
    if ( $self->param('url') ) {
        $self->{urldc} = decode_base64( $self->param('url') );
    }
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
166
# 2. Control existing sessions
167 168
# what to do with existing sessions ?
#       - delete and create a new session (default)
Xavier Guimard's avatar
Xavier Guimard committed
169
#       - re-authentication (actual scheme)
170 171
#       - nothing: user is authenticated and process
#                  returns true
172
sub controlExistingSession {
173
    my $self = shift;
174 175
    my %cookies = fetch CGI::Cookie;
    # Test if Lemonldap::NG cookie is available
Xavier Guimard's avatar
Xavier Guimard committed
176 177
    if ( my $id = $cookies{$self->{cookieName}}->value ) {
        my %h;
178 179
        # Trying to recover session from global session storage
        eval {
Xavier Guimard's avatar
Xavier Guimard committed
180
            tie %h, $self->{globalStorage}, $id, $self->{globalStorageOptions};
181
        };
Xavier Guimard's avatar
Xavier Guimard committed
182
        if ( $@ or not tied(%h) ) {
183
            # Session not available (expired ?)
Xavier Guimard's avatar
Xavier Guimard committed
184
            print STDERR "Session $id isn't yet available ($ENV{REMOTE_ADDR})\n";
185 186
            return PE_OK;
        }
Xavier Guimard's avatar
Xavier Guimard committed
187 188 189 190 191 192

	# Logout if required
        if($self->param('logout')) {
            tied(%h)->delete;
            return PE_FIRSTACCESS;
        }
193
        # A session has been find => calling &existingSession
Xavier Guimard's avatar
Xavier Guimard committed
194 195 196
        my($r, $datas);
        %$datas = %h;
        untie(%h);
197
        if ( $self->{existingSession} ) {
Xavier Guimard's avatar
Xavier Guimard committed
198
            $r = &{ $self->{existingSession} }($self, $id, $datas)
199 200
        }
        else {
Xavier Guimard's avatar
Xavier Guimard committed
201
            $r = $self->existingSession($id, $datas);
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
        }
        if ( $r == PE_DONE) {
            for my $sub qw(log autoRedirect) {
                if ( $self->{$sub} ) {
                    last if ( $self->{error} = &{ $self->{$sub} }($self) );
                }
                else {
                    last if ( $self->{error} = $self->$sub );
                }
            }
            return $self->{error} || PE_DONE;
        }
        else {
            return $r;
        }
    }
    PE_OK;
}

sub existingSession {
    my ($self, $id, $datas) = @_;
223 224 225
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
226 227
# 3. In ldap authentication scheme, we load here user and password from HTML
#    form
228 229 230 231 232 233 234 235 236 237
sub extractFormInfo {
    my $self = shift;
    return PE_FIRSTACCESS
      unless ( $self->param('user') );
    return PE_FORMEMPTY
      unless ( length( $self->{'user'} = $self->param('user') ) > 0
        && length( $self->{'password'} = $self->param('password') ) > 0 );
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
238 239
# Unused. You can overload if you have to modify user and password before
# authentication
240 241 242 243
sub formateParams() {
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
244 245
# 4. By default, the user is searched in the LDAP server with its UID. To use
#    it with Active Directory, overload it to use CN instead of UID.
246 247 248 249 250 251
sub formateFilter {
    my $self = shift;
    $self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=person))";
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
252
# 5. First LDAP connexion used to find user DN with the filter defined before.
253 254 255 256 257 258 259 260 261 262 263 264 265 266
sub connectLDAP {
    my $self = shift;
    return PE_LDAPCONNECTFAILED
      unless (
        $self->{ldap}
        or $self->{ldap} = Net::LDAP->new(
            $self->{ldapServer},
            port    => $self->{ldapPort},
            onerror => undef,
        )
      );
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
267
# 6. LDAP bind with Lemonldap account or anonymous unless defined
268 269 270 271
sub bind {
    my $self = shift;
    $self->connectLDAP unless ( $self->{ldap} );
    return PE_WRONGMANAGERACCOUNT
272 273
      unless (
        &_bind( $self->{ldap}, $self->{managerDn}, $self->{managerPassword} ) );
274 275 276
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
277
# 7. Search the DN
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
sub search {
    my $self = shift;
    my $mesg = $self->{ldap}->search(
        base   => $self->{ldapBase},
        scope  => 'sub',
        filter => $self->{filter},
    );
    if ( $mesg->code() != 0 ) {
        print STDERR $mesg->error . "\n";
        return PE_LDAPERROR;
    }
    return PE_USERNOTFOUND unless ( $self->{entry} = $mesg->entry(0) );
    $self->{dn} = $self->{entry}->dn();
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
294 295 296
# 8. Load all parameters included in exportedVars parameter.
#    Multi-value parameters are loaded in a single string with
#    '; ' separator
297 298 299 300 301
sub setSessionInfo {
    my ($self) = @_;
    $self->{sessionInfo}->{dn} = $self->{dn};
    unless ( $self->{exportedVars} ) {
        foreach (qw(uid cn mail)) {
Xavier Guimard's avatar
Xavier Guimard committed
302
            $self->{sessionInfo}->{$_} = join( '; ', $self->{entry}->get_value($_) || ("") );
303 304 305 306
        }
    }
    elsif ( ref( $self->{exportedVars} ) eq 'HASH' ) {
        foreach ( keys %{ $self->{exportedVars} } ) {
Xavier Guimard's avatar
Xavier Guimard committed
307
            $self->{sessionInfo}->{$_} = join( '; ', $self->{entry}->get_value( $self->{exportedVars}->{$_} ) || ("") );
308 309 310 311
        }
    }
    else {
        foreach ( @{ $self->{exportedVars} } ) {
Xavier Guimard's avatar
Xavier Guimard committed
312
            $self->{sessionInfo}->{$_} = join( '; ', $self->{entry}->get_value($_) || ("") );
313 314 315 316 317
        }
    }
    PE_OK;
}

Xavier Guimard's avatar
Xavier Guimard committed
318
# 9. Unused here, but overloaded in SharedConf.pm
319 320 321 322 323
sub setMacros {
    PE_OK;
}

# 10. Unused here, but overloaded in SharedConf.pm
324 325 326 327
sub setGroups {
    PE_OK;
}

328
# 11. Now, LDAP will not be used by Lemonldap except for LDAP
Xavier Guimard's avatar
Xavier Guimard committed
329
#     authentication scheme
330 331 332 333 334 335 336
sub unbind {
    my $self = shift;
    $self->{ldap}->unbind if $self->{ldap};
    delete $self->{ldap};
    PE_OK;
}

337
# 12. Default authentication: LDAP bind with user credentials
338 339 340 341 342 343 344 345 346 347 348
sub authenticate {
    my $self = shift;
    return PE_OK if ( $self->{id} );
    $self->unbind();
    my $err;
    return $err unless ( ( $err = $self->connectLDAP ) == PE_OK );
    return PE_BADCREDENTIALS
      unless ( &_bind( $self->{ldap}, $self->{dn}, $self->{password} ) );
    PE_OK;
}

349
# 13. Now, the user is authenticated. It's time to store his parameters with
Xavier Guimard's avatar
Xavier Guimard committed
350
#     Apache::Session::* module
351 352 353
sub store {
    my ($self) = @_;
    my %h;
354 355 356
    eval {
        tie %h, $self->{globalStorage}, undef, $self->{globalStorageOptions};
    };
357 358
    return PE_APACHESESSIONERROR if ($@);
    $self->{id} = $h{_session_id};
359 360
    $h{$_} = $self->{sessionInfo}->{$_}
      foreach ( keys %{ $self->{sessionInfo} } );
361 362 363 364 365
    $h{_utime} = time();
    untie %h;
    PE_OK;
}

366
# 14. If all is done, we build the Lemonldap::NG cookie
367 368 369 370 371 372 373 374 375 376 377 378 379
sub buildCookie {
    my $self = shift;
    $self->{cookie} = $self->cookie(
        -name   => $self->{cookieName},
        -value  => $self->{id},
        -domain => $self->{domain},
        -path   => "/",
        -secure => $self->{securedCookie},
        @_,
    );
    PE_OK;
}

380
# 15. By default, nothing is logged. Users actions are logged on applications.
Xavier Guimard's avatar
Xavier Guimard committed
381 382 383 384 385 386 387 388 389 390 391 392 393
#     It's easy to override this in the contructor :
#       my $portal = new Lemonldap::NG::Portal ( {
#                    ...
#                    log => sub {use Sys::Syslog; syslog;
#                                openlog("Portal $$", 'ndelay', 'auth');
#                                syslog('notice', 'User '.$self->{user}.' is authenticated');
#                               },
#                   ...
#                 } );
sub log {
    PE_OK;
}

394
# 16. If the user was redirected to the portal, we will now redirect him
Xavier Guimard's avatar
Xavier Guimard committed
395
#     to the requested URL
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
sub autoRedirect {
    my $self = shift;
    if ( my $u = $self->{urldc} ) {
        print $self->SUPER::redirect(
            -uri    => $u,
            -cookie => $self->{cookie},
            -status => '302 Moved Temporary'
        );

        # Remove this lines if your browsers does not support redirections
        #        print << "EOF";
        #<html>
        #<head>
        #<script language="Javascript">
        #function redirect() {
Xavier Guimard's avatar
Xavier Guimard committed
411
        #        document.location.href='$u';
412 413 414 415
        #}
        #</script>
        #</head>
        #<body onload="redirect();">
Xavier Guimard's avatar
Xavier Guimard committed
416
        #        <h2>The document has moved <a href="$u">HERE</a></h2>
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
        #</body>
        #</html>
        #EOF
        exit;
    }
    PE_OK;
}

1;

__END__

=head1 NAME

Lemonldap::NG::Portal::Simple - Base module for building Lemonldap::NG compatible portals

=head1 SYNOPSIS

  use Lemonldap::NG::Portal::Simple;
  my $portal = new Lemonldap::NG::Portal::Simple(
Xavier Guimard's avatar
Xavier Guimard committed
437
         domain         => 'gendarmerie.defense.gouv.fr',
438
         globalStorage  => 'Apache::Session::MySQL',
Xavier Guimard's avatar
Xavier Guimard committed
439 440 441 442 443 444 445 446 447 448 449 450
         globalStorageOptions => {
           DataSource   => 'dbi:mysql:database=dbname;host=127.0.0.1',
           UserName     => 'db_user',
           Password     => 'db_password',
           TableName    => 'sessions',
           LockDataSource   => 'dbi:mysql:database=dbname;host=127.0.0.1',
           LockUserName     => 'db_user',
           LockPassword     => 'db_password',
         },
         ldapServer     => 'ldap.domaine.com',
         securedCookie  => 1,
         exportedVars  => ["uid","cn","mail","appli"],
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
    );

  if($portal->process()) {
    # Write here the menu with CGI methods. This page is displayed ONLY IF
    # the user was not redirected here.
    print $portal->header; # DON'T FORGET THIS (see L<CGI(3)>)
    print "...";

    # or redirect the user to the menu
    print $portal->redirect( -uri => 'https://portal/menu');
  }
  else {
    # Write here the html form used to authenticate with CGI methods.
    # $portal->error returns the error message if athentification failed
    # Warning: by defaut, input names are "user" and "password"
    print $portal->header; # DON'T FORGET THIS (see L<CGI(3)>)
    print "...";
    print '<form method="POST">';
    # In your form, the following value is required for redirection
    print '<input type="hidden" name="url" value="'.$portal->param('url').'">';
    # Next, login and password
    print 'Login : <input name="user"><br>';
    print 'Password : <input name="password" type="password" autocomplete="off">';
    print '<input type="submit" value="go" />';
    print '</form>';
  }

=head1 DESCRIPTION

Lemonldap::NG::Portal::Simple is the base module for building Lemonldap::NG
compatible portals. You can use it either by inheritance or by writing
anonymous methods like in the example above.

Xavier Guimard's avatar
Xavier Guimard committed
484
See L<Lemonldap::NG::Portal::SharedConf> for a complete example of use of
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 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
Lemonldap::Portal::* libraries.

=head1 METHODS

=head2 Constructor (new)

=head3 Args

=over

=item * ldapServer: server used to retrive session informations and to valid
credentials (localhost by default).

=item * ldapPort: tcp port used by ldap server.

=item * ldapBase: base of the ldap directory.

=item * managerDn: dn to used to connect to ldap server. By default, anonymous
bind is used.

=item * managerPassword: password to used to connect to ldap server. By
default, anonymous bind is used.

=item * securedCookie: set it to 1 if you want to protect user cookies

=item * cookieName: name of the cookie used by Lemonldap (lemon by default)

=item * domain: cookie domain. You may have to give it else the SSO will work
only on your server.

=item * globalStorage: required: L<Apache::Session> library to used to store
session informations

=item * globalStorageOptions: parameters to bind to L<Apache::Session> module

=item * authentication: sheme to authenticate users (default: "ldap"). It can
be set to:

=over

=item * B<SSL>: See L<Lemonldap::NG::Portal::AuthSSL>.

=back

=back

=head2 Methods that can be overloaded

All the functions above can be overloaded to adapt Lemonldap to your
environment. They MUST return one of the exported constants (see above)
and are called in this order by process().

=head3 controlUrlOrigin

If the user was redirected by a Lemonldap NG handler, stores the url that will be
used to redirect the user after authentication.

=head3 controlExistingSession

544 545 546 547 548 549 550 551 552 553
Controls if a previous session is always available. If true, it call the sub
C<existingSession> with two parameters: id and a scalar tied on Apache::Session
module choosed to store sessions. See bellow

=head3 existingSession

This sub is called only if a previous session exists and is available. By
defaults, it returns PE_OK so user is re-authenticated. You can overload it:
for example if existingSession just returns PE_DONE: authenticated users are
not re-authenticated and C<>process> returns true.
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615

=head3 extractFormInfo

Converts form input into object variables ($self->{user} and
$self->{password}).

=head3 formateParams

Does nothing. To be overloaded if needed.

=head3 formateFilter

Creates the ldap filter using $self->{user}. By default :

  $self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=person))";

=head3 connectLDAP

Connects to LDAP server.

=head3 bind

Binds to the LDAP server using $self->{managerDn} and $self->{managerPassword}
if exist. Anonymous bind is provided else.

=head3 search

Retrives the LDAP entry corresponding to the user using $self->{filter}.

=head3 setSessionInfo

Prepares variables to store in central cache (stored temporarily in
C<$self->{sessionInfo}>). It use C<exportedVars> entry (passed to the new sub)
if defined to know what to store else it stores uid, cn and mail attributes.

=head3 setGroups

Does nothing by default.

=head3 authenticate

Authenticates the user by rebinding to the LDAP server using the dn retrived
with search() and the password.

=head3 store

Stores the informations collected by setSessionInfo into the central cache.
The portal connects the cache using the L<Apache::Session> module passed by
the globalStorage parameters (see constructor).

=head3 unbind

Disconnects from the LDAP server.

=head3 buildCookie

Creates the Lemonldap cookie.

=head3 log

Does nothing. To be overloaded if wanted.

Xavier Guimard's avatar
Xavier Guimard committed
616 617 618 619
=head3 autoRedirect

Redirects the user to the url stored by controlUrlOrigin().

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 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
=head2 Other methods

=head3 process

Main method.

=head3 error

Returns the error message corresponding to the error returned by the methods
described above

=head3 _bind( $ldap, $dn, $password )

Non-object method used to bind to the ldap server.

=head3 header

Overloads the CGI::header method to add Lemonldap cookie.

=head3 redirect

Overloads the CGI::redirect method to add Lemonldap cookie.

=head2 EXPORT

=head3 Constants

=over 5

=item * B<PE_OK>: all is good

=item * B<PE_SESSIONEXPIRED>: the user session has expired

=item * B<PE_FORMEMPTY>: Nothing was entered in the login form

=item * B<PE_USERNOTFOUND>: the user was not found in the (ldap) directory

=item * B<PE_WRONGMANAGERACCOUNT>: the account used to bind to LDAP server in order to
find the user distinguished name (dn) was refused by the server

=item * B<PE_BADCREDENTIALS>: bad login or password

=item * B<PE_LDAPERROR>: abnormal error from ldap

=item * B<PE_APACHESESSIONERROR>: abnormal error from Apache::Session

=item * B<PE_FIRSTACCESS>: First access to the portal

=item * B<PE_BADCERTIFICATE>: Wrong certificate

=back

=head1 SEE ALSO

Xavier Guimard's avatar
Xavier Guimard committed
674
L<Lemonldap::NG::Handler>, L<Lemonldap::NG::Portal::SharedConf>, L<CGI>
675 676 677 678 679 680 681

=head1 AUTHOR

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

=head1 COPYRIGHT AND LICENSE

682
Copyright (C) 2005-2007 by Xavier Guimard E<lt>x.guimard@free.frE<gt>
683 684 685 686 687 688

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.4 or,
at your option, any later version of Perl 5 you may have available.

=cut