LEMONLDAP::NG : New auth architecture in Portal
This commit is contained in:
parent
f393d06a3d
commit
aca6815c1b
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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;
|
|
@ -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__
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue