Technical Bard
Technical Bard

Reputation: 4485

Finding partial strings in a list of strings - python

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.

Upvotes: 0

Views: 859

Answers (2)

Alex Martelli
Alex Martelli

Reputation: 882181

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!-).

Upvotes: 2

ars
ars

Reputation: 123538

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

Upvotes: 1

Related Questions