Active Directory group lookup function failing

2

Help! I've been trying to write a function that will confirm a user's membership in an Active Directory group, and while it works if the member happens to be in the group, it throws an exception if the user is not.

Here is the function:

private bool IsUserMemberOfGroup(string user, string group)
{
  using (var ctx = new PrincipalContext(ContextType.Domain))
  using (var groupPrincipal = GroupPrincipal.FindByIdentity(ctx, group))
  using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user))
  {
    if (groupPrincipal == null)
    {
      return false;
    }
    else
    {
      return userPrincipal.IsMemberOf(groupPrincipal);
    }
  }
}

And here is the YSOD:

Server Error in '/' Application.

Unknown error (0x80005000)

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details:

System.Runtime.InteropServices.COMException: Unknown error (0x80005000)

Source Error:

 
Line 34:         else
Line 35:         {
Line 36:           return userPrincipal.IsMemberOf(groupPrincipal);
Line 37:         }
Line 38:       }

I don't know if it's related, but when I step through the function, groupPrincipal.Members.Count throws an exception of type "System.NullReferenceException", with Count.Base shows an exception with the message "Object reference not set to instance of an object".

What the heck's going on? Why won't a bool named IsMemberOf just return false when someone's not a member?

Thanks,

Daniel

asp.net
active-directory
asked on Stack Overflow Dec 2, 2009 by Daniel Cotter

3 Answers

4

I think you could simplify things a bit:

private bool IsUserMemberOfGroup(string user, string group)
{
  using (var ctx = new PrincipalContext(ContextType.Domain))
  using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user))
  {
      PrincipalSearchResult<Principal> result = userPrincipal.GetGroups();

      GroupPrincipal groupPrincipal = 
           result.Where(g => g.SamAccountName == groupName).FirstOrDefault();

      return (groupPrincipal != null);
  }
}

The userPrincipal.GetGroups() will give you a definitive list of all group memberships (including primary group and nested group memberships) for that user; then search that list for the group you're interested in, e.g. by samACcountName or some other property.

If you find the group you're looking for in the PrincipalSearchResult<Principal> returned by GetGroups(), then your user is a member of that group.

You can save yourself at least one "FindByIdentity" call with this.

answered on Stack Overflow Dec 2, 2009 by marc_s
1

A minor modification top the code from marc_s, I have:

using (var ctx = new PrincipalContext(ContextType.Domain)) 
using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, user)) 
using (var groupPrincipal = GroupPrincipal.FindByIdentity(ctx, group))
{
     if (userPrincipal == null) return false;
     if (groupPrincipal == null) return false;

     PrincipalSearchResult<Principal> result = userPrincipal.GetGroups(); 
     Principal grp = result.Where(g => g.Sid == groupPrincipal.Sid).FirstOrDefault(); 
     return (grp != null);
}

Comparing Sid seems to work more reliably than comparing SamAccountName.

answered on Stack Overflow Jun 13, 2011 by Rupert Curwen
0

We had a bit of a poison group in our setup which caused this to fail for some users but not others. The "FirstOrDefault" logic in the other suggested answers MIGHT have helped us dodge the poison group, but that is no guarantee.

We have two suggestions for others with this problem. First check if you have any groups with a forward slash in the group name (the actual group name, not the "pre-windows 2000" name which will replace it with an underscore). If you can rename all such groups that might fix your problem ... it worked for us.

This workaround also was working for us:

/// <summary>
/// This does a recursive group search for the given user or computer principal.
/// </summary>
public IEnumerable<Principal> GetGroups(Principal principal)
{
    return GetGroups(null, principal);
}

private IEnumerable<Principal> GetGroups(HashSet<SecurityIdentifier> ancestorPrincipalSids, Principal parentPrincipal)
{
    try
    {
        //enumerate this here so errors are thrown now and not later
        //if the current group name has a forward-slash, I think this 
        //will always error here
        var groups = parentPrincipal.GetGroups().ToArray();
        if (groups == null)
        {
            return Enumerable.Empty<Principal>();
        }

        //keep track of all ancestors in the group hierarchy to this point
        //so that we can handle circular references below
        var newAncestors = new HashSet<SecurityIdentifier>(ancestorPrincipalSids ?? Enumerable.Empty<SecurityIdentifier>());
        newAncestors.Add(parentPrincipal.Sid);

        return groups
            .Concat(groups
                .Where(g => !newAncestors.Contains(g.Sid)) //handle circular references
                .SelectMany(g => GetGroups(newAncestors, g)));
    }
    catch
    {
        return Enumerable.Empty<Principal>();
    }
}
answered on Stack Overflow Mar 30, 2015 by TCC • edited Mar 30, 2015 by TCC

User contributions licensed under CC BY-SA 3.0