package WebGUI::LDAPLink; =head1 LEGAL ------------------------------------------------------------------- WebGUI is Copyright 2001-2009 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 ------------------------------------------------------------------- =cut use strict; use Tie::CPHash; use WebGUI::International; use Net::LDAP; =head1 NAME Package WebGUI::LDAPLink =head1 DESCRIPTION This package contains utility methods for WebGUI's ldap link system. =head1 SYNOPSIS use WebGUI::LDAPLink; $hashRef = WebGUI::LDAPLink->getList($self->session,); %ldapLink = WebGUI::LDAPLink->new($self->session,$ldapLinkId)->get; $ldapLink = WebGUI::LDAPLink->new($self->session,$ldapLinkId); $connection = $ldapLink->connectToLDAP(); $ldapLink->unbind; =head1 METHODS These subroutines are available from this package: =cut #------------------------------------------------------------------- =head2 bind ( ) Authenticates against the ldap server with the parameters stored in the class, returning a valid ldap connection, or 0 if a connection cannot be established =cut sub bind { my $self = shift; my ($uri, $auth, $result, $error); if (defined $self->{_connection}) { return $self->{_connection}; } my $ldapUrl = $self->{_ldapLink}->{ldapUrl}; my $connectDn = $self->{_ldapLink}->{connectDn}; my $identifier = $self->{_ldapLink}->{identifier}; if ($ldapUrl eq "") { $self->{_error} = 100; 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; } 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; } #------------------------------------------------------------------- sub DESTROY { my $self = shift; $self->unbind; undef $self; } #------------------------------------------------------------------- =head2 get ( ) Returns the list of LDAP connection properties. =cut sub get { my $self = shift; return $self->{_ldapLink}; } #------------------------------------------------------------------- =head2 getErrorCode ( ) Returns the numerical error code generated by the bind() method. =cut sub getErrorCode { my $self = shift; return $self->{_error}; } #------------------------------------------------------------------- =head2 getErrorMessage ( [ldapErrorCode] ) Returns the error string representing the error code generated by Net::LDAP. If no code is passed in, the most recent error stored by the class is returned =head3 ldapErrorCode A valid ldap error code. =cut sub getErrorMessage { my $self = shift; my $errorCode = shift || $self->getErrorCode; return "" unless $errorCode; my $i18nCode = "LDAPLink_".$errorCode; my $i18n = WebGUI::International->new($self->session,"AuthLDAP"); return $i18n->get($i18nCode); } #------------------------------------------------------------------- =head2 getList ( session ) Returns a hash reference containing all ldap links. The format is: ldapLinkId => ldapLinkName. This is a class method. =head3 session A reference to the current session. =cut sub getList { my $class = shift; my $session = shift; my %list; tie %list, "Tie::IxHash"; %list = $session->db->buildHash("select ldapLinkId, ldapLinkName from ldapLink order by ldapLinkName"); return \%list; } #------------------------------------------------------------------- =head2 getURI ( ) Returns the uri object for the ldap connection. =cut sub getURI { my $self = shift; return $self->{_uri}; } #------------------------------------------------------------------- =head2 getValue ( property ) Returns the value of the property passed in. =cut sub getValue { my $self = shift; my $prop = shift; return $self->get->{$prop}; } #------------------------------------------------------------------- =head2 unbind ( ) Disconnect cleanly from the current databaseLink. =cut sub unbind { my $self = shift; if (defined $self->{_connection}) { $self->{_connection}->unbind; } } #------------------------------------------------------------------- =head2 new ( session, ldapLinkId ) Constructor. =head3 session A reference to the current session. =head3 ldapLinkId The ldapLinkId of the ldapLink you're creating an object reference for. =cut sub new { my $class = shift; my $session = shift; my $ldapLinkId = shift; return undef unless $ldapLinkId; my $ldapLink = $session->db->quickHashRef("select * from ldapLink where ldapLinkId=?",[$ldapLinkId]); bless {_session=>$session, _ldapLinkId=>$ldapLinkId, _ldapLink=>$ldapLink }, $class; } #------------------------------------------------------------------- =head2 session ( ) Returns a reference to the current session. =cut sub session { my $self = shift; return $self->{_session}; } #------------------------------------------------------------------- =head2 getProperty(dn,property) Returns the results of a search on the property passed in =head3 distinguished name of property distinguished name of group to check users for =head3 property ldap property to retrieve from distinguished name =cut sub getProperty { my $self = shift; my $ldap = $self->bind; my $dn = shift; my $property = shift; return [] unless($ldap && $dn && $property); my $results = []; my $msg = $ldap->search( base => $dn, scope => 'sub', filter => "&(objectClass=*)" ); if (!$msg->code && $msg->count > 0) { my $entry = $msg->entry(($msg->count)-1); $results = $entry->get_value($property,asref => 1); } return $results; } #------------------------------------------------------------------- =head2 recurseProperty(base,array,property,alternateKey) Returns the whether or not the user is in a particular group =cut sub recurseProperty { my $self = shift; my $ldap = $self->bind; my $base = $_[0]; my $array = $_[1] || []; my $property = $_[2]; my $recProperty = $_[3] || $property; my $count = $_[4] || 0; my $recurseFilter = $_[5] || $self->get->{ldapGlobalRecursiveFilter}; my $rfAlreadyTransformed = $_[6]; return undef unless($ldap && $base && $property); if (length $recurseFilter and not $rfAlreadyTransformed) { $recurseFilter =~ tr/\r//d; $recurseFilter =~ s/\A\n*//; $recurseFilter =~ s/\n*\z//; $recurseFilter = (join '|', map{quotemeta} grep{/\S/} split /\n/, $recurseFilter); $recurseFilter = length($recurseFilter)? qr/(?i:$recurseFilter)/ : undef; } #Prevent infinite recursion $count++; return undef if $count == 99; #search the base my $msg = $ldap->search( base => $base, scope => 'sub', filter => "&(objectClass=*)" ); #return undef if nothing found return undef if($msg->code || $msg->count == 0); #loop through the results for (my $i = 0; $i < $msg->count; $i++) { my $entry = $msg->entry($i); #push all the values stored in the property on to the array stack my $properties = $entry->get_value($property,asref => 1); $properties = [] unless ref $properties eq "ARRAY"; push(@{$array},@{$properties}); #Loop through the recursive keys if ($property ne $recProperty) { $properties = $entry->get_value($recProperty,asref => 1); } foreach my $prop (@{$properties}) { next if $recurseFilter and $prop =~ m/$recurseFilter/; $self->recurseProperty($prop,$array,$property,$recProperty,$count,$recurseFilter,1); } } } 1;