locked
TokenGroups fails!! Help? RRS feed

  • Question

  • User-659351695 posted
    Hi,
    We have a multi group membership AD. Its having a graph structure.I'm using TokenGroups attribute to retrieve Group membership of an entity.
    Suppose Group A contains Group B and Group B contains Two users say X and Y.
    When I try to check that whether X belongs to A using Token Group Algorithm. It succeeeds..But When I check for B..its fails...Both the users are in same domain.
    There is no problem in token groups code...
    Can there be any other reason which can explain such behaviour...like private, public membership, etc.
    I have no idea whats going on..
    Pls help...
    Monday, May 15, 2006 9:31 AM

All replies

  • User1354132231 posted
    How are you checking to see if they are a member?  Tokengroups works from user perspective, not groups, so I am unsure how you are performing this algorithm to check membership.
    Tuesday, May 16, 2006 4:31 PM
  • User-659351695 posted
    Hi Ryan,
    I 've used the code described below. Also I have tried your code which I think is nearly same...
    Actually we are having a big corporate AD with about 60K users and 300 groups. Group depth can be of the order 8 or 9.
    Strange thing is this code works for 80% of the users. ie It tells the user belongs to the group/subgroup...But its fails for 20% users..ie. Is it some problem of primary groups, distribution gropus??

     
     
      #region UserBelongsToGroup
            public bool UserBelongsToGroup(string strUser,string strGroup)
            {
                DirectoryEntry entry = GetDirectoryEntry(strUser,true);
                if ((entry == null))
                {
                    //throw new Exception("Invalid AD User");
                }
                bool isMember = false;
                try
                {
                    //  Get the SID for Group A
                    byte[] objectSid = GetGroupSID(strGroup);
                    if (!(objectSid == null))
                    {
                        //
                        //  now retrieve the tokenGroups property on the user "John Doe"
                        //
                        string[] props={"tokenGroups"};
                        entry.RefreshCache(props);
                        //
                        //  cycle through the tokenGroups and see if one matches the SID of group A
                        //
                        foreach (byte[] byteEntry in entry.Properties["tokenGroups"])
                        {
                            if (CompareByteArrays(byteEntry, objectSid))
                            {
                                isMember = true;
                                break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    //throw new ApplicationException("An error occurred while checking group membership.", ex);
                }
                return isMember;
            }
        
            #region CompareByteArrays
            private bool CompareByteArrays(byte[] data1, byte[] data2)
            {
                try
                {
                    //  If both are null, they're equal
                    if ((data1 == null) && (data2 == null))
                    {
                        return true;
                    }
                    //  If either but not both are null, they're not equal
                    if ((data1 == null) || (data2 == null))
                    {
                        return false;
                    }
                    if (data1.Length != data2.Length)
                    {
                        return false;
                    }
                    for (int index = 0; index < data1.Length ; index++)
                    {
                        if (data1[index] != data2[index])
                        {
                            return false;
                        }
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                    //throw new ApplicationException("An error occurred while comparing byte arrays.", ex);
                }
            }
            #endregion
        
            #region GetGroupSID
            private byte[] GetGroupSID(string strGroup)
            {
                DirectoryEntry entry=null;
                try
                {
                    entry = GetDirectoryEntry(strGroup,false);
                    if (!(entry == null))
                    {
                        //
                        //  retrieve the objectSID for the group
                        //
                        byte[] sid = ((byte[])(entry.Properties["objectSID"].Value));
                        return sid;
                    }
                    else
                    {
                        return null;
                    }
                }
                catch (Exception ex)
                {
                        return null;
                    //throw new ApplicationException("An error occurred while binding to the group in Active Directory.", ex);
                }
                finally
                {
                    if (!(entry == null))
                    {
                        entry.Dispose();
                        entry.Close();
                    }
                }
            }
            #endregion
    
            #region GetDirectoryEntry
            private DirectoryEntry GetDirectoryEntry(string strInput,bool blnIsUser)
            {
                string strRoot="LDAP://DC=ad,DC=company,DC=com";            
                DirectoryEntry objdirentry = new DirectoryEntry(strRoot);
                DirectorySearcher objDS = new DirectorySearcher(objdirentry);
                string strType=(blnIsUser)?"user":"group";
                string strCmp=(blnIsUser)?"mailNickName":"CN";
    
                string strFilter="(&(objectClass="+strType+")("+strCmp+"="+ strInput +"))";
                objDS.SearchScope=SearchScope.Subtree;
                objDS.Filter=strFilter;            
            
                SearchResultCollection colresults = objDS.FindAll() ;
    
                if (colresults.Count>0)
                {                    
                    try
                    {
                        foreach( Object collMemb in colresults[0].Properties["distinguishedName"])
                        {                        
                            DirectoryEntry de= new DirectoryEntry("LDAP://"+collMemb.ToString());
                            return de;
                        }
                    }
                    catch
                    {
                        return null;
                    }
                }
                return null;
            }
            #endregion
    
     
    Tuesday, May 16, 2006 11:45 PM
  • User1354132231 posted
    The 'tokenGroups' attribute only contains security groups (i.e. the ones where the groupType attribute indicates the security-enabled flag).  Distribution groups are not security groups, so they will not be in the attribute.  Primary group will be returned as long as it is not a distribution group either (which would be rare by default).

    One method I have used in the past is DsCrackNames (or IADsNameTranslate).  Essentially, I crack SIDs in the 'tokenGroup' attribute and put them into a hash or GenericPrincipal for the user.  Then I crack the group name into one of the formats to normalize it.  Finally, I just use IPrincipal.IsInRole() with the group name against the collection and get my result.  It is fairly fast, but has same issues as other tokenGroups methods since it has no distribution groups.

    The only way to definitely do this and get distribution groups as well is to use recursion to explode the groups' membership and then see if you user is in it.

    I have a sample in Ch. 11 on how to expand group membership using both 1.1 and 2.0.  You can download this for free (see the first sticky post in this forum).  With some minor modifications you can return the users in a hashtable or some other IDictionary type of object that will allow you to do a fast key lookup on your user.

    You could also try a hybrid technique that would combine techniques - but that is an optimization for later when you determine you need it.
    Wednesday, May 17, 2006 10:21 AM
  • User-659351695 posted
    Hi Ryan,
    Let me explain my problem in more detail. Suppose I have Group A as parent group which contains Group B and Group C as its children. (In my application, outlook mailing lists are groups). Now say group B has 2 users ie. User 1 and User 2 as its children.
    When I run token groups code like this
    UserBelongsToGroup("User1","Group A");

    It returns true, means objectSID of Group A is found in the array of ObjectSiDs
    of tokenGroups property of User1, thats fine.

    But

    UserBelongsToGroup("User2","Group A");

    It returns FALSE, because objectSID of Group A is not found in the array of ObjectSiDs
    of tokenGroups property of User2, whereas in outlook I can see both of them belong
    to the same mailing list ie Group A.

    Obviously, the problem is not with the Groups coz GroupA worked for one of the Users
    ie User1.
    The problem is with the association of User To Group...
    Why is it that one user works, but not the other..
    What can be factor that debars objectSID of Group A from appearing into tokenGroups
    of User2.

    Thanks in advance.
    Manpreet

    Thursday, May 18, 2006 4:39 AM
  • User1354132231 posted
    Are the two users in the same domain?
    Friday, May 19, 2006 1:11 PM
  • User-659351695 posted
    Ofcourse they belong to same domain ie DC=ad,DC=company=DC=com. Not only domain, even they belong to same group within the domain. But one of the users works, but other doesn't..Sometimes I think Active Directory is a very mysterious thing...[:P]
    Monday, May 22, 2006 12:01 AM
  • User1354132231 posted
    Sorry, lost track of this message until a similar one came up today.  Does this method help?

    public static bool IsMember(DirectoryEntry group, DirectoryEntry user)
    {
        SecurityIdentifier groupSid = new SecurityIdentifier((byte[])group.Properties["objectSid"].Value, 0);

        user.RefreshCache(new string[] { "tokenGroups" });
        foreach (byte[] sidBytes in user.Properties["tokenGroups"])
        {
            if (new SecurityIdentifier(sidBytes, 0).Equals(groupSid))
                return true;
           
        }
        return false;
    }



    Wednesday, June 7, 2006 2:44 PM
  • User-659351695 posted

    Hi Ryan,

    Unfortunately im using Framework 1.1, and SecurityIdentifier belongs to 2.0. Will try to install new framework and let you know the status.

    Thanks.

    Thursday, June 8, 2006 12:13 AM