1

How do i go about iterating a group to find out if a given user is a member of a group?

I know i can use IsInRole on WindowsPrincipal object but for some reason it don't always work for me, it doesn't error out or throw exception but just return false.

i have put together following code from web, can some help me improve it in terms of reliability, it hasn't gave any wrong result in 3 weeks of testing.

Side notes: 1: I don't have access to AD username and password hence using GC. 2: Groups can be created in any domain but with in same forest. 3: Group can have users from various domains as well as groups.

thanks

KA

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    static extern int CheckTokenMembership(int TokenHandle, byte[] PSID, out bool IsMember);

   [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    static extern bool IsValidSid(byte[] PSID);


private bool Authenticate(XmlNodeList XmlNodeGroups)
    {
        bool result = false;
        try
        {
            Dictionary<string, List<string>> Groups = GetGroups(XmlNodeGroups);
            //search global catalog and get SID of the group
            Byte[] sid = null;
            foreach (string groupName in Groups.Keys)
            {
                using (DirectoryEntry entry = new DirectoryEntry("GC:"))
                {
                    IEnumerator ie = entry.Children.GetEnumerator();
                    ie.MoveNext();
                    using (DirectorySearcher ds = new DirectorySearcher((DirectoryEntry)ie.Current))
                    {
                        ds.Filter = string.Format("(&(|(sAMAccountName={0}))(objectClass=group))", groupName);  
                        using (SearchResultCollection resColl = ds.FindAll())
                        {
                            if (resColl.Count > 0)
                            {
                                ResultPropertyCollection resultPropColl = resColl[0].Properties;
                                sid = (byte[])resultPropColl["objectsid"][0];
                                if (sid == null || !IsValidSid(sid))
                                {
                                    // log message and continue to next group                                        continue;
                                }
                            }
                            else
                            {
                                  // log message and continue to next group                                    continue;
                            }
                        }

                        bool bIsMember = false;
                        if (CheckTokenMembership(0, sid, out bIsMember) == 0)
                        {
                               // log message and initiate fall back....... use Legacy
                            result = CheckMemberOf(XmlNodeGroups, _CurrentIdentity);
                            break;
                        }
                        else
                        {
                            result = bIsMember ? true : false;
                            if (result)
                            {
                                // debug message                                    break;
                            }
                            else
                            {
                               // debug message
                            }
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            // log exception message and initiate fall back....... use Legacy
            result = CheckMemberOf(XmlNodeGroups, _CurrentIdentity);
        }
        return result;
    }</code>
TheOCD
  • 161
  • 2
  • 3
  • 11

2 Answers2

2

Are you on .NET 3.5 ? If so, check out the MSDN magazine article Managing Directory Security Principals in the .NET Framework 3.5. It shows just how much easier things have become when it comes to users and groups in AD.

As for your requirement - you could

  • find the group in question
  • enumerate all its members
  • find if your given user is a member in that group

and all this can be done quite easily with the help of the System.DirectoryServices.AccountManagement namespace:

// establish a context - define a domain (NetBIOS style name),  
// or use the current one, when not specifying a specific domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// find the group in question
GroupPrincipal theGroup = GroupPrincipal.FindByIdentity(ctx, "nameOfGroup");

// recursively enumerate the members of the group; making the search
// recursive will also enumerate the members of any nested groups
PrincipalSearchResult<Principal> result = theGroup.GetMembers(true);

// find the user in the list of group members
UserPrincipal user = (result.FirstOrDefault(p => p.DisplayName == "Some Name") as UserPrincipal);

// if found --> user is member of this group, either directly or recursively
if(user != null)
{
     // do something with the user
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • i am still using .Net v2.0 and there are no plans to move it up to 3.5 either. I think we will going straight to v4.0 in latter half this year. – TheOCD Feb 02 '10 at 08:58
  • Thanks a ton for the reply. That's exactly what I have been looking for. However what I see is that the code is too slow since it is recursive. The code line: // find the user in the list of group members UserPrincipal user = (result.FirstOrDefault(p => p.DisplayName == "Some Name") as UserPrincipal); is pretty slow when trying to parse groups with a lot of users. Any help. – Ashish Dec 20 '10 at 06:08
  • @Ashish: if you don't want it to be recursive, then call `GetMembers(false)` instead. That should speed things up quite a bit – marc_s Dec 20 '10 at 07:30
  • @marc_s: The above mentioned code works well with anonymous authentication and fails with basic+windows authentication and app pool identity set to Network services. Any idea why so? If it is caching, I do not want to do so. – Ashish Dec 21 '10 at 04:01
  • 1
    @Ashish: most likely, the "NetworkService" account just doesn't have any permission to read your AD... – marc_s Dec 21 '10 at 07:04
  • @marc_s: Thanks for pointing towards that direction. Network services was having necessary permissions on the AD. However as the documentation specifies that GetAuthorizationGroups fetches records by enumerating the tokens on local machine. My tokens are getting refreshed each 10 mins which is at the moment the accepted solution. I am still searching for AD issues as to whether AD is caching the tokens or replication is taking time. – Ashish Dec 21 '10 at 11:48
0

I tried to use your code snippet above for the 3.5 framework and this line my compiler says it's incorrect:

    // find the user in the list of group members
    UserPrincipal user = (result.FirstOrDefault(p => p.DisplayName == adUser) as UserPrincipal);

Specifically the result.FirstOfDefault, it says that's not a valid option.

Thanks!