views:

2640

answers:

2

I'm possibly just stupid, but I'm trying to find a user in Active Directory from C#, using the Login name ("domain\user").

My "Skeleton" AD Search Functionality looks like this usually:

de = new DirectoryEntry(string.Format("LDAP://{0}", ADSearchBase), null, null, AuthenticationTypes.Secure);
ds = new DirectorySearcher(de);
ds.SearchScope = SearchScope.Subtree;
ds.PropertiesToLoad.Add("directReports");
ds.PageSize = 10;
ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
SearchResult sr = ds.FindOne();

Now, that works if I have the full DN of the user (ADSearchBase usually points to the "Our Users" OU in Active Directory), but I simply have no idea how to look for a user based on the "domain\user" syntax.

Any Pointers?

+6  A: 

You need to set a filter (DirectorySearcher.Filter) something like:

"(&(objectCategory=person)(objectClass=user)(sAMAccountName={0}))"

Note that you only specify the username (without the domain) for the property sAMAccountName. To search for domain\user, first locate the naming context for the required domain, then search there for sAMAccountName.

By the way, when building LDAP query strings using String.Format, you should generally be careful to escape any special characters. It probably isn't necessary for an account name, but could be if you're searching by other properties such as the user's first (givenName property) or last (sn property) name. I have a utility method EscapeFilterLiteral to do this: you build your string like this:

String.Format("(&(objectCategory=person)(objectClass=user)(sn={0}))", 
              EscapeFilterLiteral(lastName, false));

where EscapeFilterLiteral is implemented as follows:

public static string EscapeFilterLiteral(string literal, bool escapeWildcards)
{
    if (literal == null) throw new ArgumentNullException("literal");

    literal = literal.Replace("\\", "\\5c");
    literal = literal.Replace("(", "\\28");
    literal = literal.Replace(")", "\\29");
    literal = literal.Replace("\0", "\\00");
    literal = literal.Replace("/", "\\2f");
    if (escapeWildcards) literal = literal.Replace("*", "\\2a");
    return literal;
}

This implementation allows you treat the * character as part of the literal (escapeWildcard = true) or as a wildcard character (escapeWildcard = false).

UPDATE: This is nothing to do with your question, but the example you posted does not call Dispose on the disposable objects it uses. Like all disposable objects these objects (DirectoryEntry, DirectorySearcher, SearchResultCollection) should always be disposed, normally with the using statement. See this post for more info.

Joe
A: 

Thanks. I figured that i can get the Domain (at least in my AD) through specifying "LDAP://{0}.somedomain.com/DC={0},DC=somedomain,DC=com", replacing {0} with the domain, which works in our my environment at least.

One question though: sAMAccountName seems depreciated: The logon name used to support clients and servers running older versions of the operating system, such as Windows NT 4.0, Windows 95, Windows 98, and LAN Manager. This attribute must be less than 20 characters to support older clients.

Is this still the best approach to it? Or is there a more "modern" field to query? (Windows 2003 Active Directory, Windows XP or 2003 Clients, .net 3.0)

Edit: Thanks again. Our structure is a bit complicated: we have a big "domain.com" forest, with several domains for regional offices. Essentially: The Login is "something\username", the full domain us something.domain.com and the mail is [email protected] (without the something), but the principal name is [email protected]. I will manually "translate" something\username to [email protected], as this seems to be the most robust way. Especially since I want to keep the Auto-Discovery feature in.

Michael Stum
I think a more modern way is to use the userPrincipalName property - which is in RFC822 format (usually the user's email address). But I always use sAMAccountName.
Joe