3

I'm trying to list all the groups that a given user belongs to, and I want to support both local machine accounts and domain accounts. I can get the groups for domain accounts with the following code:

public void ListAllGroupsDomain()
{
    ListAllGroups(ContextType.Domain,
                  "myDomain",
                  "myDomainUser",
                  "myDomainPassword");
}

public void ListAllGroups(ContextType contextType
                          string name,
                          string username,
                          string password)
{
    using (var context = new PrincipalContext(contextType,
                                              name,
                                              username,
                                              password))
    {
        using (var findByIdentity = UserPrincipal.FindByIdentity(context, "testedit"))
        {
            if (findByIdentity != null)
            {
                var groups = findByIdentity.GetGroups(context);
                var results = groups.Select(g => g.Name).ToArray();
                Console.WriteLine("Listing {0} groups", results.Count());
                foreach (var name in results)
                {
                    Console.WriteLine("{0}", name);
                }
            }
        }
    }
}

However, if I try to call for an account local to the machine, I get a COM exception saying that my username or password is incorrect (which they aren't):

public void ListAllGroupsMachine()
{
    ListAllGroups(ContextType.Machine,
                  "myMachine",
                  "myMachineUser",
                  "myMachinePassword");
}

I'm guessing that I'm using the FindByIdentity incorrectly for machine accounts. So I tried a slightly different approach that doesn't use FindByIdentity:

public void ListAllGroups2(ContextType contextType
                          string name,
                          string username,
                          string password)
{
    using (var context = new PrincipalContext(contextType,
                                              name,
                                              username,
                                              password))
    {
        using (var userContext = new UserPrincipal(context))
        {
            var results = userContext.GetGroups();
            Console.WriteLine("Listing {0} groups:", results.Count());
            foreach (var principal in results)
            {
                Console.WriteLine("{0}", principal.Name);
            }
        }
    }
}

This version didn't throw any exceptions, but the call to GetGroups() also didn't return any results. How can I get the groups a particular user belongs to that is reliable for both machine and domain accounts?

Gillfish
  • 717
  • 1
  • 9
  • 29

1 Answers1

6

Thanks to a little help I gleaned from https://stackoverflow.com/a/3681442/1222122, I figured out what I was doing wrong. I was setting up the PrincipalContext incorrectly. I didn't need to give it the domain, username, or password, just the ContextType. The following code works for both local machine accounts and domain accounts:

public void ListAllGroupsDomain()
{
    ListAllGroups(ContextType.Domain, "myDomainUsername");
}

public void ListAllGroupsMachine()
{
    ListAllGroups(ContextType.Machine, "myMachineUsername");
}

public void ListAllGroups(ContextType contextType, string userName)
{
    using (var context = new PrincipalContext(contextType))
    {
        using (var findByIdentity = UserPrincipal.FindByIdentity(context, userName))
        {
            if (findByIdentity != null)
            {
                var groups = findByIdentity.GetGroups(context);
                var results = groups.Select(g => g.Name).ToArray();
                Console.WriteLine("Listing {0} groups", results.Count());
                foreach (var name in results)
                {
                    Console.WriteLine("{0}", name);
                }
            }
        }
    }
}

The only thing I needed to be sure to do was to strip out the domain from the username before I passed it to ListAllGroups because the function that was providing it to me would prepend it to the username in some cases.

Community
  • 1
  • 1
Gillfish
  • 717
  • 1
  • 9
  • 29
  • This is exactly what I'm looking for atm, but I'm getting really slow access to the .FindByIdentity and .GetGroups calls. Did you experience performance issues with this? – Spikeh Jul 07 '15 at 16:04
  • How slow is slow for you, Spikeh? I tried several different methods when I was working on this, and I seem to remember some being nearly instantaneous, and some taking 10+ seconds. It's been so long, though, that I couldn't tell you much more than that. – Gillfish Jul 07 '15 at 22:54
  • FindByIdentity takes about 4 seconds (using ContextType.Machine) and GetGroups takes about 6 seconds. I'm not on a domain, though - Windows 8.1, running through IISExpress. Time for some caching, methinks. – Spikeh Jul 08 '15 at 07:41