By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,369 Members | 1,197 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,369 IT Pros & Developers. It's quick & easy.

Get list of the Groups a User belongs to

P: n/a
How can I get a list of the Groups both Local and Domain groups a User
belongs to.
Dec 9 '06 #1
Share this Question
Share on Google+
10 Replies


P: n/a
Hi,

On a Windows Server 2003 domain you can construct a WindowsIdentity by
passing in the user's name in the form, name@domain:

"WindowsIdentity(String) Constructor"
http://msdn2.microsoft.com/en-us/library/td3046fc.aspx

Then you can access the Groups property (2.0 framework only):

"WindowsIdentity.Groups Property"
http://msdn2.microsoft.com/en-us/lib...ty.groups.aspx

If you can't use the above solution since you are using a different version
of the framework or a different domain then I think you'll have to resort to
the unmanaged APIs such as LogonUser:

"LogonUser"
http://msdn2.microsoft.com/en-us/library/aa378184.aspx

The above will get you the User's token, which you can pass to the following
function:

"GetTokenInformation"
http://msdn2.microsoft.com/en-us/library/aa446671.aspx

Specify the value of TOKEN_GROUPS for the TokenInformationClass argument.

(Note that I haven't used these APIs myself)

You'll have to use P/Invoke for this, of course :)

--
Dave Sexton

"Jeff Williams" <je***********@hardsoft.com.auwrote in message
news:12*************@corp.supernews.com...
How can I get a list of the Groups both Local and Domain groups a User
belongs to.

Dec 9 '06 #2

P: n/a
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:uT*************@TK2MSFTNGP02.phx.gbl...
You'll have to use P/Invoke for this, of course :)
System.DirectoryServices will do all of this, and much more, for you without
recourse to p/invoke...

using System;
using System.Collections.Generic;
using System.DirectoryServices;

public static List<stringGetGroupsForUser(string pstrUser)
{
/// <summary>
/// Gets the groups a user is a member of
/// </summary>
/// <param name="pstrGroup">ActiveDirectory group to evaluate</param>
/// <returns>List<stringof groups for pstrUser</returns>

DirectorySearcher objDS = null;
SearchResult objSR = null;
DirectoryEntry objUser = null;
List<stringlstGroups = new List<string>();

try
{
objDS = new DirectorySearcher("objectCategory=User");
objDS.Filter = "(SAMAccountName=" + pstrUser + ")";
objSR = objDS.FindOne();
objUser = new DirectoryEntry(objSR.Path);

PropertyCollection colProperties = objUser.Properties;
PropertyValueCollection colPropertyValues = colProperties["memberOf"];
foreach (string strGroup in colPropertyValues)
{
lstGroups.Add(GetSAMAccountName(strGroup).ToLower( ));
}
return lstGroups;
}
catch (Exception)
{
throw;
}
finally
{
if (objUser != null)
{
objUser.Close();
objUser.Dispose();
objUser = null;
}
if (objSR != null)
{
objSR = null;
}
if (objDS != null)
{
objDS.Dispose();
objDS = null;
}
}
}

public static string GetSAMAccountName(string pstrPath)
{
/// <summary>
/// Gets a SAM Account Name from a given LDAP path
/// </summary>
/// <param name="pstrPath">LDAP path to bind to</param>

DirectoryEntry objADEntry = null;

try
{
objADEntry = new DirectoryEntry("LDAP://" + pstrPath);
return objADEntry.Properties["SAMAccountName"].Value.ToString();
}
catch (System.Runtime.InteropServices.COMException)
{
return String.Empty;
}
catch (System.NullReferenceException)
{
return String.Empty;
}
catch (Exception)
{
throw;
}
finally
{
if (objADEntry != null)
{
objADEntry.Close();
objADEntry.Dispose();
objADEntry = null;
}
}
}
Dec 9 '06 #3

P: n/a
"Mark Rae" <ma**@markNOSPAMrae.comwrote in message
news:OH**************@TK2MSFTNGP04.phx.gbl...
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:uT*************@TK2MSFTNGP02.phx.gbl...
>You'll have to use P/Invoke for this, of course :)

System.DirectoryServices will do all of this, and much more, for you without recourse to
p/invoke...

using System;
using System.Collections.Generic;
using System.DirectoryServices;

public static List<stringGetGroupsForUser(string pstrUser)
{
/// <summary>
/// Gets the groups a user is a member of
/// </summary>
/// <param name="pstrGroup">ActiveDirectory group to evaluate</param>
/// <returns>List<stringof groups for pstrUser</returns>

DirectorySearcher objDS = null;
SearchResult objSR = null;
DirectoryEntry objUser = null;
List<stringlstGroups = new List<string>();

try
{
objDS = new DirectorySearcher("objectCategory=User");
objDS.Filter = "(SAMAccountName=" + pstrUser + ")";
objSR = objDS.FindOne();
objUser = new DirectoryEntry(objSR.Path);

PropertyCollection colProperties = objUser.Properties;
PropertyValueCollection colPropertyValues = colProperties["memberOf"];
foreach (string strGroup in colPropertyValues)
{
lstGroups.Add(GetSAMAccountName(strGroup).ToLower( ));
}
return lstGroups;
}
catch (Exception)
{
throw;
}
finally
{
if (objUser != null)
{
objUser.Close();
objUser.Dispose();
objUser = null;
}
if (objSR != null)
{
objSR = null;
}
if (objDS != null)
{
objDS.Dispose();
objDS = null;
}
}
}

public static string GetSAMAccountName(string pstrPath)
{
/// <summary>
/// Gets a SAM Account Name from a given LDAP path
/// </summary>
/// <param name="pstrPath">LDAP path to bind to</param>

DirectoryEntry objADEntry = null;

try
{
objADEntry = new DirectoryEntry("LDAP://" + pstrPath);
return objADEntry.Properties["SAMAccountName"].Value.ToString();
}
catch (System.Runtime.InteropServices.COMException)
{
return String.Empty;
}
catch (System.NullReferenceException)
{
return String.Empty;
}
catch (Exception)
{
throw;
}
finally
{
if (objADEntry != null)
{
objADEntry.Close();
objADEntry.Dispose();
objADEntry = null;
}
}
}
Just a few remarks:
You may simplify your code and make it easier to read and maintain by applying the *using*
idiom, this way, you get rid of the Dispose, Close and completely redundant "obj = null"
calls.
Your code will only work when the caller is running in his domain account, when this is not
the case, you need to bind explicitly against the Domain or the DC, and preferably using
FastBind for performance reasons. You may also bind to the GC (the Global Catalog) using
GC://... in order to speed-up the queries.
Another point to consider is that the binding user must have "query" privileges to all of
the objects you query, normally all domain member do have this privilege, but highly secured
AD's may restrict access to some objects to special accounts only. So it's possible that a
user can bind to his user object, but not to (some) of the related objects.
You should also try to reuse the already established DirectoryEntry object for further
operations against the AD, the way you do forces adsi to rebind and this can be a costly
operation especially on slow connections and uses a lot more resources at the LDAP server.
The following code snip shows how to take advantage of a single bind by using the
GetDirectorEntry() for each successive object retrieval.

public static List<stringGetGroupsForUser(string userAccount)
{
string rootPath = "LDAP://{0}/DC=xxx,DC=yyy,DC=zzz";
string accountDomain = "domain"; // domain name or dc name or empty when binding to
logon domain
string userAccount = userAccount
rootPath = String.Format(
rootPath

, accountDomain);
string authUser = "xxx\yyyyy"; // account used to bind, here hardcoded, not
production safe!
string authPassword = "PASSWORD"; // his password, here hardcoded, not production
safe!

List<stringlstGroups = new List<string>();
using (DirectoryEntry root = new DirectoryEntry(rootPath, authUser, authPassword,
AuthenticationTypes.FastBind))
{
using (DirectorySearcher ds = new DirectorySearcher(root))
{
SearchResult sr = null;
ds.Filter = "(SAMAccountName=" + userAccount + ")";
sr = ds.FindOne();
using (DirectoryEntry user = sr.GetDirectoryEntry())
{
PropertyCollection pcoll = user.Properties;
PropertyValueCollection memberOf = pcoll["memberOf"];
foreach (string cnGroup in memberOf)
{
ds.Filter = cnGroup.Substring(0, cnGroup.IndexOf(','));
sr = ds.FindOne();
using (DirectoryEntry group = sr.GetDirectoryEntry())
{
lstGroups.Add(group.Properties["SAMAccountName"].Value.ToString());
}
}
}
}
}
return lstGroups;
}
....

Willy.

Dec 9 '06 #4

P: n/a
"Willy Denoyette [MVP]" <wi*************@telenet.bewrote in message
news:ui**************@TK2MSFTNGP04.phx.gbl...
You may simplify your code... <snip>
It's not actually my code - I just found it on the web with a simple Google
search...
Dec 9 '06 #5

P: n/a
Hi Mark,

Good stuff, but correct me if I'm wrong here. That won't get the local
groups so the user will still have to use my method anyway.

--
Dave Sexton

"Mark Rae" <ma**@markNOSPAMrae.comwrote in message
news:OH**************@TK2MSFTNGP04.phx.gbl...
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:uT*************@TK2MSFTNGP02.phx.gbl...
>You'll have to use P/Invoke for this, of course :)

System.DirectoryServices will do all of this, and much more, for you
without recourse to p/invoke...

using System;
using System.Collections.Generic;
using System.DirectoryServices;

public static List<stringGetGroupsForUser(string pstrUser)
{
/// <summary>
/// Gets the groups a user is a member of
/// </summary>
/// <param name="pstrGroup">ActiveDirectory group to evaluate</param>
/// <returns>List<stringof groups for pstrUser</returns>

DirectorySearcher objDS = null;
SearchResult objSR = null;
DirectoryEntry objUser = null;
List<stringlstGroups = new List<string>();

try
{
objDS = new DirectorySearcher("objectCategory=User");
objDS.Filter = "(SAMAccountName=" + pstrUser + ")";
objSR = objDS.FindOne();
objUser = new DirectoryEntry(objSR.Path);

PropertyCollection colProperties = objUser.Properties;
PropertyValueCollection colPropertyValues = colProperties["memberOf"];
foreach (string strGroup in colPropertyValues)
{
lstGroups.Add(GetSAMAccountName(strGroup).ToLower( ));
}
return lstGroups;
}
catch (Exception)
{
throw;
}
finally
{
if (objUser != null)
{
objUser.Close();
objUser.Dispose();
objUser = null;
}
if (objSR != null)
{
objSR = null;
}
if (objDS != null)
{
objDS.Dispose();
objDS = null;
}
}
}

public static string GetSAMAccountName(string pstrPath)
{
/// <summary>
/// Gets a SAM Account Name from a given LDAP path
/// </summary>
/// <param name="pstrPath">LDAP path to bind to</param>

DirectoryEntry objADEntry = null;

try
{
objADEntry = new DirectoryEntry("LDAP://" + pstrPath);
return objADEntry.Properties["SAMAccountName"].Value.ToString();
}
catch (System.Runtime.InteropServices.COMException)
{
return String.Empty;
}
catch (System.NullReferenceException)
{
return String.Empty;
}
catch (Exception)
{
throw;
}
finally
{
if (objADEntry != null)
{
objADEntry.Close();
objADEntry.Dispose();
objADEntry = null;
}
}
}

Dec 9 '06 #6

P: n/a
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:ui**************@TK2MSFTNGP04.phx.gbl...
Good stuff, but correct me if I'm wrong here. That won't get the local
groups so the user will still have to use my method anyway.
No it won't, but it can with a fairly trivial modification:
http://www.experts-exchange.com/Prog..._20658471.html
Dec 9 '06 #7

P: n/a
Hi Mark,

That's great :)

--
Dave Sexton

"Mark Rae" <ma**@markNOSPAMrae.comwrote in message
news:uG**************@TK2MSFTNGP06.phx.gbl...
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:ui**************@TK2MSFTNGP04.phx.gbl...
>Good stuff, but correct me if I'm wrong here. That won't get the local
groups so the user will still have to use my method anyway.

No it won't, but it can with a fairly trivial modification:
http://www.experts-exchange.com/Prog..._20658471.html

Dec 9 '06 #8

P: n/a
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:eR**************@TK2MSFTNGP02.phx.gbl...
That's great :)
By the way, if you do intend to use the code I found, be sure to follow
Willy's recommended modifications...
Dec 9 '06 #9

P: n/a
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:ui**************@TK2MSFTNGP04.phx.gbl...
Hi Mark,

Good stuff, but correct me if I'm wrong here. That won't get the local groups so the user
will still have to use my method anyway.

--

No, you can use the WinNT provider to connect to the local SAM, like this:

private static List<stringAccountGroups(string userAccount)
{
List<stringlstGroups = new List<string>(10);
string adsPath = "WinNT://<domain>/<machine>"; // or WinNT://<machine if not a
domain member.
using (DirectoryEntry groupEntry = new DirectoryEntry(adsPath +",computer"))
{
IADsContainer cont = groupEntry.NativeObject as IADsContainer;
object[] filter = {"Group"};
cont.Filter = filter;
foreach (IADsGroup group in cont) {
if(group.IsMember(adsPath + "/" + userAccount))
lstGroups.Add(group.Name);
}
}
return lstGroups;
}

the problem here is that you need to set a reference to activeds.tlb. Also note that the
above sample does not account for nested groups!!
Note that you can also use WindowsIdentity.Groups, the problem here is that you also get the
pseudo accounts returned.

A better solution is to use System.Management (and WMI classes "Win32_UserAccount" and
"Win32_Group") to check local account and group membership.
Willy.

Dec 9 '06 #10

P: n/a
Hi Willy,

Thanks for the info.

--
Dave Sexton

"Willy Denoyette [MVP]" <wi*************@telenet.bewrote in message
news:uk**************@TK2MSFTNGP05.phx.gbl...
"Dave Sexton" <dave@jwa[remove.this]online.comwrote in message
news:ui**************@TK2MSFTNGP04.phx.gbl...
>Hi Mark,

Good stuff, but correct me if I'm wrong here. That won't get the local
groups so the user will still have to use my method anyway.

--


No, you can use the WinNT provider to connect to the local SAM, like this:

private static List<stringAccountGroups(string userAccount)
{
List<stringlstGroups = new List<string>(10);
string adsPath = "WinNT://<domain>/<machine>"; // or
WinNT://<machine if not a domain member.
using (DirectoryEntry groupEntry = new DirectoryEntry(adsPath
+",computer"))
{
IADsContainer cont = groupEntry.NativeObject as IADsContainer;
object[] filter = {"Group"};
cont.Filter = filter;
foreach (IADsGroup group in cont) {
if(group.IsMember(adsPath + "/" + userAccount))
lstGroups.Add(group.Name);
}
}
return lstGroups;
}

the problem here is that you need to set a reference to activeds.tlb. Also
note that the above sample does not account for nested groups!!
Note that you can also use WindowsIdentity.Groups, the problem here is
that you also get the pseudo accounts returned.

A better solution is to use System.Management (and WMI classes
"Win32_UserAccount" and "Win32_Group") to check local account and group
membership.
Willy.

Dec 9 '06 #11

This discussion thread is closed

Replies have been disabled for this discussion.