LdapPermissionGroupProvider.get_permission_groups() too slow
|Reported by:||Andy S||Owned by:||Emmanuel Blot|
|Severity:||major||Keywords:||ldap, ldapplugin, 0.12|
we are using LdapPlugin to get group info from LDAP database. It worked perfectly until one day I decided to enable 'restrict_owner' option. Things became very slow with that option enabled, new ticket form took ~10 seconds to display. I did some debugging and it seems that the problem is in the LDAP plugin, the algorithm which extracts user's groups from LDAP is not very efficient.
From my understanding of the code it works by first exctracting the complete list of all groups from LDAP and then checking that given user is a member of every LDAP group. With too many groups (our LDAP server has more than 700 groups) combinatorial complexity kills performance.
In principle the group membership for a given user name can be obtained with a single LDAP search. Below is a patch that reimplments _get_user_groups() method to use one LDAP operation with a filter like "(&(objectclass=PosixGroup)(memberid=USER))". This patch was tested and it works much faster in our environment. Would be nice if you could include this optimization into the next plugin release.
*** api.py.orig 2010-07-06 08:51:52.000000000 -0700 --- api.py 2010-07-06 11:13:33.000000000 -0700 *************** *** 147,161 **** def _get_user_groups(self, username): """Returns a list of all groups a user belongs to""" ! ldap_groups = self._ldap.get_groups() groups =  for group in ldap_groups: ! if self._ldap.is_in_group(self.util.user_attrdn(username), group): ! m = DN_RE.search(group) ! if m: ! groupname = GROUP_PREFIX + m.group('rdn') ! if groupname not in groups: ! groups.append(groupname) return groups class LdapPermissionStore(Component): --- 147,160 ---- def _get_user_groups(self, username): """Returns a list of all groups a user belongs to""" ! ldap_groups = self._ldap.get_user_groups(self.util.user_attrdn(username)) groups =  for group in ldap_groups: ! m = DN_RE.search(group) ! if m: ! groupname = GROUP_PREFIX + m.group('rdn') ! if groupname not in groups: ! groups.append(groupname) return groups class LdapPermissionStore(Component): *************** *** 570,575 **** --- 569,588 ---- return cr return False + def get_user_groups(self, userdn): + """Return a list of group dns where user is a member""" + if self.groupmemberisdn: + udn = userdn + else: + m = re.match('[^=]+=([^,]+)', userdn) + if m is None: + self.log.warn('Malformed userdn: %s' % userdn) + return  + udn = m.group(1) + filter = "(&(objectclass=%s)(%s=%s))" % (self.groupname, self.groupmember, udn) + groups = self.get_dn(self.basedn, filter) + return groups + def get_dn(self, basedn, filterstr): """Return a list of dns that satisfy the LDAP filter""" dns =