usuarios - ¿Cómo puede encontrar un usuario en el directorio activo desde C#?
obtener datos usuario active directory c# (8)
¿Estás en .NET 3.5? Si es así, AD tiene excelentes características nuevas en .NET 3.5. Consulte este artículo Administración de los principales de seguridad de directorios en .NET 3.5 por Ethan Wilanski y Joe Kaplan.
Una de las características nuevas más importantes es una clase "PrincipalSearcher" que debería simplificar enormemente la búsqueda de usuarios y / o grupos en AD.
Si no puede usar .NET 3.5, una cosa que puede hacer su vida más fácil se llama "Resolución de nombre ambiguo", y es un filtro de búsqueda especial poco conocido que buscará en casi cualquier atributo relacionado con el nombre, todo a la vez.
Especifique su consulta de búsqueda LDAP de esta manera:
searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", yourSearchTerm)
Además, recomendaría filtrar en el atributo "objectCategory", ya que es un valor único e indexado de forma predeterminada en AD, que es mucho más rápido que usar "objectClass".
Bagazo
Estoy tratando de averiguar cómo buscar AD desde C # de manera similar a cómo funciona "Buscar usuarios, contactos y grupos" en la herramienta Usuarios y equipos de Active Directory. Tengo una cadena que contiene un nombre de grupo o un nombre de usuario (generalmente en el nombre de nombre inicialinicial [si tienen uno] apellido, pero no siempre). Incluso si hago una consulta separada para grupos frente a usuarios, no puedo encontrar una forma de buscar que capture la mayoría de las cuentas de usuario. La herramienta Buscar usuarios, contactos y grupos los devuelve casi todo el tiempo. ¿Alguien tiene alguna sugerencia?
Ya sé cómo usar la clase DirectorySearcher, el problema es que no puedo encontrar una consulta que haga lo que me gustaría. Ni cn ni samaccount name tienen algo que ver con el nombre del usuario en esto, así que no puedo buscar en esos. Dividir cosas y buscar en sn y givenName no alcanza tanto como lo hace esa herramienta.
Debe compilar la cadena de búsqueda según cómo esté buscando el usuario.
using (var adFolderObject = new DirectoryEntry())
{
using(var adSearcherObject = new DirectorySearcher(adFolderObject))
{
adSearcherObject.SearchScope = SearchScope.Subtree;
adSearcherObject.Filter = "(&(objectClass=person)(" + userType + "=" + userName + "))";
return adSearcherObject.FindOne();
}
}
userType debe ser sAMAccountName o CN, dependiendo de cómo se formatea el nombre de usuario.
ex:
firstname.lastname (o flastname) generalmente será el sAMAccountName
FirstName LastName generalmente será el CN
Para agregar a la respuesta de Miyagi ...
Aquí hay un filtro / consulta para aplicar a DirectorySearcher
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "samaccountname=" + userName;
SearchResult result = ds.FindOne();
System.DirectoryServices tiene dos espacios de nombres ... DirectoryEntry y DirectorySearcher.
Más información sobre el DirectorySearcher aquí:
http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx
Luego puede usar la propiedad Filter para filtrar por grupo, usuario, etc.
Entonces, si quisiera filtrar por nombre de cuenta, establecería .Filter en:
"(&(sAMAccountName=bsmith))"
y ejecuta el método FilterAll. Esto devolverá una SearchResultCollection que puede recorrer y extraer información sobre el usuario.
public DirectoryEntry Search(string searchTerm, string propertyName)
{
DirectoryEntry directoryObject = new DirectoryEntry(<pathToAD>);
foreach (DirectoryEntry user in directoryObject.Children)
{
if (user.Properties[propertyName].Value != null)
if (user.Properties[propertyName].Value.ToString() == searchTerm)
return user;
}
return null;
}
Obtuve esto del artículo de Joe Kaplan y Ethan Wilansky . Use esto usando (de referencia a System.DirectoryServices.AccountManagement dll):
using System.DirectoryServices.AccountManagement;
private bool CheckUserinAD(string domain, string username)
{
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain);
UserPrincipal user = new UserPrincipal(domainContext);
user.Name = username;
PrincipalSearcher pS = new PrincipalSearcher();
pS.QueryFilter = user;
PrincipalSearchResult<Principal> results = pS.FindAll();
if (results != null && results.Count() > 0)
return true;
return false;
}
Las otras respuestas fueron mal descritas, no describieron cómo implementarlas, y la mayoría dieron las propiedades de filtro incorrectas. Ni siquiera necesita usar .Filter
: puede asignar sus propiedades (apellido = .Surname
, first name = .GivenName
) a un objeto UserPrincipal
, luego buscar en ese objeto utilizando un PrincipalSearcher
en cualquier evento que active el buscar:
string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
Supongo que tiene cuadros de texto para Nombre y Apellido para obtenerlo, con ID / Nombres de txtFirstName
y txtLastName
. Tenga en cuenta que si no tiene un valor en la propiedad que está buscando, no lo agregue al UserPrincipal
, o se generará una excepción. Esa es la razón de los cheques que incluí, arriba.
A continuación, realice un .FindAll
on srch
para obtener resultados de búsqueda en una colección Principal
objetos Principal
:
using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
int resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
string username = found.SamAccountName; // Note, this is not the full user ID! It does not include the domain.
}
}
}
}
Tenga en cuenta que los resultados no serán nulos, incluso si .Count()
es 0
y por qué ambos controles están allí.
Usted itera usando ese foreach
para obtener las propiedades que necesita, y esto responde la pregunta de cómo encontrar un usuario en AD usando C #, pero note que solo puede obtener algunas propiedades usando el objeto Principal
, y si llegué a esta pregunta a través de Google (como yo lo hice), estaría muy desanimado. Si crees que eso es todo lo que necesitas, genial, ¡listo! Pero para obtener el resto (y descansar mi propia conciencia), tienes que bucear, y voy a describir cómo hacerlo.
Descubrí que no puedes usar ese username
que puse arriba, pero tienes que obtener todo el tipo de nombre DOMAIN/doej
. Así es como haces eso. En cambio, pon esto en ese ciclo foreach
, arriba:
string userId = GetUserIdFromPrincipal(found);
y usa esta función:
private static string GetUserIdFromPrincipal(Principal prin)
{
string upn = prin.UserPrincipalName;
string domain = upn.Split(''@'')[1];
domain = domain.Substring(0, domain.IndexOf(".YOURDOMAIN"));
// "domain" will be the subdomain the user belongs to.
// This may require edits depending on the organization.
return domain + @"/" + prin.SamAccountName;
}
Una vez que tenga eso, puede llamar a esta función:
public static string[] GetUserProperties(string strUserName)
{
UserPrincipal up = GetUser(strUserName);
if (up != null)
{
string firstName = up.GivenName;
string lastName = up.Surname;
string middleInit = String.IsNullOrEmpty(up.MiddleName) ? "" : up.MiddleName.Substring(0, 1);
string email = up.EmailAddress;
string location = String.Empty;
string phone = String.Empty;
string office = String.Empty;
string dept = String.Empty;
DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject();
DirectorySearcher ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("l"); // city field, a.k.a location
ds.PropertiesToLoad.Add("telephonenumber");
ds.PropertiesToLoad.Add("department");
ds.PropertiesToLoad.Add("physicalDeliveryOfficeName");
SearchResultCollection results = ds.FindAll();
if (results != null && results.Count > 0)
{
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "l") // this matches the "City" field in AD properties
location = rpc["l"][0].ToString();
if (rp == "telephonenumber")
phone = FormatPhoneNumber(rpc["telephonenumber"][0].ToString());
if (rp == "physicalDeliveryOfficeName")
office = rpc["physicalDeliveryOfficeName"][0].ToString();
if (rp == "department")
dept = rpc["department"][0].ToString();
}
}
string[] userProps = new string[10];
userProps[0] = strUserName;
userProps[1] = firstName;
userProps[2] = lastName;
userProps[3] = up.MiddleName;
userProps[4] = middleInit;
userProps[5] = email;
userProps[6] = location;
userProps[7] = phone;
userProps[8] = office;
userProps[9] = dept;
return userProps;
}
else
return null;
}
/// <summary>
/// Returns a UserPrincipal (AD) user object based on string userID being supplied
/// </summary>
/// <param name="strUserName">String form of User ID: domain/username</param>
/// <returns>UserPrincipal object</returns>
public static UserPrincipal GetUser(string strUserName)
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
try
{
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, strUserName);
return oUserPrincipal;
}
catch (Exception ex) { return null; }
}
public static string FormatPhoneNumber(string strPhoneNumber)
{
if (strPhoneNumber.Length > 0)
// return String.Format("{0:###-###-####}", strPhoneNumber); // formating does not work because strPhoneNumber is a string and not a number
return Regex.Replace(strPhoneNumber, @"(/d{3})(/d{3})(/d{4})", "$1-$2-$3");
else
return strPhoneNumber;
}
Tenga en cuenta que la función FormatPhoneNumber
es para números norteamericanos. Tomará un número que encuentre ( ##########
) y lo separará en ###-###-####
.
A continuación, puede obtener las propiedades de este modo en ese ciclo foreach
:
string[] userProps = GetUserProperties(userId);
string office = userProps[8];
Pero, como una solución completa, incluso puede agregar estos resultados en una columna de DataRow
y devolverlos como parte de una DataTable
que luego podría vincular a un ListView
o GridView
. Así es como lo hice, enviando una List<string>
con las propiedades que necesitaba:
/// <summary>
/// Gets matches based on First and Last Names.
/// This function takes a list of acceptable properties:
/// USERNAME
/// MIDDLE_NAME
/// MIDDLE_INITIAL
/// EMAIL
/// LOCATION
/// POST
/// PHONE
/// OFFICE
/// DEPARTMENT
///
/// The DataTable returned will have columns with these names, and firstName and lastName will be added to a column called "NAME"
/// as the first column, automatically.
/// </summary>
/// <param name="firstName"></param>
/// <param name="lastName"></param>
/// <param name="props"></param>
/// <returns>DataTable of columns from "props" based on first and last name results</returns>
public static DataTable GetUsersFromName(string firstName, string lastName, List<string> props)
{
string userId = String.Empty;
int resultCount = 0;
DataTable dt = new DataTable();
DataRow dr;
DataColumn dc;
// Always set the first column to the Name we pass in
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
dc.ColumnName = "NAME";
dt.Columns.Add(dc);
// Establish our property list as columns in our DataTable
if (props != null && props.Count > 0)
{
foreach (string s in props)
{
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
if (!String.IsNullOrEmpty(s))
{
dc.ColumnName = s;
dt.Columns.Add(dc);
}
}
}
// Start our search
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
// Iterate results, set into DataRow, add to DataTable
dr = dt.NewRow();
dr["NAME"] = found.DisplayName;
if (props != null && props.Count > 0)
{
userId = GetUserIdFromPrincipal(found);
// Get other properties
string[] userProps = GetUserProperties(userId);
foreach (string s in props)
{
if (s == "USERNAME")
dr["USERNAME"] = userId;
if (s == "MIDDLE_NAME")
dr["MIDDLE_NAME"] = userProps[3];
if (s == "MIDDLE_INITIAL")
dr["MIDDLE_INITIAL"] = userProps[4];
if (s == "EMAIL")
dr["EMAIL"] = userProps[5];
if (s == "LOCATION")
dr["LOCATION"] = userProps[6];
if (s == "PHONE")
dr["PHONE"] = userProps[7];
if (s == "OFFICE")
dr["OFFICE"] = userProps[8];
if (s == "DEPARTMENT")
dr["DEPARTMENT"] = userProps[9];
}
}
dt.Rows.Add(dr);
}
}
}
}
return dt;
}
Llamarías a esta función así:
string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
List<string> props = new List<string>();
props.Add("OFFICE");
props.Add("DEPARTMENT");
props.Add("LOCATION");
props.Add("USERNAME");
DataTable dt = GetUsersFromName(firstName, lastName, props);
El DataTable
se llenará con esas columnas, y una columna NAME
como la primera columna, que tendrá el .DisplayName
real del .DisplayName
de AD.
Nota: Debe hacer referencia a System.DirectoryServices
y System.DirectoryServices.AccountManagement
, System.Text.RegularExpressions
, System.Data
para usar todo esto.
HTH!
El código que estaba buscando en esta publicación fue:
string uid = Properties.Settings.Default.uid;
string pwd = Properties.Settings.Default.pwd;
using (var context = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", uid, pwd))
{
using (UserPrincipal user = new UserPrincipal(context))
{
user.GivenName = "*adolf*";
using (var searcher = new PrincipalSearcher(user))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine("Mail: " + de.Properties["mail"].Value);
PrincipalSearchResult<Principal> groups = result.GetGroups();
foreach (Principal item in groups)
{
Console.WriteLine("Groups: {0}: {1}", item.DisplayName, item.Name);
}
Console.WriteLine();
}
}
}
}
Console.WriteLine("End");
Console.ReadLine();
Parece que el comodín para cualquier personaje es Asterisk (*). Es por eso:
user.GivenName = "*firstname*";
Lea más en la documentación de Microsoft