From d7d9eb946f0e298221624851878d9dd3eccd6800 Mon Sep 17 00:00:00 2001 From: Frank Dillon Date: Mon, 20 Oct 2008 20:25:09 +0000 Subject: [PATCH] Modified LDAP connections to support ldaps and ldapi authentication. Additionally modified some of the labels and hoverhelp within the LDAPLink edit screen to better explain and make them less confusing. --- lib/WebGUI/Auth/LDAP.pm | 329 ++++++++++++++-------------- lib/WebGUI/LDAPLink.pm | 69 ++++-- lib/WebGUI/Operation/LDAPLink.pm | 1 - lib/WebGUI/i18n/English/AuthLDAP.pm | 19 +- 4 files changed, 229 insertions(+), 189 deletions(-) diff --git a/lib/WebGUI/Auth/LDAP.pm b/lib/WebGUI/Auth/LDAP.pm index 1142d9ad3..bbf23d7d2 100644 --- a/lib/WebGUI/Auth/LDAP.pm +++ b/lib/WebGUI/Auth/LDAP.pm @@ -40,83 +40,82 @@ i.e., it does not validate their username or ensure their account is active. =cut sub _isValidLDAPUser { - my $self = shift; - my ($uri, $error, $ldap, $search, $auth, $connectDN, $username, $password); - my $i18n = WebGUI::International->new($self->session); - my $connection = $self->getLDAPConnection; + my $self = shift; + my ($error, $ldap, $search, $auth, $connectDN); + my $i18n = WebGUI::International->new($self->session); - $username = $self->session->form->get("authLDAP_ldapId") || $self->session->form->get("username"); - $password = $self->session->form->get("authLDAP_identifier") || $self->session->form->get("identifier"); - - $uri = URI->new($connection->{ldapUrl}) or $error = '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; - - if($error ne ""){ - $self->error($error); - return 0; - } - - # Create an LDAP object - if ($ldap = Net::LDAP->new($uri->host, (port=>$uri->port))) { + my $connection = $self->getLDAPConnection; + + #Check to see that the LDAP Link is valid + my $ldapLink = $self->getLDAPLink; + unless ($ldapLink) { + $self->error('
  • '.$i18n->get(2,'AuthLDAP').'
  • '); + return 0; + } - # Bind as a proxy user to search for the user trying to login - if($connection->{connectDn}) { - $auth = $ldap->bind(dn=>$connection->{connectDn}, password=>$connection->{identifier}); - } - else { # No proxy user specified, try to bind anonymously for the search - $auth = $ldap->bind; - } - - # If we were able to bind - if ($auth) { - - # Search for the user trying to login - $search = $ldap->search(base=>$uri->dn, filter=>$connection->{ldapIdentity}.'='.$username); + my $username = $self->session->form->get("authLDAP_ldapId") || $self->session->form->get("username"); + my $password = $self->session->form->get("authLDAP_identifier") || $self->session->form->get("identifier"); + + # Create an LDAP object + if ($ldap = $ldapLink->connectToLDAP) { + my $uri = $ldapLink->getURI; + # Bind as a proxy user to search for the user trying to login + if($connection->{connectDn}) { + $auth = $ldap->bind(dn=>$connection->{connectDn}, password=>$connection->{identifier}); + } + else { # No proxy user specified, try to bind anonymously for the search + $auth = $ldap->bind; + } + + # If we were able to bind + if ($auth) { + # Search for the user trying to login + $search = $ldap->search(base=>$uri->dn, filter=>$connection->{ldapIdentity}.'='.$username); - # If we found a match - if (defined $search->entry(0)) { + # If we found a match + if (defined $search->entry(0)) { + # Determine the users distinguished name using dn + if ($connection->{ldapUserRDN} eq 'dn') { + $connectDN = $search->entry(0)->dn; + } + else { # or... use a releative distinguished name instead + $connectDN = $search->entry(0)->get_value($connection->{ldapUserRDN}); + } - # Determine the users distinguished name using dn - if ($connection->{ldapUserRDN} eq 'dn') { - $connectDN = $search->entry(0)->dn; - } - else { # or... use a releative distinguished name instead - $connectDN = $search->entry(0)->get_value($connection->{ldapUserRDN}); - } - - # Remember the users DN so we can use it later. - $self->setConnectDN($connectDN); - $ldap->unbind; + # Remember the users DN so we can use it later. + $self->setConnectDN($connectDN); + $ldap->unbind; - # Create a new LDAP object - $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)) or $error .= $i18n->get(2,'AuthLDAP'); + # Create a new LDAP object + $ldap = $ldapLink->connectToLDAP or $error .= $i18n->get(2,'AuthLDAP'); - # Try to bind to the directory using the users dn and password - $auth = $ldap->bind(dn=>$connectDN, password=>$password); + #Try to bind to the directory using the users dn and password + $auth = $ldap->bind(dn=>$connectDN, password=>$password); - # Invalid login credentials, directory did not authenticate the user - if ($auth->code == 48 || $auth->code == 49) { - $error .= '
  • '.$i18n->get(68).'
  • '; - $self->session->errorHandler->warn("Invalid LDAP information for registration of LDAP ID: ".$self->session->form->process('authLDAP_ldapId')); + # Invalid login credentials, directory did not authenticate the user + if ($auth->code == 48 || $auth->code == 49) { + $error .= '
  • '.$i18n->get(68).'
  • '; + $self->session->errorHandler->warn("Invalid LDAP information for registration of LDAP ID: ".$self->session->form->process('authLDAP_ldapId')); + } + elsif ($auth->code > 0) { # Some other LDAP error occured + $error .= '
  • LDAP error "'.$self->ldapStatusCode($auth->code).'" occured. '.$i18n->get(69).'
  • '; + $self->session->errorHandler->error("LDAP error: ".$self->ldapStatusCode($auth->code)); + } + $ldap->unbind; } - elsif ($auth->code > 0) { # Some other LDAP error occured - $error .= '
  • LDAP error "'.$self->ldapStatusCode($auth->code).'" occured. '.$i18n->get(69).'
  • '; - $self->session->errorHandler->error("LDAP error: ".$self->ldapStatusCode($auth->code)); + else { # Could not find the user in the directory to build a DN + $error .= '
  • '.$i18n->get(68).'
  • '; + $self->session->errorHandler->warn("Invalid LDAP information for registration of LDAP ID: ".$self->session->form->process("authLDAP_ldapId")); } - $ldap->unbind; - } - else { # Could not find the user in the directory to build a DN - $error .= '
  • '.$i18n->get(68).'
  • '; - $self->session->errorHandler->warn("Invalid LDAP information for registration of LDAP ID: ".$self->session->form->process("authLDAP_ldapId")); - } - } - else { # Unable to bind with proxy user credentials or anonymously for our search - $error = '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; - $self->session->errorHandler->error("Couldn't bind to LDAP server: ".$connection->{ldapUrl}); - } + } + else { # Unable to bind with proxy user credentials or anonymously for our search + $error = '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; + $self->session->errorHandler->error("Couldn't bind to LDAP server: ".$connection->{ldapUrl}); + } } else { # Could not create our LDAP object $error = '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; - $self->session->errorHandler->error("Couldn't create LDAP object: ".$uri->host); + $self->session->errorHandler->error("Couldn't create LDAP object: ".$connection->{ldapUrl}); } $self->error($error); @@ -141,78 +140,79 @@ Returns 1 on success. =cut sub authenticate { - my $self = shift; - my ($uri, $ldap, $auth, $result, $error); - my $i18n = WebGUI::International->new($self->session); - return 0 if !$self->SUPER::authenticate($_[0]); #see that the username entered actually exists and is active in webgui + my $self = shift; + my ($uri, $ldap, $auth, $result, $error); + my $i18n = WebGUI::International->new($self->session); + return 0 if !$self->SUPER::authenticate($_[0]); #see that the username entered actually exists and is active in webgui - my $userId = $self->userId; - my $identifier = $_[1]; - my $userData = $self->getParams; + my $userId = $self->userId; + my $identifier = $_[1]; + my $userData = $self->getParams; - $error .= '
  • '.$i18n->get(12,'AuthLDAP').'
  • ' if ($userData->{ldapUrl} eq ""); - $error .= '
  • '.$i18n->get(11,'AuthLDAP').'
  • ' if ($userData->{connectDN} eq ""); - $self->error($error); + $error .= '
  • '.$i18n->get(12,'AuthLDAP').'
  • ' if ($userData->{ldapUrl} eq ""); + $error .= '
  • '.$i18n->get(11,'AuthLDAP').'
  • ' if ($userData->{connectDN} eq ""); + $self->error($error); - if($error ne ""){ - $self->user(WebGUI::User->new($self->session,1)); - return 0 ; - } + if($error ne ""){ + $self->user(WebGUI::User->new($self->session,1)); + return 0 ; + } - if($uri = URI->new($userData->{ldapUrl})) { + if($uri = URI->new($userData->{ldapUrl})) { - # Create an LDAP object - $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)) or $error .= '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; + # Create an LDAP object + $ldap = Net::LDAP->new($uri->host, (port=>$uri->port, scheme=>$uri->scheme)) or $error .= '
  • '.$i18n->get(2,'AuthLDAP').'
  • '; - if($error ne ""){ - $self->user(WebGUI::User->new($self->session,1)); - return 0 ; - } + if($error ne ""){ + $self->user(WebGUI::User->new($self->session,1)); + return 0 ; + } - # Try to bind using the users dn and password - $auth = $ldap->bind(dn=>$userData->{connectDN}, password=>$identifier); - - # Authentication failed - if ($auth->code == 48 || $auth->code == 49){ - $error .= '
  • '.$i18n->get(68).'
  • '; - } - elsif ($auth->code > 0) { # Some other LDAP error happened - $error .= '
  • LDAP error "'.$self->ldapStatusCode($auth->code).'" occured.'.$i18n->get(69).'
  • '; - $self->session->errorHandler->error("LDAP error: ".$self->ldapStatusCode($auth->code)); - } + # Try to bind using the users dn and password + $auth = $ldap->bind(dn=>$userData->{connectDN}, password=>$identifier); + + # Authentication failed + if ($auth->code == 48 || $auth->code == 49){ + $error .= '
  • '.$i18n->get(68).'
  • '; + } + elsif ($auth->code > 0) { # Some other LDAP error happened + $error .= '
  • LDAP error "'.$self->ldapStatusCode($auth->code).'" occured.'.$i18n->get(69).'
  • '; + $self->session->errorHandler->error("LDAP error: ".$self->ldapStatusCode($auth->code)); + } - $ldap->unbind; - } - else { - $error .= '
  • '.$i18n->get(13,'AuthLDAP').'
  • '; - $self->session->errorHandler->error("Could not process this LDAP URL: ".$userData->{ldapUrl}); - } + $ldap->unbind; + } + else { + $error .= '
  • '.$i18n->get(13,'AuthLDAP').'
  • '; + $self->session->errorHandler->error("Could not process this LDAP URL: ".$userData->{ldapUrl}); + } - if($error ne ""){ - $self->error($error); - $self->user(WebGUI::User->new($self->session,1)); - } + if($error ne ""){ + $self->error($error); + $self->user(WebGUI::User->new($self->session,1)); + } - return $error eq ""; + return $error eq ""; } #------------------------------------------------------------------- sub connectToLDAP { - # This method needs to do some excpetion handling when we try to create an LDAPLink object - # Lot's to do though because then everything calling connectToLDAP must also handle exceptions on up - # - # Problem is that $connectionId may not have a value or the object creation may fail for other reasons. - # Quick fix for now is to ensure the ldapConnection setting is set in the settings table with the id of - # the default ldap connection. + # This method needs to do some excpetion handling when we try to create an LDAPLink object + # Lot's to do though because then everything calling connectToLDAP must also handle exceptions on up + # + # Problem is that $connectionId may not have a value or the object creation may fail for other reasons. + # Quick fix for now is to ensure the ldapConnection setting is set in the settings table with the id of + # the default ldap connection. - my $self = shift; - my $connectionId = $self->session->form->process("connection") || $self->session->setting->get("ldapConnection"); - my $ldapLink = WebGUI::LDAPLink->new($self->session,$connectionId); - my $connection = $ldapLink->get; + my $self = shift; + my $connectionId = $self->session->form->process("connection") || $self->session->setting->get("ldapConnection"); + my $ldapLink = WebGUI::LDAPLink->new($self->session,$connectionId); + my $connection = $ldapLink->get; - $self->{_connection} = $connection; - return $connection; + $self->{'_ldapLink' } = $ldapLink; + $self->{'_connection'} = $connection; + return $connection; } #------------------------------------------------------------------- @@ -274,46 +274,50 @@ sub createAccountSave { return $self->createAccount("

    ".$i18n->get(70)."

    ".$self->error); } - my $connection = $self->getLDAPConnection; - #Get connectDN from settings - my $uri = URI->new($connection->{ldapUrl}); - my $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)); - my $auth; - if($connection->{connectDn}) { - $auth = $ldap->bind(dn=>$connection->{connectDn}, password=>$connection->{identifier}); - }else{ - $auth = $ldap->bind; - } - #$ldap->bind; - my $search = $ldap->search (base => $uri->dn, filter=>$connection->{ldapIdentity}."=".$username); - my $connectDN = ""; - if (defined $search->entry(0)) { - if ($connection->{ldapUserRDN} eq 'dn') { - $connectDN = $search->entry(0)->dn; - } else { - $connectDN = $search->entry(0)->get_value($connection->{ldapUserRDN}); - } - } - $ldap->unbind; - - - #Check that username is valid and not a duplicate in the system. - $error .= $self->error if(!$self->validUsername($username)); - #Validate profile data. - my ($profile, $temp, $warning) = WebGUI::Operation::Profile::validateProfileData($self->session); - $error .= $temp; - return $self->createAccount("
  • ".$error."") unless ($error eq ""); - #If Email address is not unique, a warning is displayed - if($warning ne "" && !$self->session->form->process("confirm")){ - return $self->createAccount('
  • '.$i18n->get(1078).'
  • ', 1); - } - - my $properties; - $properties->{connectDN} = $connectDN; - $properties->{ldapUrl} = $connection->{ldapUrl}; - $properties->{ldapConnection} = $connection->{ldapLinkId}; + my $connection = $self->getLDAPConnection; + my $ldapLink = $self->getLDAPLink; - return $self->SUPER::createAccountSave($username,$properties,$password,$profile); + #Get connectDN from settings + my $ldap = $ldapLink->connectToLDAP; + my $uri = $ldapLink->getURI; + my $auth; + if($connection->{connectDn}) { + $auth = $ldap->bind(dn=>$connection->{connectDn}, password=>$connection->{identifier}); + } + else{ + $auth = $ldap->bind; + } + #$ldap->bind; + my $search = $ldap->search (base => $uri->dn, filter=>$connection->{ldapIdentity}."=".$username); + my $connectDN = ""; + if (defined $search->entry(0)) { + if ($connection->{ldapUserRDN} eq 'dn') { + $connectDN = $search->entry(0)->dn; + } + else { + $connectDN = $search->entry(0)->get_value($connection->{ldapUserRDN}); + } + } + $ldap->unbind; + + + #Check that username is valid and not a duplicate in the system. + $error .= $self->error if(!$self->validUsername($username)); + #Validate profile data. + my ($profile, $temp, $warning) = WebGUI::Operation::Profile::validateProfileData($self->session); + $error .= $temp; + return $self->createAccount("
  • ".$error."") unless ($error eq ""); + #If Email address is not unique, a warning is displayed + if($warning ne "" && !$self->session->form->process("confirm")){ + return $self->createAccount('
  • '.$i18n->get(1078).'
  • ', 1); + } + + my $properties; + $properties->{connectDN} = $connectDN; + $properties->{ldapUrl} = $connection->{ldapUrl}; + $properties->{ldapConnection} = $connection->{ldapLinkId}; + + return $self->SUPER::createAccountSave($username,$properties,$password,$profile); } #------------------------------------------------------------------- @@ -489,6 +493,13 @@ sub getLDAPConnection { return $self->connectToLDAP; } +#------------------------------------------------------------------- +sub getLDAPLink { + my $self = shift; + + return $self->{_ldapLink}; +} + #------------------------------------------------------------------- sub getLoginTemplateId { my $self = shift; diff --git a/lib/WebGUI/LDAPLink.pm b/lib/WebGUI/LDAPLink.pm index 92607b42a..576c3a1d6 100644 --- a/lib/WebGUI/LDAPLink.pm +++ b/lib/WebGUI/LDAPLink.pm @@ -56,14 +56,14 @@ cannot be established sub bind { my $self = shift; - my ($uri, $ldap, $auth, $result, $error); + my ($uri, $auth, $result, $error); if (defined $self->{_connection}) { return $self->{_connection}; } - my $ldapUrl = $self->{_ldapLink}->{ldapUrl}; - my $connectDn = $self->{_ldapLink}->{connectDn}; + my $ldapUrl = $self->{_ldapLink}->{ldapUrl}; + my $connectDn = $self->{_ldapLink}->{connectDn}; my $identifier = $self->{_ldapLink}->{identifier}; if ($ldapUrl eq "") { @@ -71,25 +71,52 @@ sub bind { return 0; } - if ($uri = URI->new($ldapUrl)) { - unless ($ldap = Net::LDAP->new($uri->host, (port=>($uri->port || 389)))) { - $self->{_error} = 103; - return 0; - } + my $ldap = $self->connectToLDAP($ldapUrl); + + return 0 unless ($ldap); - $auth = $ldap->bind(dn=>$connectDn, password=>$identifier); - if ($auth->code == 48 || $auth->code == 49) { - $self->{_error} = 104; - } elsif($auth->code > 0) { - $self->{_error} = $auth->code; - } - $self->{_uri } = $uri; - $self->{_connection} = $ldap; - } else { - $self->{_error} = 105; - return 0; - } - return $self->{_connection}; + $auth = $ldap->bind(dn=>$connectDn, password=>$identifier); + if ($auth->code == 48 || $auth->code == 49) { + $self->{_error} = 104; + } elsif($auth->code > 0) { + $self->{_error} = $auth->code; + } + + return $ldap; +} + +#------------------------------------------------------------------- + +=head2 connectToLDAP ( ) + +Attempts to bind to an LDAP server returning the Net::LDAP object if successful + +=cut + +sub connectToLDAP { + my $self = shift; + my $ldapUrl = shift || $self->getValue("ldapUrl"); + my $uri = URI->new($ldapUrl); + + unless ($uri) { + $self->{_error} = 105; + return undef; + } + + $self->{_uri} = $uri; + my $ldap = Net::LDAP->new($uri->host, + port => $uri->port, #Port will default to 389 or 636 + scheme => $uri->scheme + ); + + unless($ldap) { + $self->{_error} = 103; + return undef; + } + + $self->{_connection} = $ldap; + + return $ldap; } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Operation/LDAPLink.pm b/lib/WebGUI/Operation/LDAPLink.pm index 3c9f09948..28d1edcf3 100644 --- a/lib/WebGUI/Operation/LDAPLink.pm +++ b/lib/WebGUI/Operation/LDAPLink.pm @@ -159,7 +159,6 @@ sub www_deleteLDAPLink { my $session = shift; return $session->privilege->insufficient unless canView($session); $session->db->write("delete from ldapLink where ldapLinkId=".$session->db->quote($session->form->process("llid"))); - $session->form->process("op") = "listLDAPLinks"; return www_listLDAPLinks($session); } diff --git a/lib/WebGUI/i18n/English/AuthLDAP.pm b/lib/WebGUI/i18n/English/AuthLDAP.pm index c5770b84b..6bc06cde4 100644 --- a/lib/WebGUI/i18n/English/AuthLDAP.pm +++ b/lib/WebGUI/i18n/English/AuthLDAP.pm @@ -140,7 +140,7 @@ our $I18N = { }, '9' => { - message => q|User RDN|, + message => q|Authentication Attribute|, lastUpdated => 1053777552 }, @@ -546,28 +546,31 @@ our $I18N = { }, 'LDAPLink_993 description' => { - message => q|The URL used to connect to the LDAP server.|, + message => q|

    The URL used to connect to the LDAP server. LDAP url should look like:

    +

    ldap://ldap.mycompany.com/baseDN

    baseDN is the node on your LDAP server that WebGUI should use to initialize user searches. Typically this looks like dc=mycompany,dc=com.

    +

    ldap://ldap.mycompany.com/dc=mycompany,dc=com

    |, lastUpdated => 1120164594, }, 'LDAPLink_994 description' => { message => q|

    DN = Distinguished Name. A DN is a unique path to a particular object within an LDAP directory. In this case, the "Connect DN" is the DN that points to the user account -record. Usually that will look something like:

    +record for authenticating against this LDAP server at a permission level that has full read and write access to all of the users and groups on your LDAP server. +Usually that will look something like:

    cn=Joe Shmoe,ou=people,dc=example,dc=com

    |, lastUpdated => 1146630168, }, 'LDAPLink_995 description' => { - message => q|The password for the LDAP connection|, + message => q|The password for the account entered in the "Connect DN" field|, lastUpdated => 1120164594, }, '9 description' => { - message => q|

    RDN is a relative distinguished name. It means that we're looking at only part of the -path. In this case, the "User RDN" is the path to where user records can be found. -Usually the RDN looks something like:

    -

    ou=people,dc=example,dc=com

    |, + message => q|

    Enter the attribute that should be used for each record in LDAP to uniquely identify a user. +This field is used for auto creating user accounts for users already in your LDAP repository when they attempt to log in and +for finding users who sign up for the site via the anonymous registration feature if it is enabled. In almost all cases this attrubute is 'dn' +and should be entered as such.

    |, lastUpdated => 1146630220, },