186 lines
5.2 KiB
Perl
186 lines
5.2 KiB
Perl
##@file
|
|
# LDAP common functions
|
|
|
|
##@class
|
|
# LDAP common functions
|
|
package Lemonldap::NG::Portal::_LDAP;
|
|
|
|
require Net::LDAP; #inherits
|
|
use Exporter;
|
|
use base qw(Exporter Net::LDAP);
|
|
use Lemonldap::NG::Portal::Simple;
|
|
use strict;
|
|
|
|
our @EXPORT = qw(ldap);
|
|
|
|
our $VERSION = '0.11';
|
|
|
|
## @cmethod Lemonldap::NG::Portal::_LDAP new(Lemonldap::NG::Portal::Simple portal)
|
|
# Build a Net::LDAP object using parameters issued from $portal
|
|
# @return Lemonldap::NG::Portal::_LDAP object
|
|
sub new {
|
|
my $class = shift;
|
|
my $portal = shift;
|
|
my $self;
|
|
unless ($portal) {
|
|
$class->abort("$class : portal argument required !");
|
|
}
|
|
my $useTls = 0;
|
|
my $tlsParam;
|
|
my @servers = ();
|
|
foreach my $server ( split /[\s,]+/, $portal->{ldapServer} ) {
|
|
if ( $server =~ m{^ldap\+tls://([^/]+)/?\??(.*)$} ) {
|
|
$useTls = 1;
|
|
$server = $1;
|
|
$tlsParam = $2 || "";
|
|
}
|
|
else {
|
|
$useTls = 0;
|
|
}
|
|
push @servers, $server;
|
|
}
|
|
$self = Net::LDAP->new(
|
|
\@servers,
|
|
onerror => undef,
|
|
( $portal->{ldapPort} ? ( port => $portal->{ldapPort} ) : () ),
|
|
);
|
|
unless ($self) {
|
|
$portal->lmLog( $@, 'error' );
|
|
return 0;
|
|
}
|
|
bless $self, $class;
|
|
if ($useTls) {
|
|
my %h = split( /[&=]/, $tlsParam );
|
|
$h{cafile} = $portal->{caFile} if ( $portal->{caFile} );
|
|
$h{capath} = $portal->{caPath} if ( $portal->{caPath} );
|
|
my $mesg = $self->start_tls(%h);
|
|
if ( $mesg->code ) {
|
|
$portal->lmLog( 'StartTLS failed', 'error' );
|
|
return 0;
|
|
}
|
|
}
|
|
$self->{portal} = $portal;
|
|
return $self;
|
|
}
|
|
|
|
## @method Net::LDAP::Message bind(string dn, %args)
|
|
# Reimplementation of Net::LDAP::bind(). Connection is done :
|
|
# - with $dn and $args->{password} as dn/password if defined,
|
|
# - or with Lemonldap::NG account,
|
|
# - or with an anonymous bind.
|
|
# @param $dn LDAP distinguish name
|
|
# @param %args See Net::LDAP(3) manpage for more
|
|
# @return Net::LDAP::Message
|
|
sub bind {
|
|
my $self = shift;
|
|
my $mesg;
|
|
my ( $dn, %args ) = @_;
|
|
unless ($dn) {
|
|
$dn = $self->{portal}->{managerDn};
|
|
$args{password} = $self->{portal}->{managerPassword};
|
|
}
|
|
if ( $dn && $args{password} ) {
|
|
$mesg = $self->SUPER::bind( $dn, %args );
|
|
}
|
|
else {
|
|
$mesg = $self->SUPER::bind();
|
|
}
|
|
return $mesg;
|
|
}
|
|
|
|
our $ppLoaded = 0;
|
|
|
|
## @method private boolean loadPP ()
|
|
# Load Net::LDAP::Control::PasswordPolicy
|
|
# @return true if succeed.
|
|
sub loadPP {
|
|
my $self = shift;
|
|
return 1 if ($ppLoaded);
|
|
|
|
# require Perl module
|
|
eval {require Net::LDAP::Control::PasswordPolicy};
|
|
if ($@) {
|
|
$self->lmLog(
|
|
"Module Net::LDAP::Control::PasswordPolicy not found in @INC",
|
|
'error' );
|
|
return 0;
|
|
}
|
|
$ppLoaded = 1;
|
|
}
|
|
|
|
## @method protected int userBind(string dn, %args)
|
|
# Call bind() with and return
|
|
# @param $dn LDAP distinguish name
|
|
# @param %args See Net::LDAP(3) manpage for more
|
|
# @return Lemonldap::NG portal error code
|
|
sub userBind {
|
|
my $self = shift;
|
|
if ( $self->{portal}->{ldapPpolicyControl} ) {
|
|
|
|
# Create Control object
|
|
my $pp = Net::LDAP::Control::PasswordPolicy->new();
|
|
|
|
# Bind with user credentials
|
|
my $mesg = $self->bind(
|
|
@_,
|
|
control => [$pp]
|
|
);
|
|
|
|
# Get server control response
|
|
my ($resp) = $mesg->control("1.3.6.1.4.1.42.2.27.8.5.1");
|
|
return ( $mesg->code == 0 ? PE_OK : PE_LDAPERROR )
|
|
unless ( defined $resp );
|
|
|
|
# Get expiration warning and graces
|
|
#
|
|
$self->{portal}->{mustRedirect} = 0 if($self->{portal}->{ppolicy}->{time_before_expiration} = $resp->time_before_expiration or $self->{portal}->{ppolicy}->{grace_authentications_remaining} = $resp->grace_authentications_remaining);
|
|
|
|
my $pp_error = $resp->pp_error;
|
|
if ( defined $pp_error ) {
|
|
#return [
|
|
# PE_PP_PASSWORD_EXPIRED,
|
|
# PE_PP_ACCOUNT_LOCKED,
|
|
# PE_PP_CHANGE_AFTER_RESET,
|
|
# PE_PP_PASSWORD_MOD_NOT_ALLOWED,
|
|
# PE_PP_MUST_SUPPLY_OLD_PASSWORD,
|
|
# PE_PP_INSUFFICIENT_PASSWORD_QUALITY,
|
|
# PE_PP_PASSWORD_TOO_SHORT,
|
|
# PE_PP_PASSWORD_TOO_YOUNG,
|
|
# PE_PP_PASSWORD_IN_HISTORY,
|
|
#]->[$pp_error];
|
|
return $pp_error + PE_PP_PASSWORD_EXPIRED;
|
|
}
|
|
elsif ( $mesg->code == 0 ) {
|
|
return PE_OK;
|
|
}
|
|
}
|
|
else {
|
|
my $mesg =
|
|
$self->bind( @_ );
|
|
if ( $mesg->code == 0 ) {
|
|
return PE_OK;
|
|
}
|
|
}
|
|
$self->{portal}->_sub( 'userError', "Bad password for $self->{user}" );
|
|
return PE_BADCREDENTIALS;
|
|
}
|
|
|
|
## @method protected Lemonldap::NG::Portal::_LDAP ldap()
|
|
# @return Lemonldap::NG::Portal::_LDAP object
|
|
sub ldap {
|
|
my $self = shift;
|
|
return $self->{ldap} if ( ref( $self->{ldap} ) );
|
|
if ( $self->{ldap} = Lemonldap::NG::Portal::_LDAP->new($self)
|
|
and my $mesg = $self->{ldap}->bind )
|
|
{
|
|
return $self->{ldap} if ( $mesg->code == 0 );
|
|
$self->lmLog( "LDAP error : " . $mesg->error, 'error' );
|
|
}
|
|
else {
|
|
$self->lmLog( "LDAP error : $@", 'error' );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
1;
|