views:

2406

answers:

4

Does anybody know how to make WindowsPrincipal.IsInRole("domain\role") work with active directory universal groups?

Let's say the current user is a member of a group called Role in a domain called domain, and that the Role group is a Global group in active directory. The following code would then yield result = true:

        WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
        bool result = wp.IsInRole(@"domain\Role");

But if the Role group is changed to a universal group the code yields result = false.

+3  A: 

I found no good answer to my question, what I had to do was to write a new Principal class that scanned the directory for all groups that the user belongs to, and recursivly scan all those groups to solve group-in-group memberships. Code provided for users with the same problem. It's not the prittiest code I've written, but atleast it works.

Use like this:

var wp = new WindowsPrincipalEx(WindowsIdentity.GetCurrent());
result = wp.IsInRole(@"domain\role");



public class WindowsPrincipalEx : IPrincipal
{
    // Dictionary to store all groups, key = uppercase groupname, value = groupname as entered in AD
    private Dictionary<string,string> completeGroupList = new Dictionary<string,string>();
    // Private vars
    private WindowsIdentity identity;
    private string domain;

    // Identity property
    public IIdentity Identity
    { 
        get { return identity; }
    }

    // Constructor, accepts identity
    public WindowsPrincipalEx(IIdentity identity)
    {
        this.identity = (WindowsIdentity)identity;
        // Find domain name and store it for filtering purposes
        if (identity.Name.Contains('\\'))
            this.domain = identity.Name.Substring(0, identity.Name.IndexOf('\\') + 1);

        // Find all groups this user belongs to, and store the list for later use
        getRoles(completeGroupList);
    }

    public bool IsInRole(string role)
    {
        // Remove domain
        if (role.StartsWith(domain, StringComparison.CurrentCultureIgnoreCase))
            role = role.Substring(domain.Length);
        return completeGroupList.ContainsKey(role.ToUpper());
    }

    private void getRoles(Dictionary<string,string> groupList)
    {
        // Find username and remove domain
        string name = Identity.Name.Replace(domain,"");

        // Find user in AD
        DirectorySearcher search = new DirectorySearcher("(&(sAMAccountName="+name+")(objectCategory=user))");
        search.PropertiesToLoad.Add("memberof");

        SearchResult result = search.FindOne();
        if (result != null)
        {
            // Add all groups to the groupList dictionary
            foreach (string s in result.Properties["memberOf"])
            {
                string[] elements = s.Split(new char[] { ',' });
                foreach (string e in elements)
                    if (e.StartsWith("CN=", StringComparison.CurrentCultureIgnoreCase))
                    {
                        if (!groupList.ContainsKey(e.Substring(3).ToUpper()))
                            groupList.Add(e.Substring(3).ToUpper(),e.Substring(3));
                        break;
                    }
            }
        }

        // Scan through all groups found, and find group on group memberships recursevly
        foreach (var ng in groupList.ToArray())
            getRolesInRoles(groupList, ng.Key);
    }

    private void getRolesInRoles(Dictionary<string, string> groupList, string roleName)
    {
        string name = roleName.Replace(domain, "");

        // Find group in AD
        DirectorySearcher search = new DirectorySearcher("(&(cn="+name+")(objectCategory=group))");
        search.PropertiesToLoad.Add("memberof");

        SearchResult result = search.FindOne();
        if (result != null)
        {
            // Add all groups to the groupList dictionary
            foreach (string s in result.Properties["memberOf"])
            {
                string[] elements = s.Split(new char[] { ',' });
                foreach (string e in elements)
                    if (e.StartsWith("CN=", StringComparison.CurrentCultureIgnoreCase))
                    {
                        if (!groupList.ContainsKey(e.Substring(3).ToUpper()))
                        {
                            groupList.Add(e.Substring(3).ToUpper(),e.Substring(3));
                            getRolesInRoles(groupList, e.Substring(3));
                        }
                        break;
                    }
            }
        }
    }
}
sindre j
A: 

Isn't it too slow?

I'm running this through a VPN connection and a regular 3 mbit~ish internet connection, and it definitely is a lot slower than the regular WindowsPrincipal.IsInRole. But atleast it works, and since we have to use universal groups I really don't have another option.It's important to save the Principal class in some kind of global object so that the code recusing through the ad-groups is called only once.
sindre j
However, on a LAN the delay is not noticable on our network atleast.
sindre j
A: 

ok am I the only one that wants to know how you got ToArray() to work on a Dictionary?

Hal Diggs
This is not an answer and should thus be posted as a comment instead. However, ToArray() on a dictionary just gives an array of KeyValue pairs. Nothing to it.
sindre j
oops, sorry I thought I hit comment. hmm looks like I don't have enough points to comment... heh heh. sucks being a StackOverFlow noob. :-)
Hal Diggs
A: 

i guess he used Framework 3.5. I copied to code in a Framework 3.5 Winform App, it was working pretty well, then changed to 2.0 and it won't accept .ToArray() since then .. Christian

Yes, this was run in FW 3.5
sindre j