Re: Get list of the Groups a User belongs to

Tech-Archive recommends: Fix windows errors by optimizing your registry



"Mark Rae" <mark@xxxxxxxxxxxxxxxxx> wrote in message news:OHJnUd5GHHA.1276@xxxxxxxxxxxxxxxxxxxxxxx
"Dave Sexton" <dave@jwa[remove.this]online.com> wrote in message news:uTLecD5GHHA.420@xxxxxxxxxxxxxxxxxxxxxxx

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<string> GetGroupsForUser(string pstrUser)
{
/// <summary>
/// Gets the groups a user is a member of
/// </summary>
/// <param name="pstrGroup">ActiveDirectory group to evaluate</param>
/// <returns>List<string> of groups for pstrUser</returns>

DirectorySearcher objDS = null;
SearchResult objSR = null;
DirectoryEntry objUser = null;
List<string> lstGroups = 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<string> GetGroupsForUser(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<string> lstGroups = 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.

.



Relevant Pages

  • Problem beim automatisierten erstellen einer Website
    ... Public Shared Function CreateWebSite(ByVal webSiteName As String, ... ' Find unused ID value for new web site ... Dim e As DirectoryEntry ...
    (microsoft.public.de.german.entwickler.dotnet.vb)
  • Problem beim automatisiertn erstellen einer Website
    ... Public Shared Function CreateWebSite(ByVal webSiteName As String, ... ' Find unused ID value for new web site ... Dim e As DirectoryEntry ...
    (microsoft.public.de.inetserver.iis)
  • Re: ADFS Development Issues
    ... Public Function IsUserAuthenticated(ByVal strAdPath As String, ... On Apr 22, 5:23 am, "Joe Kaplan" ... You can use that to build a directoryentry to use as the search root. ... user's credentials. ...
    (microsoft.public.windows.server.active_directory)
  • Re: FTP maximum password attempts
    ... I did the IPSec stuff last week due to a new string of attacks... ... public void GetBlock ... foreach (DirectoryEntry IIS in root.Children) ... null, IPSecurity, null); ...
    (microsoft.public.inetserver.iis.security)
  • DirectoryServices Add/Remove Manager to Direct Report
    ... public void AddDirectReport(string ManagerAlias, string UserAlias) ... DirectoryEntry deDirectReport = GetUser; ... this.SetProperty(deDirectReport, "manager", this.GetProperty(deManager, ... code is running fine with this account on my staging environment. ...
    (microsoft.public.dotnet.languages.csharp)