Opened 16 years ago
Last modified 16 years ago
#3993 new defect
Trac LDAP plugin makes too many round trips for group membership
Reported by: | Jason Dusek | Owned by: | Emmanuel Blot |
---|---|---|---|
Priority: | normal | Component: | LdapPlugin |
Severity: | major | Keywords: | |
Cc: | Trac Release: | 0.11 |
Description
I have written a group membership routine to allow faster searches to be performed. The Trac plugin need only go to the LDAP server one time to get a user's groups listing, instead of running a separate query for each group on the server.
-
ldapplugin/api.py
146 146 147 147 def _get_user_groups(self, username): 148 148 """Returns a list of all groups a user belongs to""" 149 ldap_groups = self._ldap.get_groups() 150 groups = [] 151 for group in ldap_groups: 152 if self._ldap.is_in_group(self.util.user_attrdn(username), group): 153 m = DN_RE.search(group) 154 if m: 155 groupname = GROUP_PREFIX + m.group('rdn') 156 if groupname not in groups: 157 groups.append(groupname) 158 return groups 149 return [GROUP_PREFIX + g for g in self._ldap.groups_for_user(username)] 159 150 160 151 class LdapPermissionStore(Component): 161 152 """ … … 541 532 groups = self.get_dn(self.basedn, 'objectclass=' + self.groupname) 542 533 return groups 543 534 535 def groups_for_user(self, uid): 536 """Obtain group listing for a particular uid.""" 537 if self.groupmemberisdn: 538 dns = get_dn(self.basedn, '(uid=%s)' % uid) 539 if dns: 540 uidstr = dns[0] 541 else: 542 return False 543 else: 544 uidstr = uid 545 filter = ('(&(objectclass=%s)(%s=%s))' % 546 (self.groupname, self.groupmember, uidstr)) 547 results = self._search( self.basedn 548 , filter 549 , [self.groupattr] 550 , ldap.SCOPE_SUBTREE ) 551 self.log.error("Found: %s", str(results)) 552 return [ attrs[self.groupattr][0] for (dn, attrs) in results ] 553 544 554 def is_in_group(self, userdn, groupdn): 545 555 """Tell whether the uid is member of the group""" 546 556 if self.groupmemberisdn:
Attachments (0)
Change History (4)
comment:1 Changed 16 years ago by
comment:2 follow-ups: 3 4 Changed 16 years ago by
Oh, there is a pointless error logging line in my code.
-
ldapplugin/api.py
146 146 147 147 def _get_user_groups(self, username): 148 148 """Returns a list of all groups a user belongs to""" 149 ldap_groups = self._ldap.get_groups() 150 groups = [] 151 for group in ldap_groups: 152 if self._ldap.is_in_group(self.util.user_attrdn(username), group): 153 m = DN_RE.search(group) 154 if m: 155 groupname = GROUP_PREFIX + m.group('rdn') 156 if groupname not in groups: 157 groups.append(groupname) 158 return groups 149 return [GROUP_PREFIX + g for g in self._ldap.groups_for_user(username)] 159 150 160 151 class LdapPermissionStore(Component): 161 152 """ … … 541 532 groups = self.get_dn(self.basedn, 'objectclass=' + self.groupname) 542 533 return groups 543 534 535 def groups_for_user(self, uid): 536 """Obtain group listing for a particular uid.""" 537 if self.groupmemberisdn: 538 dns = get_dn(self.basedn, '(uid=%s)' % uid) 539 if dns: 540 uidstr = dns[0] 541 else: 542 return False 543 else: 544 uidstr = uid 545 filter = ('(&(objectclass=%s)(%s=%s))' % 546 (self.groupname, self.groupmember, uidstr)) 547 results = self._search( self.basedn 548 , filter 549 , [self.groupattr] 550 , ldap.SCOPE_SUBTREE ) 551 return [ attrs[self.groupattr][0] for (dn, attrs) in results ] 552 544 553 def is_in_group(self, userdn, groupdn): 545 554 """Tell whether the uid is member of the group""" 546 555 if self.groupmemberisdn:
comment:3 Changed 16 years ago by
Oh, there is a pointless error logging line in my code.
Thanks, your patch speed up my setup. With the original setup, I was having some LDAP errors in lots of groups to which a user didn't belonged to. It worked yes, but it was slow.
comment:4 Changed 16 years ago by
Replying to jason.dusek@gmail.com:
Oh, there is a pointless error logging line in my code.
I'm using this in an Active Directory environment where uid
is not the attribute for group members. So I changed that to use self.uidattr
instead.
I'm not sure why I had to change an escaped ',' but the result from get_dn() needed an extra backslash to work correctly. Our user DNs are of the form "CN=Last\, First,OU=..."
and the search string needed 3 backslashes, like "(member=CN=Last\\\, First,OU=...)"
. In any case, enjoy.
-
api.py
23 23 import re 24 24 import time 25 25 import ldap 26 import string 26 27 27 28 from trac.core import * 28 29 from trac.perm import IPermissionGroupProvider, IPermissionStore … … 147 148 148 149 def _get_user_groups(self, username): 149 150 """Returns a list of all groups a user belongs to""" 150 ldap_groups = self._ldap.get_groups() 151 groups = [] 152 for group in ldap_groups: 153 if self._ldap.is_in_group(self.util.user_attrdn(username), group): 154 m = DN_RE.search(group) 155 if m: 156 groupname = GROUP_PREFIX + m.group('rdn') 157 if groupname not in groups: 158 groups.append(groupname) 159 return groups 151 return [GROUP_PREFIX + g for g in self._ldap.groups_for_user(username)] 160 152 161 153 class LdapPermissionStore(Component): 162 154 """ … … 554 546 groups = self.get_dn(self.basedn, 'objectclass=' + self.groupname) 555 547 return groups 556 548 549 def groups_for_user(self, uid): 550 """Obtain group listing for a particular uid.""" 551 if self.groupmemberisdn: 552 dns = self.get_dn(self.basedn, '(%s=%s)' % (self.uidattr,uid) ) 553 if dns: 554 uidstr = dns[0] 555 else: 556 return False 557 else: 558 uidstr = uid 559 filter = ('(&(objectclass=%s)(%s=%s))' % 560 (self.groupname, self.groupmember, string.replace(uidstr,"\\,","\\\,") ) ) 561 self.log.debug(filter) 562 results = self._search( self.basedn 563 , filter 564 , [self.groupattr] 565 , ldap.SCOPE_SUBTREE ) 566 return [ attrs[self.groupattr][0] for (dn, attrs) in results ] 567 557 568 def is_in_group(self, userdn, groupdn): 558 569 """Tell whether the uid is member of the group""" 559 570 if self.groupmemberisdn:
I have not tested this with
dn
s instead ofuid
s -- I don't have that setup readily available -- but I am using it foruid
s on a live system and it works fine.