Skip Menu |

This queue is for tickets about the RT-Authen-ExternalAuth CPAN distribution.

Report information
The Basics
Id: 72113
Status: open
Priority: 0/
Queue: RT-Authen-ExternalAuth

People
Owner: Nobody in particular
Requestors: kiwiroy [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 0.09
Fixed in: (no value)



Subject: group list membership - with possible patch
I don't know if this is a common problem with LDAP, it is with our AD, but resolving memberships in groups of groups is not possible. For example say there is GROUP_A which has multiple members GROUP_X, GROUP_Y and GROUP_Z (there are no objectClass=user members). Using GROUP_A in the $ExternalSettings->{'MyLDAP'}{'group'} does not authenticate a user in GROUP_X. As a result I submit the attached patch which may be of use to someone/make it into the dist. The approach is to specify a list of groups ({'MyLDAP'}{'group_list'} = [qw/GROUP_X GROUP_Y GROUP_Z/]). The user may be a member of one or all and both options are available for auth based on the value of {'MyLDAP'}{'group_list_rule'} (see RT_SiteConfig.pm in patch). This may not be the best solution, so comments welcome.
Subject: rt-authen-externalauth_group-list.patch
diff --git a/etc/RT_SiteConfig.pm b/etc/RT_SiteConfig.pm index 4c38a5d..1f80c91 100644 --- a/etc/RT_SiteConfig.pm +++ b/etc/RT_SiteConfig.pm @@ -128,6 +128,10 @@ Set($ExternalSettings, { # AN EXAMPLE DB SERVICE 'net_ldap_args' => [ version => 3 ], # Does authentication depend on group membership? What group name? 'group' => 'GROUP_NAME', + # Does authentication depend on group membership? What group names? + 'group_list' => [ 'GROUP_NAME_1', 'GROUP_NAME_2' ], + # Are _all_ the groups required ('and') or just one ('or')? + 'group_list_rule' => 'or', # What is the attribute for the group object that determines membership? 'group_attr' => 'GROUP_ATTR', ## RT ATTRIBUTE MATCHING SECTION diff --git a/lib/RT/Authen/ExternalAuth/LDAP.pm b/lib/RT/Authen/ExternalAuth/LDAP.pm index 885c7dd..750583d 100644 --- a/lib/RT/Authen/ExternalAuth/LDAP.pm +++ b/lib/RT/Authen/ExternalAuth/LDAP.pm @@ -18,6 +18,8 @@ sub GetAuth { my $base = $config->{'base'}; my $filter = $config->{'filter'}; my $group = $config->{'group'}; + my $group_list = $config->{'group_list'} || []; + my $group_rule = lc($config->{'group_list_rule'} || 'and'); my $group_attr = $config->{'group_attr'}; my $attr_map = $config->{'attr_map'}; my @attrs = ('dn'); @@ -92,47 +94,40 @@ sub GetAuth { return 0; } - # The user is authenticated ok, but is there an LDAP Group to check? - if ($group) { - # If we've been asked to check a group... - $filter = Net::LDAP::Filter->new("(${group_attr}=${ldap_dn})"); - - $RT::Logger->debug( "LDAP Search === ", - "Base:", - $base, - "== Filter:", - $filter->as_string, - "== Attrs:", - join(',',@attrs)); - - $ldap_msg = $ldap->search( base => $group, - filter => $filter, - attrs => \@attrs, - scope => 'base'); - - # And the user isn't a member: - unless ($ldap_msg->code == LDAP_SUCCESS || - $ldap_msg->code == LDAP_PARTIAL_RESULTS) { - $RT::Logger->critical( "Search for", - $filter->as_string, - "failed:", - ldap_error_name($ldap_msg->code), - $ldap_msg->code); + ref($group_list) eq 'ARRAY' || do { $group_list = []; }; - # Fail auth - jump to next external auth service - return 0; - } - - unless ($ldap_msg->count == 1) { - $RT::Logger->info( $service, - "AUTH FAILED:", - $username); - - # Fail auth - jump to next external auth service - return 0; - } + # The user is authenticated ok, but is there an LDAP Group to check? + if ($group || @$group_list) { + ## incase there is only a group, push the group to the list. + ## if there is no group, hope there's a list + push @$group_list, $group if $group; + + ## sanity check 'group_rule' + if($group_rule ne 'or' && $group_rule ne 'and') { + $group_rule = 'and'; + } + + my $membership = 0; + foreach my $group (@$group_list){ + my $member = _UserIsGroupMember($ldap, $service, $base, $ldap_dn, $group_attr, $group, $username, @attrs); + $membership += $member; + + ## optimise, bug out as soon as possible + last if $membership && $group_rule eq 'or'; + last if $member == 0 && $group_rule eq 'and'; + } + + if(($group_rule eq 'or' && !$membership) || + ($group_rule eq 'and' && $membership != @$group_list)) { + $RT::Logger->info( $service, + "AUTH FAILED:", + $username ); + + # Fail auth - jump to next external auth service + return 0; + } } - + # Any other checks you want to add? Add them here. # If we've survived to this point, we're good. @@ -476,4 +471,52 @@ sub _GetBoundLdapObj { # }}} +# {{{ sub _UserIsGroupMember +sub _UserIsGroupMember { + my ($ldap, $service, $base, $ldap_dn, + $group_attr, $group, $username, @attrs) = @_; + my $is_member = 1; + + # If we've been asked to check a group... + my $filter = Net::LDAP::Filter->new("(${group_attr}=${ldap_dn})"); + + $RT::Logger->debug( "LDAP Search === ", + "Base:", + $base, + "== Filter:", + $filter->as_string, + "== Attrs:", + join(',',@attrs)); + + my $ldap_msg = $ldap->search( base => $group, + filter => $filter, + attrs => \@attrs, + scope => 'base'); + + # And the user isn't a member: + unless ($ldap_msg->code == LDAP_SUCCESS || + $ldap_msg->code == LDAP_PARTIAL_RESULTS) { + $RT::Logger->critical( "Search for", + $filter->as_string, + "failed:", + ldap_error_name($ldap_msg->code), + $ldap_msg->code); + + # Fail auth - jump to next external auth service + return 0; + } + + unless ($ldap_msg->count == 1) { + $RT::Logger->info( $service, + "GROUP MEMBERSHIP FAILED:", + $username, '! in', $group); + + return 0; + } + + return $is_member; +} + +# }}} + 1;
Subject: Re: [rt.cpan.org #72113] group list membership - with possible patch
Date: Wed, 2 Nov 2011 19:17:42 -0400
To: "https://www.google.com/accounts/o8/id?id=AItOawm6mqN_GWI3FdLYzSWUXoFefrtzwr-SnZk via RT" <bug-RT-Authen-ExternalAuth [...] rt.cpan.org>
From: Kevin Falcone <falcone [...] bestpractical.com>
On Wed, Nov 02, 2011 at 02:49:40AM -0400, https://www.google.com/accounts/o8/id?id=AItOawm6mqN_GWI3FdLYzSWUXoFefrtzwr-SnZk via RT wrote: Hi Show quoted text
> Wed Nov 02 02:49:39 2011: Request 72113 was acted upon. > in GROUP_X. As a result I submit the attached patch which may be of use > to someone/make it into the dist.
The patch came through with some really bizarre line endings. Would you be able to regenerate it? A few comments from reading through it. Show quoted text
> The approach is to specify a list of groups ({'MyLDAP'}{'group_list'} = > [qw/GROUP_X GROUP_Y GROUP_Z/]). The user may be a member of one or all > and both options are available for auth based on the value of > {'MyLDAP'}{'group_list_rule'} (see RT_SiteConfig.pm in patch). This may > not be the best solution, so comments welcome.
Is there a reason not to just make group dual life as a string and an arrayref of strings? I worry about adding more config options to something that is already complex and underdocumented. I also know that at some point in the next month or so, we need to merge down some of the existing branches (you can see them here) https://github.com/bestpractical/rt-authen-externalauth/branches and I'm not sure how well they'll all merge without looking a lot more deeply. You may find it useful to test the multiple emails branch and generate your diff from that. Thanks -kevin
Show quoted text
> I don't know if this is a common problem with LDAP, it is with our AD, > but resolving memberships in groups of groups is not possible. For > example say there is GROUP_A which has multiple members GROUP_X,
GROUP_Y Show quoted text
> and GROUP_Z (there are no objectClass=user members). Using GROUP_A in > the $ExternalSettings->{'MyLDAP'}{'group'} does not authenticate a
user Show quoted text
> in GROUP_X. As a result I submit the attached patch which may be of
use Show quoted text
> to someone/make it into the dist.
I suspect you really just want an option to change the scope of the group membership check. Currently it searches just the base, not the whole subtree. This avoids the need to maintain a list of groups. The list of the groups may be useful in other cases, but your problem is better solved with a scope change, I believe. Thomas