tags:

views:

191

answers:

2

I am trying to check if a user is a member of an Active Directory group, and I have this:

ldap.set_option(ldap.OPT_REFERRALS, 0)
try:
  con = ldap.initialize(LDAP_URL)
  con.simple_bind_s(userid+"@"+ad_settings.AD_DNS_NAME, password)
  ADUser = con.search_ext_s(ad_settings.AD_SEARCH_DN, ldap.SCOPE_SUBTREE, \  
           "sAMAccountName=%s" % userid, ad_settings.AD_SEARCH_FIELDS)[0][1]
except ldap.LDAPError:
  return None

ADUser returns a list of strings:

{'givenName': ['xxxxx'],
 'mail': ['[email protected]'],
 'memberOf': ['CN=group1,OU=Projects,OU=Office,OU=company,DC=domain,DC=com',
              'CN=group2,OU=Projects,OU=Office,OU=company,DC=domain,DC=com',
              'CN=group3,OU=Projects,OU=Office,OU=company,DC=domain,DC=com',
              'CN=group4,OU=Projects,OU=Office,OU=company,DC=domain,DC=com'],
 'sAMAccountName': ['myloginid'],
 'sn': ['Xxxxxxxx']}

Of course in the real world the group names are verbose and of varied structure, and users will belong to tens or hundreds of groups.

If I get the list of groups out as ADUser.get('memberOf')[0], what is the best way to check if any members of a separate list exist in the main list?

For example, the check list would be ['group2', 'group16'] and I want to get a true/false answer as to whether any of the smaller list exist in the main list.

+1  A: 

You can use set intersection (& operator) once you parse the group list out. For example:

> memberOf = 'CN=group1,OU=Projects,OU=Office,OU=company,DC=domain,DC=com'

> groups = [token.split('=')[1] for token in memberOf.split(',')]

> groups
['group1', 'Projects', 'Office', 'company', 'domain', 'com']

> checklist1 = ['group1', 'group16']

> set(checklist1) & set(groups)
set(['group1'])

> checklist2 = ['group2', 'group16']

> set(checklist2) & set(groups)
set([])

Note that a conditional evaluation on a set works the same as for lists and tuples. True if there are any elements in the set, False otherwise. So, "if set(checklist2) & set(groups): ..." would not execute since the condition evaluates to False in the above example (the opposite is true for the checklist1 test).

Also see:

http://docs.python.org/library/sets.html

ars
+2  A: 

If the format example you give is somewhat reliable, something like:

import re
grps = re.compile(r'CN=(\w+)').findall

def anyof(short_group_list, adu):
  all_groups_of_user = set(g for gs in adu.get('memberOf',()) for g in grps(gs))
  return sorted(all_groups_of_user.intersection(short_group_list))

where you pass your list such as ['group2', 'group16'] as the first argument, your ADUser dict as the second argument; this returns an alphabetically sorted list (possibly empty, meaning "none") of the groups, among those in short_group_list, to which the user belongs.

It's probably not much faster to just a bool, but, if you insist, changing the second statement of the function to:

  return any(g for g in short_group_list if g in all_groups_of_user)

might possibly save a certain amount of time in the "true" case (since any short-circuits) though I suspect not in the "false" case (where the whole list must be traversed anyway). If you care about the performance issue, best is to benchmark both possibilities on data that's realistic for your use case!

If performance isn't yet good enough (and a bool yes/no is sufficient, as you say), try reversing the looping logic:

def anyof_v2(short_group_list, adu):
  gset = set(short_group_list)
  return any(g for gs in adu.get('memberOf',()) for g in grps(gs) if g in gset)

any's short-circuit abilities might prove more useful here (at least in the "true" case, again -- because, again, there's no way to give a "false" result without examining ALL the possibilities anyway!-).

Alex Martelli
The anyof() definition with the boolean return is perfect. searching the list is faster than the latency of the network to give me the list in the first place, so performance is fine.
Technical Bard