You can do this a lot more simply if you skip searching a particular OU and use an LDAP filter instead. A new DirectoryEntry with no arguments to the constructor is equivalent to DC=dev,DC=local authenticated with the current Windows user to the current
domain. This also has the benefit of generating a lot less traffic by having the AD do the search for you and return only the matching result.
string emailAddress = null;
using (DirectoryEntry searchRoot = new DirectoryEntry())
using (DirectorySearcher userSearcher = new DirectorySearcher(searchRoot, @"(&(objectCategory=Person)(objectClass=user)(mail=*)(sAMAccountName=username))", new string[] { "cn", "sAMAccountName", "displayName", "givenName", "sn", "mail" }))
{
SearchResult user = userSearcher.FindOne();
if (user != null)
{
emailAddress = user.Properties["mail"][0].ToString());
}
}
I've also included some other properties of interest that it might be desirable to either search on instead of sAMAccountName or use with your code in the string array that specifies properties to retrieve. A property name does not have to appear in the properties to retrieve to be able to use it in the LDAP filter string.
If you do have to authenticate with a particular user or use a specific OU, it gets slightly more complicated.
private static string GetNearestDomainController()
{
string nearestDomainController = null;
using (Domain computerDomain = Domain.GetComputerDomain())
using (DomainController currentDomainController = computerDomain.FindDomainController())
{
nearestDomainController = currentDomainController.Name;
}
return nearestDomainController;
}
string emailAddress = null;
using (DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + GetNearestDomainController() + "/OU=People,DC=dev,DC=local"), username, password)
using (DirectorySearcher userSearcher = new DirectorySearcher(searchRoot, @"(&(objectCategory=Person)(objectClass=user)(mail=*)(sAMAccountName=username))", new string[] { "cn", "sAMAccountName", "displayName", "givenName", "sn", "mail" }))
{
SearchResult user = userSearcher.FindOne();
if (user != null)
{
emailAddress = user.Properties["mail"][0].ToString();
}
}
Obviously, if your computer isn't bound to the domain, GetComputerDomain won't work and you'll have to specify an LDAP host manually.
Also, if you're taking a parameter to use in the LDAP filter string from user input, it's wise to make sure that any characters special to LDAP are properly escaped. Here's an extension to the string class that makes it easy.
using System;
using System.Text.RegularExpressions;
/// <summary>
///
/// </summary>
public static class LdapFilterEscapeStringExtension
{
private static Regex escapeFilter = new Regex(@"([\," + Regex.Escape("\\#+<>;\"=(*)") + "])", RegexOptions.Compiled);
private static Regex unescapeFilter = new Regex(@"\\([\," + Regex.Escape(" \\#+<>;\"=(*)") + "])", RegexOptions.Compiled);
/// <summary>
/// Escapes special characters in a string to be used within an LDAP filter.
/// </summary>
/// <param name="inputString">The input string.</param>
/// <returns></returns>
public static string EscapeLdapFilterValue(this string inputString)
{
string escaped = inputString;
if (!string.IsNullOrEmpty(inputString))
{
escaped = escapeFilter.Replace(inputString, @"\$1");
if (inputString.StartsWith(" "))
{
escaped = @"\" + escaped;
}
if (inputString.EndsWith(" "))
{
escaped = escaped.Substring(0, escaped.Length - 1) + @"\ ";
}
}
return escaped;
}
/// <summary>
/// Unescapes special characters in a string to be used within an LDAP filter.
/// </summary>
/// <param name="inputString">The input string.</param>
/// <returns></returns>
public static string UnescapeLdapFilterValue(this string inputString)
{
string unescaped = inputString;
if (!string.IsNullOrEmpty(inputString))
{
unescaped = unescapeFilter.Replace(inputString, "$1");
}
return unescaped;
}
}
JamieSee
Member
2 Points
1 Post
Re: Constructing LDAP path string
Jan 09, 2013 04:05 PM|LINK
You can do this a lot more simply if you skip searching a particular OU and use an LDAP filter instead. A new DirectoryEntry with no arguments to the constructor is equivalent to DC=dev,DC=local authenticated with the current Windows user to the current domain. This also has the benefit of generating a lot less traffic by having the AD do the search for you and return only the matching result.
string emailAddress = null; using (DirectoryEntry searchRoot = new DirectoryEntry()) using (DirectorySearcher userSearcher = new DirectorySearcher(searchRoot, @"(&(objectCategory=Person)(objectClass=user)(mail=*)(sAMAccountName=username))", new string[] { "cn", "sAMAccountName", "displayName", "givenName", "sn", "mail" })) { SearchResult user = userSearcher.FindOne(); if (user != null) { emailAddress = user.Properties["mail"][0].ToString()); } }I've also included some other properties of interest that it might be desirable to either search on instead of sAMAccountName or use with your code in the string array that specifies properties to retrieve. A property name does not have to appear in the properties to retrieve to be able to use it in the LDAP filter string.
If you do have to authenticate with a particular user or use a specific OU, it gets slightly more complicated.
private static string GetNearestDomainController() { string nearestDomainController = null; using (Domain computerDomain = Domain.GetComputerDomain()) using (DomainController currentDomainController = computerDomain.FindDomainController()) { nearestDomainController = currentDomainController.Name; } return nearestDomainController; } string emailAddress = null; using (DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + GetNearestDomainController() + "/OU=People,DC=dev,DC=local"), username, password) using (DirectorySearcher userSearcher = new DirectorySearcher(searchRoot, @"(&(objectCategory=Person)(objectClass=user)(mail=*)(sAMAccountName=username))", new string[] { "cn", "sAMAccountName", "displayName", "givenName", "sn", "mail" })) { SearchResult user = userSearcher.FindOne(); if (user != null) { emailAddress = user.Properties["mail"][0].ToString(); } }Obviously, if your computer isn't bound to the domain, GetComputerDomain won't work and you'll have to specify an LDAP host manually.
Also, if you're taking a parameter to use in the LDAP filter string from user input, it's wise to make sure that any characters special to LDAP are properly escaped. Here's an extension to the string class that makes it easy.
using System; using System.Text.RegularExpressions; /// <summary> /// /// </summary> public static class LdapFilterEscapeStringExtension { private static Regex escapeFilter = new Regex(@"([\," + Regex.Escape("\\#+<>;\"=(*)") + "])", RegexOptions.Compiled); private static Regex unescapeFilter = new Regex(@"\\([\," + Regex.Escape(" \\#+<>;\"=(*)") + "])", RegexOptions.Compiled); /// <summary> /// Escapes special characters in a string to be used within an LDAP filter. /// </summary> /// <param name="inputString">The input string.</param> /// <returns></returns> public static string EscapeLdapFilterValue(this string inputString) { string escaped = inputString; if (!string.IsNullOrEmpty(inputString)) { escaped = escapeFilter.Replace(inputString, @"\$1"); if (inputString.StartsWith(" ")) { escaped = @"\" + escaped; } if (inputString.EndsWith(" ")) { escaped = escaped.Substring(0, escaped.Length - 1) + @"\ "; } } return escaped; } /// <summary> /// Unescapes special characters in a string to be used within an LDAP filter. /// </summary> /// <param name="inputString">The input string.</param> /// <returns></returns> public static string UnescapeLdapFilterValue(this string inputString) { string unescaped = inputString; if (!string.IsNullOrEmpty(inputString)) { unescaped = unescapeFilter.Replace(inputString, "$1"); } return unescaped; } }