My current logic is as below.
- Take username and domain name.
Loop over each domain controller in that domain and get the user entry.
Consider the latest entry from all of them based onwhenChangedtimestamp that we always capture, and fetch the user groups i.e.memberOffrom it. - For each user group,
loop over each domain controller in the domain and get the group entry.
Consider the latest entry from all of them, and fetch the group's sub groups i.e.memberOffield. (Do this recursively.) - Send the final list back to user.
Note:
There are also cases of duplicacy and there might be rare chances of infinite loop also, say where groups A is present in B and B is present in A.
Hence, if a group is looked up once, it should not be looked again.
My config is like below.
{
"domain_controllers":
["abc.domainname.companyname.com",
"pqr.domainname.companyname.com"],
"name": "domainname",
"user": "*",
"password": "*",
"base_dn": "DC=domainname,DC=companyname,DC=com"
}
My current code is as below. But it is very time consumung.
def get_latest_entry(self, domain_name, data_type, ldap_query):
domain = self.config.get_domain(domain_name)
ldap_attrs = ["memberOf", "whenChanged"]
ldap_entries_for_all_dcs = []
for hostname in domain.domain_controllers:
with "<<ldap_connection>>":
try:
ldap_connection.search(serahc_base=domain.base_dn, search_filter=ldap_query, attributes=ldap_attrs)
except ldap3.core.exceptions.LDAPInvalidFilterError:
return None
if not ldap_connection.entries:
continue
json_entry = <<self.converttojson>>(ldap_connection.entries[0]))
if json_entry["memberOf"]:
json_entry["memberOf"] = [str(memberOf.encode('utf-8').decode('latin-1')) for memberOf in json_entry["memberOf"]]
ldap_entries_for_all_dcs.append(json_entry)
return max(ldap_entries_for_all_dcs,
key=lambda ldap_entry: ldap_entry['whenChanged'])["memberOf"] if ldap_entries_for_all_dcs else None
def get_user_groups(self, sam_account_name, domain_name):
return self.get_latest_entry(domain_name=domain_name, data_type="user", ldap_query="(&(objectCategory=Person)(objectClass=user)(sAMAccountName={}))".format(sam_account_name))
def get_sub_groups(self, distinguished_name, domain_name):
return self.get_latest_entry(domain_name=domain_name, data_type="group", ldap_query="(&(objectCategory=Group)(objectClass=Group)(distinguishedName={}))".format( distinguished_name))
def get_recursive_groups(self, sam_account_name, domain_name):
def nestedgrouplookup(distinguished_name, domain_name):
if distinguished_name not in recursive_groups_list:
recursive_groups_list.append(distinguished_name)
nested_list = self.get_sub_groups(distinguished_name, domain_name)
if nested_list:
for memberOf in nested_list:
nestedgrouplookup(memberOf, domain_name)
recursive_groups_list = []
user_groups_list = self.get_user_groups(sam_account_name, domain_name)
if not user_groups_list:
return []
for distinguished_name in user_groups_list:
nestedgrouplookup(distinguished_name, domain_name)
return recursive_groups_list
For groups within Microsoft Active Directory you can use a single query and no need to query all Domain Controllers.
To get All Groups a User is a member of including Nested Groups use a filter similar to:
Will return all groups that the user is a member of including nested groups.
There are a few limitations but none that most people would ever run into.