LEMONLDAP::NG : New auth architecture in Portal

This commit is contained in:
Xavier Guimard 2008-05-30 04:47:32 +00:00
parent f393d06a3d
commit aca6815c1b
7 changed files with 180 additions and 165 deletions

View File

@ -2,9 +2,10 @@ lemonldap-ng (0.9.2) unstable; urgency=low
* New css in manager * New css in manager
* cleaning Handler code * cleaning Handler code
* Status system for Lemonldap::NG::Handler * Status system for Lemonldap::NG::Handler and for the portal
* Debian Czech translation for debconf (Closes: #483301 / bugs.debian.org)
-- Xavier Guimard <x.guimard@free.fr> Fri, 09 May 2008 22:10:37 +0200 -- Xavier Guimard <x.guimard@free.fr> Wed, 28 May 2008 10:53:28 +0200
lemonldap-ng (0.9.1) unstable; urgency=low lemonldap-ng (0.9.1) unstable; urgency=low

View File

@ -62,6 +62,7 @@ t/99-pod.t
t/Lemonldap-NG-Portal-AuthApache.t t/Lemonldap-NG-Portal-AuthApache.t
t/Lemonldap-NG-Portal-AuthCAS.t t/Lemonldap-NG-Portal-AuthCAS.t
t/Lemonldap-NG-Portal-AuthLA.t t/Lemonldap-NG-Portal-AuthLA.t
t/Lemonldap-NG-Portal-AuthLDAP.t
t/Lemonldap-NG-Portal-AuthSSL.t t/Lemonldap-NG-Portal-AuthSSL.t
t/Lemonldap-NG-Portal-CDA.t t/Lemonldap-NG-Portal-CDA.t
t/Lemonldap-NG-Portal-i18n.t t/Lemonldap-NG-Portal-i18n.t

View File

@ -2,26 +2,26 @@ package Lemonldap::NG::Portal::AuthApache;
use strict; use strict;
use Lemonldap::NG::Portal::Simple; use Lemonldap::NG::Portal::Simple;
our @ISA = qw(Lemonldap::NG::Portal::Simple);
our $VERSION = '0.01'; our $VERSION = '0.02';
our $OVERRIDE = { # By default, authentication is valid if REMOTE_USER environment
# By default, authentication is valid if REMOTE_USER environment # variable is present. Change formateFilter if this does not match with
# variable is present. Change formateFilter if this does not match with # UID.
# UID. sub extractFormInfo {
extractFormInfo => sub { my $self = shift;
my $self = shift; return PE_FORMEMPTY unless ( $self->{user} = $ENV{REMOTE_USER} );
return PE_FORMEMPTY unless( $self->{user} = $ENV{REMOTE_USER} );
# This is needed for Kerberos authentication
$self->{user} =~ s/(.*)@(.*)/$1/g;
PE_OK;
},
# Authentication is made by Apache. # This is needed for Kerberos authentication
authenticate => sub { $self->{user} =~ s/(.*)@(.*)/$1/g;
PE_OK; PE_OK;
}, }
};
# Authentication is made by Apache.
sub authenticate {
PE_OK;
}
1; 1;
__END__ __END__

View File

@ -2,12 +2,12 @@ package Lemonldap::NG::Portal::AuthCAS;
use strict; use strict;
use Lemonldap::NG::Portal::Simple; use Lemonldap::NG::Portal::Simple;
our @ISA = qw(Lemonldap::NG::Portal::Simple);
use AuthCAS; use AuthCAS;
our $VERSION = '0.02'; our $VERSION = '0.03';
our $OVERRIDE = { sub extractFormInfo {
extractFormInfo => sub {
my $self = shift; my $self = shift;
my $cas = new AuthCAS(casUrl => $self->{CAS_url}, my $cas = new AuthCAS(casUrl => $self->{CAS_url},
CAFile => $self->{CAS_CAFile}, CAFile => $self->{CAS_CAFile},
@ -24,12 +24,11 @@ our $OVERRIDE = {
exit; exit;
} }
PE_OK; PE_OK;
}, }
authenticate => sub { sub authenticate {
PE_OK; PE_OK;
}, }
};
1; 1;
__END__ __END__

View File

@ -0,0 +1,77 @@
package Lemonldap::NG::Portal::AuthLDAP;
use strict;
use Lemonldap::NG::Portal::Simple;
our @ISA = qw(Lemonldap::NG::Portal::Simple);
our $VERSION = '0.1';
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;
}
sub authenticate {
my $self = shift;
$self->unbind();
my $err;
return $err unless ( ( $err = $self->connectLDAP ) == PE_OK );
# Check if we use Ppolicy control
if ( $self->{ldapPpolicyControl} ) {
# require Perl module
eval 'require Net::LDAP::Control::PasswordPolicy';
die('Module Net::LDAP::Control::PasswordPolicy not found in @INC')
if ($@);
eval
'use Net::LDAP::Constant qw( LDAP_CONTROL_PASSWORDPOLICY LDAP_PP_ACCOUNT_LOCKED LDAP_PP_PASSWORD_EXPIRED );';
no strict 'subs';
# Create Control object
my $pp = Net::LDAP::Control::PasswordPolicy->new;
# Bind with user credentials
my $mesg = $self->{ldap}->bind(
$self->{dn},
password => $self->{password},
control => [$pp]
);
# Get bind response
return PE_OK if ( $mesg->code == 0 );
# Get server control response
my ($resp) = $mesg->control(LDAP_CONTROL_PASSWORDPOLICY);
if ( defined $resp ) {
my $pp_error = $resp->error;
if ($pp_error) {
return PE_PP_ACCOUNT_LOCKED
if ( $pp_error == LDAP_PP_ACCOUNT_LOCKED );
return PE_PP_PASSWORD_EXPIRED
if ( $pp_error == LDAP_PP_PASSWORD_EXPIRED );
}
else {
return PE_BADCREDENTIALS;
}
}
else {
return PE_LDAPERROR;
}
}
else {
return PE_BADCREDENTIALS
unless (
$self->_bind( $self->{ldap}, $self->{dn}, $self->{password} ) );
}
$self->{sessionInfo}->{authenticationLevel} = 2;
PE_OK;
}
1;

View File

@ -2,6 +2,9 @@ package Lemonldap::NG::Portal::AuthSSL;
use strict; use strict;
use Lemonldap::NG::Portal::Simple; use Lemonldap::NG::Portal::Simple;
use Lemonldap::NG::Portal::AuthLDAP;
our @ISA = qw(Lemonldap::NG::Portal::AuthLDAP Lemonldap::NG::Portal::Simple);
our $VERSION = '0.1'; our $VERSION = '0.1';
@ -9,70 +12,71 @@ our $VERSION = '0.1';
# Directory. # Directory.
# So authenticate is overloaded to return only PE_OK. # So authenticate is overloaded to return only PE_OK.
our $OVERRIDE = { # By default, authentication is valid if SSL_CLIENT_S_DN_Email environment
# variable is present. Adapt it if you want
sub extractFormInfo {
my $self = shift;
# By default, authentication is valid if SSL_CLIENT_S_DN_Email environment # Defaults values
# variable is present. Adapt it if you want $self->{SSLRequire} = 1 unless ( defined $self->{SSLRequire} );
extractFormInfo => sub { $self->{SSLVar} ||= 'SSL_CLIENT_S_DN_Email';
my $self = shift; $self->{SSLLDAPField} ||= 'mail';
# Defaults values my $user = $self->https ? $ENV{ $self->{SSLVar} } : 0;
$self->{SSLRequire} = 1 unless ( defined $self->{SSLRequire} ); if ($user) {
$self->{SSLVar} ||= 'SSL_CLIENT_S_DN_Email'; $self->{sessionInfo}->{authenticationLevel} = 5;
$self->{SSLLDAPField} ||= 'mail'; $self->{user} = $user;
return PE_OK;
}
elsif ( $self->{SSLRequire} ) {
return PE_CERTIFICATEREQUIRED;
}
return $self->SUPER::extractFormInfo(@_);
}
my $user = $self->https ? $ENV{$self->{SSLVar}} : 0; # As we know only user mail (or SSLVar), we have to use it to find him in
if ($user) { # the LDAP directory
$self->{sessionInfo}->{authenticationLevel} = 5; sub formateFilter {
$self->{user} = $user; my $self = shift;
return PE_OK; if ( $self->{sessionInfo}->{authenticationLevel}
} and $self->{sessionInfo}->{authenticationLevel} > 4 )
elsif ( $self->{SSLRequire} ) { {
return PE_CERTIFICATEREQUIRED; $self->{filter} = '(&('
} . $self->{SSLLDAPField} . '='
return $self->extractFormInfo(@_); . $self->{user}
}, . ")(objectClass=person))";
return PE_OK;
}
return $self->SUPER::formateFilter(@_);
}
# As we know only user mail (or SSLVar), we have to use it to find him in # Apache SSL environment variable are available in exportedVars:
# the LDAP directory sub setSessionInfo {
formateFilter => sub { my $self = shift;
my $self = shift; my $save = $self->{exportedVars};
if ( $self->{sessionInfo}->{authenticationLevel} and $self->{sessionInfo}->{authenticationLevel} > 4 ) { if ( ref( $self->{exportedVars} ) eq 'HASH' ) {
$self->{filter} = '(&(' foreach ( keys %{ $self->{exportedVars} } ) {
. $self->{SSLLDAPField} . '=' if (/^SSL/) {
. $self->{user} $self->{sessionInfo}->{$_} = $ENV{$_};
. ")(objectClass=person))"; delete $self->{exportedVars}->{$_};
return PE_OK;
}
return $self->formateFilter(@_);
},
# Apache SSL environment variable are available in exportedVars:
setSessionInfo => sub {
my $self = shift;
my $save = $self->{exportedVars};
if ( ref( $self->{exportedVars} ) eq 'HASH' ) {
foreach ( keys %{ $self->{exportedVars} } ) {
if (/^SSL/) {
$self->{sessionInfo}->{$_} = $ENV{$_};
delete $self->{exportedVars}->{$_};
}
} }
} }
my $r = $self->setSessionInfo(@_); }
$self->{exportedVars} = $save; my $r = $self->SUPER::setSessionInfo(@_);
return $r; $self->{exportedVars} = $save;
}, return $r;
}
# If authentication has been done with SSL, LDAP bind is disabled # If authentication has been done with SSL, LDAP bind is disabled
authenticate => sub { sub authenticate {
my $self = shift; my $self = shift;
if ( $self->{sessionInfo}->{authenticationLevel} and $self->{sessionInfo}->{authenticationLevel} > 4 ) { if ( $self->{sessionInfo}->{authenticationLevel}
return PE_OK; and $self->{sessionInfo}->{authenticationLevel} > 4 )
} {
return $self->authenticate(@_); return PE_OK;
}, }
}; return $self->SUPER::authenticate(@_);
}
1; 1;
__END__ __END__

View File

@ -61,21 +61,14 @@ sub new {
$self->{securedCookie} ||= 0; $self->{securedCookie} ||= 0;
$self->{cookieName} ||= "lemonldap"; $self->{cookieName} ||= "lemonldap";
$self->{ldapPpolicyControl} ||= 0; $self->{ldapPpolicyControl} ||= 0;
$self->{authentication} ||= 'LDAP';
$self->{authentication} =~ s/^ldap/LDAP/;
if ( $self->{authentication} and $self->{authentication} ne "ldap" ) { # $Lemonldap::NG::Portal::AuthSSL::OVERRIDE does not overload $self
# variables: if the administrator has defined a sub, we respect it
# $Lemonldap::NG::Portal::AuthSSL::OVERRIDE does not overload $self eval 'require Lemonldap::NG::Portal::Auth' . $self->{authentication};
# variables: if the administrator has defined a sub, we respect it die($@) if ($@);
my $tmp = bless $self, 'Lemonldap::NG::Portal::Auth' . $self->{authentication};
'require Lemonldap::NG::Portal::Auth'
. $self->{authentication}
. '; $tmp = $Lemonldap::NG::Portal::Auth'
. $self->{authentication}
. '::OVERRIDE;';
eval $tmp;
die($@) if ($@);
%$self = ( %$tmp, %$self );
}
return $self; return $self;
} }
@ -103,6 +96,7 @@ sub error {
# Private sub used to bind to LDAP server both with Lemonldap::NG account and user # Private sub used to bind to LDAP server both with Lemonldap::NG account and user
# credentials if LDAP authentication is used # credentials if LDAP authentication is used
sub _bind { sub _bind {
my $self = shift;
my ( $ldap, $dn, $password ) = @_; my ( $ldap, $dn, $password ) = @_;
my $mesg; my $mesg;
if ( $dn and $password ) { # named bind if ( $dn and $password ) { # named bind
@ -270,15 +264,8 @@ sub existingSession {
# 3. In ldap authentication scheme, we load here user and password from HTML # 3. In ldap authentication scheme, we load here user and password from HTML
# form # form
sub extractFormInfo {
my $self = shift; # sub extractFormInfo has to be defined in Auth module used
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;
}
# Unused. You can overload if you have to modify user and password before # Unused. You can overload if you have to modify user and password before
# authentication # authentication
@ -290,7 +277,7 @@ sub formateParams() {
# it with Active Directory, overload it to use CN instead of UID. # it with Active Directory, overload it to use CN instead of UID.
sub formateFilter { sub formateFilter {
my $self = shift; my $self = shift;
$self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=person))"; $self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=inetOrgPerson))";
PE_OK; PE_OK;
} }
@ -333,7 +320,7 @@ sub bind {
$self->connectLDAP unless ( $self->{ldap} ); $self->connectLDAP unless ( $self->{ldap} );
return PE_WRONGMANAGERACCOUNT return PE_WRONGMANAGERACCOUNT
unless ( unless (
&_bind( $self->{ldap}, $self->{managerDn}, $self->{managerPassword} ) ); $self->_bind( $self->{ldap}, $self->{managerDn}, $self->{managerPassword} ) );
PE_OK; PE_OK;
} }
@ -404,62 +391,8 @@ sub unbind {
} }
# 12. Default authentication: LDAP bind with user credentials # 12. Default authentication: LDAP bind with user credentials
sub authenticate {
my $self = shift;
$self->unbind();
my $err;
return $err unless ( ( $err = $self->connectLDAP ) == PE_OK );
# Check if we use Ppolicy control # sub authenticate has to be defined in Auth module used
if ( $self->{ldapPpolicyControl} ) {
# require Perl module
eval 'require Net::LDAP::Control::PasswordPolicy';
die('Module Net::LDAP::Control::PasswordPolicy not found in @INC')
if ($@);
eval
'use Net::LDAP::Constant qw( LDAP_CONTROL_PASSWORDPOLICY LDAP_PP_ACCOUNT_LOCKED LDAP_PP_PASSWORD_EXPIRED );';
no strict 'subs';
# Create Control object
my $pp = Net::LDAP::Control::PasswordPolicy->new;
# Bind with user credentials
my $mesg = $self->{ldap}->bind(
$self->{dn},
password => $self->{password},
control => [$pp]
);
# Get bind response
return PE_OK if ( $mesg->code == 0 );
# Get server control response
my ($resp) = $mesg->control(LDAP_CONTROL_PASSWORDPOLICY);
if ( defined $resp ) {
my $pp_error = $resp->error;
if ($pp_error) {
return PE_PP_ACCOUNT_LOCKED
if ( $pp_error == LDAP_PP_ACCOUNT_LOCKED );
return PE_PP_PASSWORD_EXPIRED
if ( $pp_error == LDAP_PP_PASSWORD_EXPIRED );
}
else {
return PE_BADCREDENTIALS;
}
}
else {
return PE_LDAPERROR;
}
}
else {
return PE_BADCREDENTIALS
unless ( &_bind( $self->{ldap}, $self->{dn}, $self->{password} ) );
}
$self->{sessionInfo}->{authenticationLevel} = 2;
PE_OK;
}
# 13. Now, the user is authenticated. It's time to store his parameters with # 13. Now, the user is authenticated. It's time to store his parameters with
# Apache::Session::* module # Apache::Session::* module
@ -696,7 +629,7 @@ Does nothing. To be overloaded if needed.
Creates the ldap filter using $self->{user}. By default : Creates the ldap filter using $self->{user}. By default :
$self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=person))"; $self->{filter} = "(&(uid=" . $self->{user} . ")(objectClass=inetOrgPerson))";
=head3 connectLDAP =head3 connectLDAP
@ -761,7 +694,7 @@ described above
=head3 _bind( $ldap, $dn, $password ) =head3 _bind( $ldap, $dn, $password )
Non-object method used to bind to the ldap server. Method used to bind to the ldap server.
=head3 header =head3 header