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;