Externalauth changed to accommodate local fix.
authorMikal Kolbein Gule <m.k.gule@usit.uio.no>
Tue, 3 Jul 2012 09:04:25 +0000 (11:04 +0200)
committerMikal Kolbein Gule <m.k.gule@usit.uio.no>
Tue, 3 Jul 2012 09:04:25 +0000 (11:04 +0200)
local/plugins/RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth.pm
local/plugins/RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth/DBI.pm
local/plugins/RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth/LDAP.pm
local/plugins/RT-Authen-ExternalAuth/lib/perllocal.pod

index 5877af7..fa7609f 100644 (file)
@@ -4,67 +4,22 @@ our $VERSION = '0.09';
 
 =head1 NAME
 
-RT::Authen::ExternalAuth - RT Authentication using External Sources
+  RT::Authen::ExternalAuth - RT Authentication using External Sources
 
 =head1 DESCRIPTION
 
-A complete package for adding external authentication mechanisms
-to RT. It currently supports LDAP via Net::LDAP and External Database
-authentication for any database with an installed DBI driver.
+  A complete package for adding external authentication mechanisms
+  to RT. It currently supports LDAP via Net::LDAP and External Database
+  authentication for any database with an installed DBI driver.
 
-It also allows for authenticating cookie information against an
-external database through the use of the RT-Authen-CookieAuth extension.
+  It also allows for authenticating cookie information against an
+  external database through the use of the RT-Authen-CookieAuth extension.
 
-=head1 CONFIGURATION
+=begin testing
 
-=head2 Generic
+ok(require RT::Authen::ExternalAuth);
 
-=head3 attr_match_list
-
-The list of RT attributes that uniquely identify a user. It's
-recommended to use 'Name' and 'EmailAddress' to save
-encountering problems later. Example:
-
-    'attr_match_list' => [
-        'Name',
-        'EmailAddress', 
-        'RealName',
-        'WorkPhone', 
-    ],
-
-=head3 attr_map
-
-Mapping of RT attributes on to attributes in the external source.
-Example:
-
-    'attr_map' => {
-        'Name'         => 'sAMAccountName',
-        'EmailAddress' => 'mail',
-        'Organization' => 'physicalDeliveryOfficeName',
-        'RealName'     => 'cn',
-        ...
-    },
-
-Since version 0.10 it's possible to map one RT field to multiple
-external attributes, for example:
-
-    attr_map => {
-        EmailAddress => ['mail', 'alias'],
-        ...
-    },
-
-Note that only one value storred in RT. However, search goes by
-all external attributes if such RT field list in L</attr_match_list>.
-On create or update entered value is used as long as it's valid.
-If user didn't enter value then value stored in the first external
-attribute is used. Config example:
-
-    attr_match_list => ['Name', 'EmailAddress'],
-    attr_map => {
-        Name         => 'account',
-        EmailAddress => ['mail', 'alias'],
-        ...
-    },
+=end testing
 
 =cut    
 
@@ -324,8 +279,8 @@ sub UpdateUserInfo {
     # Update their info from external service using the username as the lookup key
     # CanonicalizeUserInfo will work out for itself which service to use
     # Passing it a service instead could break other RT code
-    my %args;
-    $UserObj->CanonicalizeUserInfo( \%args );
+    my %args = (Name => $username);
+    $UserObj->CanonicalizeUserInfo(\%args);
 
     # For each piece of information returned by CanonicalizeUserInfo,
     # run the Set method for that piece of info to change it for the user
@@ -482,19 +437,12 @@ sub CanonicalizeUserInfo {
     
     my $UserObj = shift;
     my $args    = shift;
-
-    WorkaroundAutoCreate( $UserObj, $args );
-
-    my $current_value = sub {
-        my $field = shift;
-        return $args->{ $field } if keys %$args;
-
-        return undef unless $UserObj->can( $field );
-        return $UserObj->$field();
-    };
-
-    my ($found, $config, %params) = (0);
-
+    
+    my $found   = 0;
+    my %params  = (Name         => undef,
+                  EmailAddress => undef,
+                  RealName     => undef);
+    
     $RT::Logger->debug( (caller(0))[3], 
                         "called by", 
                         caller, 
@@ -503,7 +451,7 @@ sub CanonicalizeUserInfo {
                             sort(keys(%$args))));
 
     # Get the list of defined external services
-    my @info_services = $RT::ExternalInfoPriority ? @{$RT::ExternalInfoPriority} : ();
+    my @info_services = $RT::ExternalInfoPriority ? @{$RT::ExternalInfoPriority} : undef;
     # For each external service...
     foreach my $service (@info_services) {
         
@@ -511,50 +459,54 @@ sub CanonicalizeUserInfo {
                             $service);
         
         # Get the config for the service so that we know what attrs we can canonicalize
-        $config = $RT::ExternalSettings->{$service};
-
+        my $config = $RT::ExternalSettings->{$service};
+        
         if($config->{'type'} eq 'cookie'){
             $RT::Logger->debug("You cannot use SSO cookies as an information service!");
             next;
         }  
         
-        # Get the list of unique attrs we need
-        my @service_attrs = do {
-            my %seen;
-            grep !$seen{$_}++, map ref($_)? @$_ : ($_), values %{ $config->{'attr_map'} }
-        };
-
         # For each attr we've been told to canonicalize in the match list
+        my $attr_map = (defined $config->{'lookup_attr_map'}) ? $config->{'lookup_attr_map'} : $config->{'attr_map'};
         foreach my $rt_attr (@{$config->{'attr_match_list'}}) {
             # Jump to the next attr in $args if this one isn't in the attr_match_list
             $RT::Logger->debug( "Attempting to use this canonicalization key:",$rt_attr);
-            my $value = $current_value->( $rt_attr );
-            unless( defined $value && length $value ) {
+            unless(defined($args->{$rt_attr})) {
                 $RT::Logger->debug("This attribute (",
                                     $rt_attr,
-                                    ") is null or incorrectly defined in the attr_match_list for this service (",
+                                    ") is null or incorrectly defined in the attr_map for this service (",
                                     $service,
                                     ")");
                 next;
             }
                                
             # Else, use it as a canonicalization key and lookup the user info    
-            my $key = $config->{'attr_map'}->{$rt_attr};
-            unless ( $key ) {
-                $RT::Logger->warning(
-                    "No mapping for $rt_attr in attr_map for this service ($service)"
-                );
+            my $key = $attr_map->{$rt_attr};
+            my $value = $args->{$rt_attr};
+            
+            # Check to see that the key being asked for is defined in the config's attr_map
+            my $valid = 0;
+            my ($attr_key, $attr_value);
+            while (($attr_key, $attr_value) = each %$attr_map) {
+                $valid = 1 if ($key eq $attr_value);
+            }
+            unless ($valid){
+                $RT::Logger->debug( "This key (",
+                                    $key,
+                                    "is not a valid attribute key (",
+                                    $service,
+                                    ")");
                 next;
             }
-
+            
             # Use an if/elsif structure to do a lookup with any custom code needed 
             # for any given type of external service, or die if no code exists for
             # the service requested.
             
             if($config->{'type'} eq 'ldap'){    
-                ($found, %params) = RT::Authen::ExternalAuth::LDAP::CanonicalizeUserInfo($service,$key,$value, \@service_attrs);
+                ($found, %params) = RT::Authen::ExternalAuth::LDAP::CanonicalizeUserInfo($service,$key,$value);
             } elsif ($config->{'type'} eq 'db') {
-                ($found, %params) = RT::Authen::ExternalAuth::DBI::CanonicalizeUserInfo($service,$key,$value, \@service_attrs);
+                ($found, %params) = RT::Authen::ExternalAuth::DBI::CanonicalizeUserInfo($service,$key,$value);
             } else {
                 $RT::Logger->debug( (caller(0))[3],
                                     "does not consider",
@@ -568,48 +520,29 @@ sub CanonicalizeUserInfo {
         # Don't Check any more services
         last if $found;
     }
-
-    unless ( $found ) {
-        ### HACK: The config var below is to overcome the (IMO) bug in
-        ### RT::User::Create() which expects this function to always
-        ### return true or rejects the user for creation. This should be
-        ### a different config var (CreateUncanonicalizedUsers) and 
-        ### should be honored in RT::User::Create()
-        return($RT::AutoCreateNonExternalUsers);
-    }
-
-    # If found let's build back RT's fields
-    my %res;
-    while ( my ($k, $v) = each %{ $config->{'attr_map'} } ) {
-        unless ( ref $v ) {
-            $res{ $k } = $params{ $v };
-            next;
-        }
-
-        my $current = $current_value->( $k );
-        unless ( defined $current ) {
-            $res{ $k } = (grep defined && length, map $params{ $_ }, @$v)[0];
-        } else {
-            unless ( grep defined && length && $_ eq $current, map $params{ $_ }, @$v ) {
-                $res{ $k } = (grep defined && length, map $params{ $_ }, @$v)[0];
-            }
-        }
-    }
-
-    # It's important that we always have a canonical email address
-    if ($res{'EmailAddress'}) {
-        $res{'EmailAddress'} = $UserObj->CanonicalizeEmailAddress($res{'EmailAddress'});
-    } 
-
+    
+    # If found, Canonicalize Email Address and 
     # update the args hash that we were given the hashref for
-    %$args = (%$args, %res);
+    if ($found) {
+        # It's important that we always have a canonical email address
+        if ($params{'EmailAddress'}) {
+            $params{'EmailAddress'} = $UserObj->CanonicalizeEmailAddress($params{'EmailAddress'});
+        } 
+        %$args = (%$args, %params);
+    }
 
     $RT::Logger->info(  (caller(0))[3], 
                         "returning", 
                         join(", ", map {sprintf("%s: %s", $_, $args->{$_})} 
                             sort(keys(%$args))));
 
-    return $found;
+    ### HACK: The config var below is to overcome the (IMO) bug in
+    ### RT::User::Create() which expects this function to always
+    ### return true or rejects the user for creation. This should be
+    ### a different config var (CreateUncanonicalizedUsers) and 
+    ### should be honored in RT::User::Create()
+    return($found || $RT::AutoCreateNonExternalUsers);
+   
 }
 
 {
@@ -621,191 +554,4 @@ sub CanonicalizeUserInfo {
     };
 }
 
-{
-    no warnings 'redefine';
-    my $orig = RT::User->can('LoadByCols');
-    *RT::User::LoadByCols = sub {
-        my $self = shift;
-        my %args = @_;
-
-        my $rv = $orig->( $self, %args );
-        return $rv if $self->id;
-
-# we couldn't load a user. ok, but user may exist anyway. It may happen in the following
-# cases:
-# 1) Service has multiple fields in attr_match_list, it's important when we have Name
-# and EmailAddress in there. 
-
-        my (%other) = FindRecordsByOtherFields( $self, %args );
-        while ( my ($search_by, $values) = each %other ) {
-            foreach my $value ( @$values ) {
-                my $rv = $orig->( $self, $search_by => $value );
-                return $rv if $self->id;
-            }
-        }
-
-# 2) RT fields in attr_match_list are mapped to multiple attributes in an external
-# source, for example: attr_map => { EmailAddress => [qw(mail alias1 alias2 alias3)], }
-        my ($search_by, @alternatives) = FindRecordsWithAlternatives( $self, %args );
-        foreach my $value ( @alternatives ) {
-            my $rv = $orig->( $self, %args, $search_by => $value );
-            return $rv if $self->id;
-        }
-
-        return $rv;
-    };
-}
-
-sub FindRecordsWithAlternatives {
-    my $user = shift;
-    my %args = @_;
-
-    # find services that may have alternative values for a field we search by
-    my @info_services = $RT::ExternalInfoPriority ? @{$RT::ExternalInfoPriority} : ();
-    foreach my $service ( splice @info_services ) {
-        my $config = $RT::ExternalSettings->{ $service };
-        next if $config->{'type'} eq 'cookie';
-        next unless
-            grep ref $config->{'attr_map'}{ $_ },
-            @{ $config->{'attr_match_list'} };
-
-        push @info_services, $service;
-    }
-    return unless @info_services;
-
-    # find user in external service and fetch alternative values
-    # for a field
-    foreach my $service (@info_services) {
-        my $config = $RT::ExternalSettings->{$service};
-
-        my $search_by = undef;
-        foreach my $rt_attr ( @{ $config->{'attr_match_list'} } ) {
-            next unless exists $args{ $rt_attr }
-                && defined $args{ $rt_attr }
-                && length $args{ $rt_attr };
-            next unless ref $config->{'attr_map'}{ $rt_attr };
-
-            $search_by = $rt_attr;
-            last;
-        }
-        next unless $search_by;
-
-        my @search_args = (
-            $service,
-            $config->{'attr_map'}{ $search_by },
-            $args{ $search_by },
-            $config->{'attr_map'}{ $search_by },
-        );
-
-        my ($found, %params);
-        if($config->{'type'} eq 'ldap') {
-            ($found, %params) = RT::Authen::ExternalAuth::LDAP::CanonicalizeUserInfo( @search_args );
-        } elsif ($config->{'type'} eq 'db') {
-            ($found, %params) = RT::Authen::ExternalAuth::DBI::CanonicalizeUserInfo( @search_args );
-        } else {
-            $RT::Logger->debug( (caller(0))[3],
-                                "does not consider",
-                                $service,
-                                "a valid information service");
-        }
-        next unless $found;
-
-        my @alternatives = grep defined && length && $_ ne $args{ $search_by }, values %params;
-
-        # Don't Check any more services
-        return @alternatives;
-    }
-    return;
-}
-
-sub FindRecordsByOtherFields {
-    my $user = shift;
-    my %args = @_;
-
-    my @info_services = $RT::ExternalInfoPriority ? @{$RT::ExternalInfoPriority} : ();
-    foreach my $service ( splice @info_services ) {
-        my $config = $RT::ExternalSettings->{ $service };
-        next if $config->{'type'} eq 'cookie';
-        next unless @{ $config->{'attr_match_list'} } > 1;
-
-        push @info_services, $service;
-    }
-    return unless @info_services;
-
-    # find user in external service and fetch alternative values
-    # for a field
-    foreach my $service (@info_services) {
-        my $config = $RT::ExternalSettings->{$service};
-
-        foreach my $search_by ( @{ $config->{'attr_match_list'} } ) {
-            next unless exists $args{ $search_by }
-                && defined $args{ $search_by }
-                && length $args{ $search_by };
-
-            my @fetch;
-            foreach my $field ( @{ $config->{'attr_match_list'} } ) {
-                next if $field eq $search_by;
-
-                my $external = $config->{'attr_map'}{ $field };
-                push @fetch, ref $external? (@$external) : ($external);
-            }
-            my @search_args = (
-                $service,
-                $config->{'attr_map'}{ $search_by },
-                $args{ $search_by },
-                \@fetch,
-            );
-
-            my ($found, %params);
-            if($config->{'type'} eq 'ldap') {
-                ($found, %params) = RT::Authen::ExternalAuth::LDAP::CanonicalizeUserInfo( @search_args );
-            } elsif ($config->{'type'} eq 'db') {
-                ($found, %params) = RT::Authen::ExternalAuth::DBI::CanonicalizeUserInfo( @search_args );
-            } else {
-                $RT::Logger->debug( (caller(0))[3],
-                                    "does not consider",
-                                    $service,
-                                    "a valid information service");
-            }
-            next unless $found;
-
-            my %res =
-                map { $_ => $config->{'attr_map'}{ $_ } }
-                grep defined $config->{'attr_map'}{ $_ },
-                grep $_ ne $search_by,
-                @{ $config->{'attr_match_list'} }
-            ;
-            foreach my $value ( values %res ) {
-                $value = ref $value? [ map $params{$_}, @$value ] : [ $params{ $value } ];
-            }
-            return %res;
-        }
-    }
-    return;
-}
-
-=head2 WorkaroundAutoCreate
-
-RT has C<$AutoCreate> option in the config. However, up to RT 4.0.0 this
-option is no used when account created by incomming email. This module
-workarounds this problem.
-
-=cut
-
-sub WorkaroundAutoCreate {
-    my $user = shift;
-    my $args = shift;
-
-    # CreateUser in RT::Interface::Email doesn't account $RT::AutoCreate
-    # config option. Let's workaround it.
-
-    return unless $RT::AutoCreate && keys %$RT::AutoCreate;
-    return unless keys %$args; # no args - update
-    return unless (caller(4))[3] eq 'RT::Interface::Email::CreateUser';
-
-    my %tmp = %$RT::AutoCreate;
-    delete @tmp{qw(Name EmailAddress RealName Comments)};
-    %$args = (%$args, %$RT::AutoCreate);
-}
-
 1;
index adc5a38..7099632 100644 (file)
@@ -120,10 +120,12 @@ sub GetAuth {
 \r
 sub CanonicalizeUserInfo {\r
     \r
-    my ($service, $key, $value, $attrs) = @_;\r
+    my ($service, $key, $value) = @_;\r
 \r
     my $found = 0;\r
-    my %params = ();\r
+    my %params = (Name         => undef,\r
+                  EmailAddress => undef,\r
+                  RealName     => undef);\r
     \r
     # Load the config\r
     my $config = $RT::ExternalSettings->{$service};\r
@@ -145,21 +147,27 @@ sub CanonicalizeUserInfo {
         return ($found, %params);\r
     }\r
     \r
+    # "where" refers to WHERE section of SQL query\r
+    my ($where_key,$where_value) = ("@{[ $key ]}",$value);\r
+\r
     # Get the list of unique attrs we need\r
-    my $query = "SELECT ". join(', ', @$attrs) ." FROM $table WHERE $key=?";\r
-    my @bind_params = ($value);\r
+    my %db_attrs = map {$_ => 1} values(%{$config->{'attr_map'}});\r
+    my @attrs = keys(%db_attrs);\r
+    my $fields = join(',',@attrs);\r
+    my $query = "SELECT $fields FROM $table WHERE $where_key=?";\r
+    my @bind_params = ($where_value);\r
 \r
     # Uncomment this to trace basic DBI throughput in a log\r
     # DBI->trace(1,'/tmp/dbi.log');\r
     my $dbh = _GetBoundDBIObj($config);\r
-    my $results = $dbh->selectall_arrayref($query, undef, @bind_params);\r
+    my $results_hashref = $dbh->selectall_hashref($query,$key,{},@bind_params);\r
     $dbh->disconnect();\r
 \r
-    if ( @$results != 1 ) {\r
+    if ((scalar keys %$results_hashref) != 1) {\r
         # If returned users <> 1, we have no single unique user, so prepare to die\r
         my $death_msg;\r
         \r
-           if ( @$results == 0) {\r
+           if ((scalar keys %$results_hashref) == 0) {\r
             # If no user...\r
                $death_msg = "No User Found in External Database!";\r
         } else {\r
@@ -182,8 +190,14 @@ sub CanonicalizeUserInfo {
 \r
     # We haven't dropped out, so DB search must have succeeded with \r
     # exactly 1 result. Get the result and set $found to 1\r
+    my $result = $results_hashref->{$value};\r
\r
+    # Use the result to populate %params for every key we're given in the config\r
+    foreach my $key (keys(%{$config->{'attr_map'}})) {\r
+        $params{$key} = ($result->{$config->{'attr_map'}->{$key}})[0];\r
+    }\r
+    \r
     $found = 1;\r
-    @params{ @$attrs } = @{ $results->[0] };\r
   \r
     return ($found, %params);\r
 }\r
index 48e97c9..207b9fe 100644 (file)
@@ -9,6 +9,7 @@ use strict;
 require Net::SSLeay if $RT::ExternalServiceUsesSSLorTLS;\r
 \r
 sub GetAuth {\r
+    \r
     my ($service, $username, $password) = @_;\r
     \r
     my $config = $RT::ExternalSettings->{$service};\r
@@ -30,17 +31,36 @@ sub GetAuth {
     my $ldap = _GetBoundLdapObj($config);\r
     return 0 unless ($ldap);\r
 \r
-    $filter = '(&'\r
-        .'('.  $attr_map->{'Name'} .  '=' .  $username .  ')' . \r
-        $filter\r
-    .')';\r
-\r
-    my $ldap_msg = PerformSearch(\r
-        $ldap,\r
-        base   => $base,\r
-        filter => $filter,\r
-        attrs  => \@attrs\r
-    ) or return 0;;\r
+    $filter = Net::LDAP::Filter->new(   '(&(' . \r
+                                        $attr_map->{'Name'} . \r
+                                        '=' . \r
+                                        $username . \r
+                                        ')' . \r
+                                        $filter . \r
+                                        ')'\r
+                                    );\r
+\r
+    $RT::Logger->debug( "LDAP Search === ",\r
+                        "Base:",\r
+                        $base,\r
+                        "== Filter:", \r
+                        $filter->as_string,\r
+                        "== Attrs:", \r
+                        join(',',@attrs));\r
+\r
+    my $ldap_msg = $ldap->search(   base   => $base,\r
+                                    filter => $filter,\r
+                                    attrs  => \@attrs);\r
+\r
+    unless ($ldap_msg->code == LDAP_SUCCESS || $ldap_msg->code == LDAP_PARTIAL_RESULTS) {\r
+        $RT::Logger->debug( "search for", \r
+                            $filter->as_string, \r
+                            "failed:", \r
+                            ldap_error_name($ldap_msg->code), \r
+                            $ldap_msg->code);\r
+        # Didn't even get a partial result - jump straight to the next external auth service\r
+        return 0;\r
+    }\r
 \r
     unless ($ldap_msg->count == 1) {\r
         $RT::Logger->info(  $service,\r
@@ -75,13 +95,33 @@ sub GetAuth {
     # The user is authenticated ok, but is there an LDAP Group to check?\r
     if ($group) {\r
         # If we've been asked to check a group...\r
-        $ldap_msg = PerformSearch(\r
-            $ldap,\r
-            base   => $group,\r
-            filter => "(${group_attr}=${ldap_dn})",\r
-            attrs  => \@attrs,\r
-            scope  => 'base'\r
-        ) or return 0;\r
+        $filter = Net::LDAP::Filter->new("(${group_attr}=${ldap_dn})");\r
+        \r
+        $RT::Logger->debug( "LDAP Search === ",\r
+                            "Base:",\r
+                            $base,\r
+                            "== Filter:", \r
+                            $filter->as_string,\r
+                            "== Attrs:", \r
+                            join(',',@attrs));\r
+        \r
+        $ldap_msg = $ldap->search(  base   => $group,\r
+                                    filter => $filter,\r
+                                    attrs  => \@attrs,\r
+                                    scope  => 'base');\r
+\r
+        # And the user isn't a member:\r
+        unless ($ldap_msg->code == LDAP_SUCCESS || \r
+                $ldap_msg->code == LDAP_PARTIAL_RESULTS) {\r
+            $RT::Logger->critical(  "Search for", \r
+                                    $filter->as_string, \r
+                                    "failed:",\r
+                                    ldap_error_name($ldap_msg->code), \r
+                                    $ldap_msg->code);\r
+\r
+            # Fail auth - jump to next external auth service\r
+            return 0;\r
+        }\r
 \r
         unless ($ldap_msg->count == 1) {\r
             $RT::Logger->info(  $service,\r
@@ -108,23 +148,40 @@ sub GetAuth {
 \r
 sub CanonicalizeUserInfo {\r
     \r
-    my ($service, $key, $value, $attrs) = @_;\r
+    my ($service, $key, $value) = @_;\r
+\r
+    my $found = 0;\r
+    my %params = (Name         => undef,\r
+                  EmailAddress => undef,\r
+                  RealName     => undef);\r
 \r
     # Load the config\r
     my $config = $RT::ExternalSettings->{$service};\r
+   \r
+    # Figure out what's what\r
+    my $base            = $config->{'base'};\r
+    my $filter          = $config->{'filter'};\r
 \r
-    my $filter = JoinFilters(\r
-        '&',\r
-        JoinFilters('|', map "($_=$value)", ref $key? @$key: ($key) ),\r
-        $config->{'filter'},\r
-    ) or return (0);\r
+    # Get the list of unique attrs we need\r
+    my @attrs = values(%{$config->{'attr_map'}});\r
+\r
+    # This is a bit confusing and probably broken. Something to revisit..\r
+    my $filter_addition = ($key && $value) ? "(". $key . "=$value)" : "";\r
+    if(defined($filter) && ($filter ne "()")) {\r
+        $filter = Net::LDAP::Filter->new(   "(&" . \r
+                                            $filter . \r
+                                            $filter_addition . \r
+                                            ")"\r
+                                        ); \r
+    } else {\r
+        $RT::Logger->debug( "LDAP Filter invalid or not present.");\r
+    }\r
 \r
-    my $base = $config->{'base'};\r
     unless (defined($base)) {\r
         $RT::Logger->critical(  (caller(0))[3],\r
                                 "LDAP baseDN not defined");\r
         # Drop out to the next external information service\r
-        return (0);\r
+        return ($found, %params);\r
     }\r
 \r
     # Get a Net::LDAP object based on the config we provide\r
@@ -132,33 +189,85 @@ sub CanonicalizeUserInfo {
 \r
     # Jump to the next external information service if we can't get one, \r
     # errors should be logged by _GetBoundLdapObj so we don't have to.\r
-    return (0) unless ($ldap);\r
-\r
-    my $ldap_msg = PerformSearch(\r
-        $ldap,\r
-        base   => $base,\r
-        filter => $filter,\r
-        attrs  => $attrs\r
-    );\r
-    \r
-    # If there's only one match, we're good; more than one and\r
-    # we don't know which is the right one so we skip it.\r
-    unless ($ldap_msg && $ldap_msg->count == 1) {\r
-        Unbind( $ldap );\r
-        return (0);\r
-    }\r
-\r
-    my %res;\r
-    my $entry = $ldap_msg->first_entry;\r
-    foreach my $attr ( @$attrs ) {\r
-        if ( $attr eq 'dn' ) {\r
-            $res{ $attr } = $entry->dn;\r
+    return ($found, %params) unless ($ldap);\r
+\r
+    # Do a search for them in LDAP\r
+    $RT::Logger->debug( "LDAP Search === ",\r
+                        "Base:",\r
+                        $base,\r
+                        "== Filter:", \r
+                        $filter->as_string,\r
+                        "== Attrs:", \r
+                        join(',',@attrs));\r
+\r
+    my $ldap_msg = $ldap->search(base   => $base,\r
+                                 filter => $filter,\r
+                                 attrs  => \@attrs);\r
+\r
+    # If we didn't get at LEAST a partial result, just die now.\r
+    if ($ldap_msg->code != LDAP_SUCCESS and \r
+        $ldap_msg->code != LDAP_PARTIAL_RESULTS) {\r
+        $RT::Logger->critical(  (caller(0))[3],\r
+                                ": Search for ",\r
+                                $filter->as_string,\r
+                                " failed: ",\r
+                                ldap_error_name($ldap_msg->code), \r
+                                $ldap_msg->code);\r
+        # $found remains as 0\r
+        \r
+        # Drop out to the next external information service\r
+        $ldap_msg = $ldap->unbind();\r
+        if ($ldap_msg->code != LDAP_SUCCESS) {\r
+            $RT::Logger->critical(  (caller(0))[3],\r
+                                    ": Could not unbind: ", \r
+                                    ldap_error_name($ldap_msg->code), \r
+                                    $ldap_msg->code);\r
+        }\r
+        undef $ldap;\r
+        undef $ldap_msg;\r
+        return ($found, %params);\r
+      \r
+    } else {\r
+        # If there's only one match, we're good; more than one and\r
+        # we don't know which is the right one so we skip it.\r
+        if ($ldap_msg->count == 1) {\r
+            my $entry = $ldap_msg->first_entry();\r
+            foreach my $key (keys(%{$config->{'attr_map'}})) {\r
+                if ($RT::LdapAttrMap->{$key} eq 'dn') {\r
+                    $params{$key} = $entry->dn();\r
+                } else {\r
+                    $params{$key} = \r
+                      ($entry->get_value($config->{'attr_map'}->{$key}))[0];\r
+                }\r
+\r
+            }\r
+            $found = 1;\r
         } else {\r
-            $res{ $attr } = ($entry->get_value( $attr ))[0];\r
+            # Drop out to the next external information service\r
+            $ldap_msg = $ldap->unbind();\r
+            if ($ldap_msg->code != LDAP_SUCCESS) {\r
+                $RT::Logger->critical(  (caller(0))[3],\r
+                                        ": Could not unbind: ", \r
+                                        ldap_error_name($ldap_msg->code), \r
+                                        $ldap_msg->code);\r
+            }\r
+            undef $ldap;\r
+            undef $ldap_msg;\r
+            return ($found, %params);\r
         }\r
     }\r
-    Unbind( $ldap );\r
-    return (1, %res);\r
+    $ldap_msg = $ldap->unbind();\r
+    if ($ldap_msg->code != LDAP_SUCCESS) {\r
+        $RT::Logger->critical(  (caller(0))[3],\r
+                                ": Could not unbind: ", \r
+                                ldap_error_name($ldap_msg->code), \r
+                                $ldap_msg->code);\r
+    }\r
+\r
+    undef $ldap;\r
+    undef $ldap_msg;\r
+\r
+    return ($found, %params);\r
 }\r
 \r
 sub UserExists {\r
@@ -166,6 +275,7 @@ sub UserExists {
    $RT::Logger->debug("UserExists params:\nusername: $username , service: $service"); \r
     my $config              = $RT::ExternalSettings->{$service};\r
     \r
+    my $base                = $config->{'base'};\r
     my $filter              = $config->{'filter'};\r
 \r
     # While LDAP filters must be surrounded by parentheses, an empty set\r
@@ -189,13 +299,20 @@ sub UserExists {
     my $ldap = _GetBoundLdapObj($config);\r
     return unless $ldap;\r
 \r
+    my @attrs = values(%{$config->{'attr_map'}});\r
+\r
     # Check that the user exists in the LDAP service\r
-    my $user_found = PerformSearch(\r
-        $ldap,\r
-        base    => $config->{'base'},\r
-        filter  => $filter,\r
-        attrs   => ['uid'],\r
-    ) or return 0;\r
+    $RT::Logger->debug( "LDAP Search === ",\r
+                        "Base:",\r
+                        $base,\r
+                        "== Filter:", \r
+                        $filter->as_string,\r
+                        "== Attrs:", \r
+                        join(',',@attrs));\r
+    \r
+    my $user_found = $ldap->search( base    => $base,\r
+                                    filter  => $filter,\r
+                                    attrs   => \@attrs);\r
 \r
     if($user_found->count < 1) {\r
         # If 0 or negative integer, no user found or major failure\r
@@ -275,13 +392,21 @@ sub UserDisabled {
     my $ldap = _GetBoundLdapObj($config);\r
     next unless $ldap;\r
 \r
-    my $disabled_users = PerformSearch(\r
-        $ldap,\r
-        base   => $base, \r
-        filter => $search_filter, \r
-        attrs  => ['uid'], # We only need the UID for confirmation now\r
-    ) or return 0;\r
-\r
+    # We only need the UID for confirmation now, \r
+    # the other information would waste time and bandwidth\r
+    my @attrs = ('uid'); \r
+    \r
+    $RT::Logger->debug( "LDAP Search === ",\r
+                        "Base:",\r
+                        $base,\r
+                        "== Filter:", \r
+                        $search_filter->as_string,\r
+                        "== Attrs:", \r
+                        join(',',@attrs));\r
+          \r
+    my $disabled_users = $ldap->search(base   => $base, \r
+                                       filter => $search_filter, \r
+                                       attrs  => \@attrs);\r
     # If ANY results are returned, \r
     # we are going to assume the user should be disabled\r
     if ($disabled_users->count) {\r
@@ -350,69 +475,6 @@ sub _GetBoundLdapObj {
     }\r
 }\r
 \r
-sub Unbind {\r
-    my $ldap = shift;\r
-    my $res = $ldap->unbind;\r
-    return $res if !$res || $res->code == LDAP_SUCCESS;\r
-\r
-    $RT::Logger->error(\r
-        (caller(1))[3], ": Could not unbind: ", \r
-        ldap_error_name($res->code), \r
-        $res->code\r
-    );\r
-    return $res;\r
-}\r
-\r
-sub PerformSearch {\r
-    my $ldap = shift;\r
-    my %args = @_;\r
-\r
-    $args{'filter'} = Net::LDAP::Filter->new($args{'filter'})\r
-        if $args{'filter'} && !ref $args{'filter'};\r
-\r
-    $RT::Logger->debug(\r
-        "LDAP Search === ",\r
-        $args{'base'}? ("Base:", $args{'base'}) : (),\r
-        $args{'filter'}? ("== Filter:", $args{'filter'}->as_string) : (),\r
-        $args{'attrs'}? ("== Attrs:", join ',', @{ $args{'attrs'} }) : (),\r
-    );\r
-    \r
-    my $res = $ldap->search( %args );\r
-    return undef unless $res;\r
-\r
-    unless (\r
-        $res->code == LDAP_SUCCESS\r
-        || $res->code == LDAP_PARTIAL_RESULTS\r
-    ) {\r
-        $RT::Logger->error(\r
-            "Search for", $args{'filter'}->as_string, "failed:",\r
-            ldap_error_name($res->code), $res->code\r
-        );\r
-\r
-        return undef;\r
-    }\r
-    return $res;\r
-}\r
-\r
-sub JoinFilters {\r
-    my $op = shift;\r
-    my @list =\r
-        grep defined && length && $_ ne '()',\r
-        map ref $_? $_->as_string : $_,\r
-        @_;\r
-    return undef unless @list;\r
-\r
-    my $str = @list > 1\r
-        ? "($op". join( '', @list ) .')'\r
-        : $list[0]\r
-    ;\r
-    my $obj = Net::LDAP::Filter->new( $str );\r
-    $RT::Logger->error("'$str' is not valid LDAP filter")\r
-        unless $obj;\r
-\r
-    return $obj;\r
-}\r
-\r
 # }}}\r
 \r
 1;\r
index 1da6ada..83ae55a 100644 (file)
@@ -1,4 +1,4 @@
-=head2 Wed May 18 17:17:07 2011: C<Module> L<RT::Authen::ExternalAuth|RT::Authen::ExternalAuth>
+=head2 Wed May 11 15:55:12 2011: C<Module> L<RT::Authen::ExternalAuth|RT::Authen::ExternalAuth>
 
 =over 4