package WebGUI::Auth::LDAP; #------------------------------------------------------------------- # WebGUI is Copyright 2001-2005 Plain Black Corporation. #------------------------------------------------------------------- # Please read the legal notices (docs/legal.txt) and the license # (docs/license.txt) that came with this distribution before using # this software. #------------------------------------------------------------------- # http://www.plainblack.com info@plainblack.com #------------------------------------------------------------------- use strict; use WebGUI::Auth; use WebGUI::DateTime; use WebGUI::HTMLForm; use WebGUI::Form; use WebGUI::LDAPLink; use WebGUI::Mail; use WebGUI::Session; use WebGUI::Utility; use WebGUI::Operation::Shared; use WebGUI::Asset::Template; use URI; use Net::LDAP; our @ISA = qw(WebGUI::Auth); my %ldapStatusCode = ( 0=>'success (0)', 1=>'Operations Error (1)', 2=>'Protocol Error (2)', 3=>'Time Limit Exceeded (3)', 4=>'Size Limit Exceeded (4)', 5=>'Compare False (5)', 6=>'Compare True (6)', 7=>'Auth Method Not Supported (7)', 8=>'Strong Auth Required (8)', 9=>'Referral (10)', 11=>'Admin Limit Exceeded (11)', 12=>'Unavailable Critical Extension (12)', 13=>'Confidentiality Required (13)', 14=>'Sasl Bind In Progress (14)', 15=>'No Such Attribute (16)', 17=>'Undefined Attribute Type (17)', 18=>'Inappropriate Matching (18)', 19=>'Constraint Violation (19)', 20=>'Attribute Or Value Exists (20)', 21=>'Invalid Attribute Syntax (21)', 32=>'No Such Object (32)', 33=>'Alias Problem (33)', 34=>'Invalid DN Syntax (34)', 36=>'Alias Dereferencing Problem (36)', 48=>'Inappropriate Authentication (48)', 49=>'Invalid Credentials (49)', 50=>'Insufficient Access Rights (50)', 51=>'Busy (51)', 52=>'Unavailable (52)', 53=>'Unwilling To Perform (53)', 54=>'Loop Detect (54)', 64=>'Naming Violation (64)', 65=>'Object Class Violation (65)', 66=>'Not Allowed On Non Leaf (66)', 67=>'Not Allowed On RDN (67)', 68=>'Entry Already Exists (68)', 69=>'Object Class Mods Prohibited (69)', 71=>'Affects Multiple DSAs (71)', 80=>'other (80)'); #------------------------------------------------------------------- sub _isValidLDAPUser { my $self = shift; my ($uri, $error, $ldap, $search, $auth, $connectDN); $uri = URI->new($session{setting}{ldapURL}) or $error = WebGUI::International::get(2,'AuthLDAP'); if($error ne ""){ $self->error($error); return 0; } if ($ldap = Net::LDAP->new($uri->host, (port=>$uri->port))) { if ($ldap->bind) { $search = $ldap->search (base=>$uri->dn,filter=>$session{setting}{ldapId}."=".$session{form}{'authLDAP.ldapId'}); if (defined $search->entry(0)) { if ($session{setting}{ldapUserRDN} eq 'dn') { $connectDN = $search->entry(0)->dn; } else { $connectDN = $search->entry(0)->get_value($session{setting}{ldapUserRDN}); } $ldap->unbind; $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)) or $error .= WebGUI::International::get(2,'AuthLDAP'); $auth = $ldap->bind(dn=>$connectDN, password=>$session{form}{'authLDAP.identifier'}); if ($auth->code == 48 || $auth->code == 49) { $error .= '
  • '.WebGUI::International::get(68); WebGUI::ErrorHandler::warn("Invalid LDAP information for registration of LDAP ID: ".$session{form}{'authLDAP.ldapId'}); } elsif ($auth->code > 0) { $error .= '
  • LDAP error "'.$ldapStatusCode{$auth->code}.'" occured. '.WebGUI::International::get(69); WebGUI::ErrorHandler::error("LDAP error: ".$ldapStatusCode{$auth->code}); } $ldap->unbind; } else { $error .= '
  • '.WebGUI::International::get(68); WebGUI::ErrorHandler::warn("Invalid LDAP information for registration of LDAP ID: ".$session{form}{'authLDAP.ldapId'}); } } else { $error = WebGUI::International::get(2,'AuthLDAP'); WebGUI::ErrorHandler::error("Couldn't bind to LDAP server: ".$session{setting}{ldapURL}); } } else { $error = WebGUI::International::get(2,'AuthLDAP'); WebGUI::ErrorHandler::error("Couldn't create LDAP object: ".$uri->host); } $self->error($error); return $error eq ""; } #------------------------------------------------------------------- =head2 addUserForm ( ) Creates user form elements specific to this Auth Method. =cut sub addUserForm { my $self = shift; my $userData = $self->getParams; my $ldapUrl = $session{form}{'authLDAP_ldapUrl'} || $userData->{ldapUrl} || $session{setting}{ldapURL}; my $connectDN = $session{form}{'authLDAP_connectDN'} || $userData->{connectDN}; my $ldapConnection = $session{form}{'authLDAP_ldapConnection'} || $userData->{ldapConnection}; my $ldapLinks = WebGUI::SQL->buildHashRef("select ldapLinkId,ldapUrl from ldapLink"); my $f = WebGUI::HTMLForm->new; my $jscript = ""; if(scalar(keys %{$ldapLinks}) > 0) { my $jsArray = ""; foreach my $key (keys %{$ldapLinks}) { next unless ($key); $jsArray .= 'ldapValue["'.$key.'"]="'.$ldapLinks->{$key}.'";'."\n"; } $jsArray .= 'ldapValue["0"]="'.$ldapUrl.'";'."\n"; $jscript = qq| |; $f->selectList( -name=>"authLDAP_ldapConnection", -label=>WebGUI::International::get("ldapConnection",'AuthLDAP'), -options=>WebGUI::LDAPLink::getList(), -value=>[$ldapConnection], -extras=>q|onchange="this.form.authLDAP_ldapUrl.value=ldapValue[this.options[this.selectedIndex].value];"| ); } $f->url("authLDAP_ldapUrl",WebGUI::International::get(3,'AuthLDAP'),$ldapUrl); $f->text("authLDAP_connectDN",WebGUI::International::get(4,'AuthLDAP'),$connectDN); return $jscript.$f->printRowsOnly; } #------------------------------------------------------------------- =head2 addUserFormSave ( ) Saves user elements unique to this authentication method =cut sub addUserFormSave { my $self = shift; my $properties; $properties->{connectDN} = $session{form}{'authLDAP_connectDN'}; $properties->{ldapUrl} = $session{form}{'authLDAP_ldapUrl'}; $properties->{ldapConnection} = $session{form}{'authLDAP_ldapConnection'}; $self->SUPER::addUserFormSave($properties); } #------------------------------------------------------------------- sub authenticate { my $self = shift; my ($uri, $ldap, $auth, $result, $error); return 0 if !$self->SUPER::authenticate($_[0]); #authenticate that the username entered actually exists and is active my $userId = $self->userId; my $identifier = $_[1]; my $userData = $self->getParams; $error .= WebGUI::International::get(12,'AuthLDAP') if ($userData->{ldapUrl} eq ""); $error .= WebGUI::International::get(11,'AuthLDAP') if ($userData->{connectDN} eq ""); $self->error($error); if($error ne ""){ $self->user(WebGUI::User->new(1)); return 0 ; } if($uri = URI->new($userData->{ldapUrl})) { $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)) or $error .= WebGUI::International::get(2,'AuthLDAP'); if($error ne ""){ $self->user(WebGUI::User->new(1)); return 0 ; } $auth = $ldap->bind(dn=>$userData->{connectDN}, password=>$identifier); if ($auth->code == 48 || $auth->code == 49){ $error .= WebGUI::International::get(68); }elsif($auth->code > 0){ $error .= 'LDAP error "'.$ldapStatusCode{$auth->code}.'" occured.'.WebGUI::International::get(69); WebGUI::ErrorHandler::error("LDAP error: ".$ldapStatusCode{$auth->code}); } $ldap->unbind; }else{ $error .= WebGUI::International::get(13,'AuthLDAP'); WebGUI::ErrorHandler::error("Could not process this LDAP URL: ".$userData->{ldapUrl}); } if($error ne ""){ $self->error($error); $self->user(WebGUI::User->new(1)); } return $error eq ""; } #------------------------------------------------------------------- sub createAccount { my $self = shift; my $vars; if ($session{user}{userId} ne "1") { return $self->displayAccount; } elsif (!$session{setting}{anonymousRegistration}) { return $self->displayLogin; } $vars->{'create.message'} = $_[0] if ($_[0]); $vars->{'create.form.ldapId'} = WebGUI::Form::text({"name"=>"authLDAP.ldapId","value"=>$session{form}{"authLDAP.ldapId"}}); $vars->{'create.form.ldapId.label'} = $session{setting}{ldapIdName}; $vars->{'create.form.password'} = WebGUI::Form::password({"name"=>"authLDAP.identifier","value"=>$session{form}{"authLDAP.identifier"}}); $vars->{'create.form.password.label'} = $session{setting}{ldapPasswordName}; $vars->{'create.form.hidden'} = WebGUI::Form::hidden({"name"=>"confirm","value"=>$session{form}{confirm}}); return $self->SUPER::createAccount("createAccountSave",$vars); } #------------------------------------------------------------------- sub createAccountSave { my $self = shift; my $username = $session{form}{'authLDAP.ldapId'}; my $password = $session{form}{'authLDAP.identifier'}; my $error = ""; #Validate user in LDAP if(!$self->_isValidLDAPUser()){ return $self->createAccount("

    ".WebGUI::International::get(70)."

    ".$self->error); } #Get connectDN from settings my $uri = URI->new($session{setting}{ldapURL}); my $ldap = Net::LDAP->new($uri->host, (port=>$uri->port)); $ldap->bind; my $search = $ldap->search (base => $uri->dn, filter=>$session{setting}{ldapId}."=".$username); my $connectDN = ""; if (defined $search->entry(0)) { if ($session{setting}{ldapUserRDN} eq 'dn') { $connectDN = $search->entry(0)->dn; } else { $connectDN = $search->entry(0)->get_value($session{setting}{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(); $error .= $temp; return $self->createAccount("

    ".WebGUI::International::get(70)."

    ".$error) unless ($error eq ""); #If Email address is not unique, a warning is displayed if($warning ne "" && !$session{form}{confirm}){ $session{form}{confirm} = 1; return $self->createAccount('
  • '.WebGUI::International::get(1078)); } my $properties; $properties->{connectDN} = $connectDN; $properties->{ldapUrl} = $session{setting}{ldapURL}; return $self->SUPER::createAccountSave($username,$properties,$password,$profile); } #------------------------------------------------------------------- sub deactivateAccount { my $self = shift; return $self->displayLogin if($self->userId eq '1'); return $self->SUPER::deactivateAccount("deactivateAccountConfirm"); } #------------------------------------------------------------------- sub deactivateAccountConfirm { my $self = shift; return $self->displayLogin unless ($session{setting}{selfDeactivation}); return $self->SUPER::deactivateAccountConfirm; } #------------------------------------------------------------------- sub displayAccount { my $self = shift; my $vars; return $self->displayLogin($_[0]) if ($self->userId eq '1'); $vars->{displayTitle} = '

    '.WebGUI::International::get(61).'

    '; $vars->{'account.message'} = WebGUI::International::get(856); if($session{setting}{useKarma}){ $vars->{'account.form.karma'} = $session{user}{karma}; $vars->{'account.form.karma.label'} = WebGUI::International::get(537); } $vars->{'account.options'} = WebGUI::Operation::Shared::accountOptions(); return WebGUI::Asset::Template->new($self->getAccountTemplateId)->process($vars); } #------------------------------------------------------------------- sub displayLogin { my $self = shift; my $vars; return $self->displayAccount($_[0]) if ($self->userId ne "1"); $vars->{'login.message'} = $_[0] if ($_[0]); return $self->SUPER::displayLogin("login",$vars); } #------------------------------------------------------------------- =head2 editUserForm ( ) Creates user form elements specific to this Auth Method. =cut sub editUserForm { my $self = shift; return $self->addUserForm; } #------------------------------------------------------------------- =head2 editUserFormSave ( ) Saves user elements unique to this authentication method =cut sub editUserFormSave { my $self = shift; return $self->addUserFormSave; } #------------------------------------------------------------------- =head2 editUserSettingsForm ( ) Creates form elements for user settings page custom to this auth module =cut sub editUserSettingsForm { my $self = shift; my $sth = WebGUI::SQL->read("select * from ldapLink"); my $f = WebGUI::HTMLForm->new; my $jscript = ""; if($sth->rows > 0 ){ my $jsArray = ""; $jsArray = qq|ldapValue["0"] = ["$session{setting}{ldapUserRDN}","$session{setting}{ldapURL}","$session{setting}{ldapId}","$session{setting}{ldapIdName}","$session{setting}{ldapPasswordName}","$session{setting}{ldapSendWelcomeMessage}","$session{setting}{ldapWelcomeMessage}","$session{setting}{ldapAccountTemplate}","$session{setting}{ldapCreateAccountTemplate}","$session{setting}{ldapLoginTemplate}"];|."\n"; while (my $lhash = $sth->hashRef) { $jsArray .= qq|ldapValue["$lhash->{ldapLinkId}"] = ["$lhash->{ldapUserRDN}","$lhash->{ldapUrl}","$lhash->{ldapIdentity}","$lhash->{ldapIdentityName}","$lhash->{ldapPasswordName}","$lhash->{ldapSendWelcomeMessage}","$lhash->{ldapWelcomeMessage}","$lhash->{ldapAccountTemplate}","$lhash->{ldapCreateAccountTemplate}","$lhash->{ldapLoginTemplate}"];|."\n"; } $jscript = qq| |; $f->selectList( -name=>"ldapConnection", -label=>WebGUI::International::get("ldapConnection",'AuthLDAP'), -options=>WebGUI::LDAPLink::getList(), -value=>[$session{setting}{ldapConnection}], -extras=>q|onchange="changeFormValues(this.form,this.options[this.selectedIndex].value);"| ); } $f->text("ldapUserRDN",WebGUI::International::get(9,'AuthLDAP'),$session{setting}{ldapUserRDN}); $f->url("ldapURL",WebGUI::International::get(5,'AuthLDAP'),$session{setting}{ldapURL}); $f->text("ldapId",WebGUI::International::get(6,'AuthLDAP'),$session{setting}{ldapId}); $f->text("ldapIdName",WebGUI::International::get(7,'AuthLDAP'),$session{setting}{ldapIdName}); $f->text("ldapPasswordName",WebGUI::International::get(8,'AuthLDAP'),$session{setting}{ldapPasswordName}); $f->yesNo( -name=>"ldapSendWelcomeMessage", -value=>$session{setting}{ldapSendWelcomeMessage}, -label=>WebGUI::International::get(868) ); $f->textarea( -name=>"ldapWelcomeMessage", -value=>$session{setting}{ldapWelcomeMessage}, -label=>WebGUI::International::get(869) ); $f->template( -name=>"ldapAccountTemplate", -value=>$session{setting}{ldapAccountTemplate}, -namespace=>"Auth/LDAP/Account", -label=>WebGUI::International::get("account template","AuthLDAP") ); $f->template( -name=>"ldapCreateAccountTemplate", -value=>$session{setting}{ldapCreateAccountTemplate}, -namespace=>"Auth/LDAP/Create", -label=>WebGUI::International::get("create account template","AuthLDAP") ); $f->template( -name=>"ldapLoginTemplate", -value=>$session{setting}{ldapLoginTemplate}, -namespace=>"Auth/LDAP/Login", -label=>WebGUI::International::get("login template","AuthLDAP") ); return $jscript.$f->printRowsOnly; } #------------------------------------------------------------------- sub getAccountTemplateId { return $session{setting}{ldapAccountTemplate} || "PBtmpl0000000000000004"; } #------------------------------------------------------------------- sub getCreateAccountTemplateId { return $session{setting}{ldapCreateAccountTemplate} || "PBtmpl0000000000000005"; } #------------------------------------------------------------------- sub getLoginTemplateId { return $session{setting}{ldapLoginTemplate} || "PBtmpl0000000000000006"; } #------------------------------------------------------------------- sub login { my $self = shift; if(!$self->authenticate($session{form}{username},$session{form}{identifier})){ WebGUI::ErrorHandler::security("login to account ".$session{form}{username}." with invalid information."); return $self->displayLogin("

    ".WebGUI::International::get(70)."

    ".$self->error); } return $self->SUPER::login(); #Standard login routine for login } #------------------------------------------------------------------- sub new { my $class = shift; my $authMethod = $_[0]; my $userId = $_[1]; my @callable = ('createAccount','deactivateAccount','displayAccount','displayLogin','login','logout','createAccountSave','deactivateAccountConfirm'); my $self = WebGUI::Auth->new($authMethod,$userId,\@callable); bless $self, $class; } 1;