Finding Active Directory user’s group membership in C#

Hi there,

there are a couple of ways to programmatically find a user and groups he belongs to in Active Directory. Recently I tested a few of them and here are some thoughts of what I found out.

DirectorySearcher

The System.DirectoryServices namespace provides us with a DirectorySearcher class. Filter property of that class can be used in order to specify the search query on the entire directory. An example filter for a user with login name ‘lkarolak’ could look like this:

(&(objectClass=user)(SAMAccountName=lkarolak))

If search is successful, the FindOne() method of the DirectorySearcher class should return an object of type DirectoryEntry. Finding this object’s membership requires iterating through its properties, finding the ones with name ‘memberOf’, and then (if needed) also performing some recursion in order to find out the nested group membership. After all, a bit complicated and quite resource-costly.

SearchRequest

Similar approach could be to use the SearchRequest and SearchResponse objects (this time from System.DirectoryServices.Protocols namespace), which are executed within a LDAP connection. The filter for the query looks just as in the previous example. Also in this case one has to recursively search within the result class’ (SearchResultEntry) attributes in order to get all the user’s nested groups.

Here’s a small example:

// establish a connection to LDAP
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain, port);
LdapConnection _connection = new LdapConnection(id);
_connection.SessionOptions.SecureSocketLayer = secureConnection;
_connection.AuthType = AuthType.Basic;
_connection.Credential = new NetworkCredential(ldapUser, ldapPassword);
_connection.Bind();

// distinguished name of the object 
// at which to start the search.
String _target = "dc=EXAMPLE,dc=COM";

String filter = "(&(objectCategory=person)(SAMAccountName=lkarolak))";
String[] attributesToReturn = { "SAMAccountName", "memberOf", "cn" };

SearchRequest searchRequest = new SearchRequest(_target, filter,
      SearchScope.Subtree, attributesToReturn);
SearchResponse response =
      (SearchResponse)_connection.SendRequest(searchRequest);

if (response.Entries.Count == 1)
{
  SearchResultEntry entry = response.Entries[0];
  for (int index = 0; index < entry.Attributes["memberOf"].Count; index++)
  {
      // get the group name, for example:
      String groupName = entry.Attributes["memberOf"][index].ToString();
  }
}

GroupPrincipal

The most interesting and straightforward solution to me was, however, another approach. Within the namespace System.DirectoryServices.AccountManagement we can find an easy way to find a user in AD and check his group memberships. Without having to recursively loop over the parent groups, we’re able to fetch the groups of the user, an much more data. The only constraint is that the code has to be run on a machine that is located within the domain. Let’s have a look at this sample code:

// "company" is the domain we would like to search in
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "COMPANY");

// get the user of that domain by his username, within the context
UserPrincipal up = UserPrincipal.FindByIdentity(pc, username);

// fetch the group list
PrincipalSearchResult groups = up.GetAuthorizationGroups();
GroupPrincipal[] filteredGroups = (from p in groups
           where p.ContextType == ContextType.Domain
           && p.Guid != null
           && p is GroupPrincipal
           && ((GroupPrincipal)p).GroupScope == GroupScope.Universal
           select p as GroupPrincipal).ToArray();

The last lines actually do the trick. The GetAuthorizationGroups() method would fetch all the security groups of the user. If we would also like to have the distribution groups of the user, we’d have to use the GetGroups() method instead. Of course one could want to filter out some groups, like „Everyone” etc., maybe with help of a LINQ query like here, or in another way.
Anyway, the GroupPrincipal object returned contains the properties we need in order to get the name of the groups of a user (first of all, the DistinguishedName property).

After some unit tests done, it seems also that this last method is the fastest approach of those three mentioned here, probably also due to lack of recursive functions.

Hope this helps,
Łukasz