7

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?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Michael Stum
  • 177,530
  • 117
  • 400
  • 535

3 Answers3

9

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.

Community
  • 1
  • 1
Joe
  • 122,218
  • 32
  • 205
  • 338
  • How do you *first locate the naming context for the required domain, then search there*? – Sam Oct 15 '13 at 04:03
0

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 user@domain.com (without the something), but the principal name is user@something.domain.com. I will manually "translate" something\username to username@something.domain.com, as this seems to be the most robust way. Especially since I want to keep the Auto-Discovery feature in.

Michael Stum
  • 177,530
  • 117
  • 400
  • 535
  • 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 Oct 02 '08 at 16:47
-1

Logon Name(Pre-Windows 2000)

"(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(sAMAccountName=John))"

Logon Name(Windows 2000 and above)

"(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(userPrincipalName=John))"
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Kevin M
  • 5,436
  • 4
  • 44
  • 46