usuario - query active directory c#
Obtener todos los informes directos de Active Directory (1)
Estoy tratando de obtener todos los informes directos de un usuario a través de Active Directory, recursivamente. Entonces, dado un usuario, terminaré con una lista de todos los usuarios que tienen a esta persona como gerente o que tienen una persona como gerente que tiene una persona como gerente ... que eventualmente tiene el usuario de entrada como gerente.
Mi intento actual es bastante lento:
private static Collection<string> GetDirectReportsInternal(string userDN, out long elapsedTime)
{
Collection<string> result = new Collection<string>();
Collection<string> reports = new Collection<string>();
Stopwatch sw = new Stopwatch();
sw.Start();
long allSubElapsed = 0;
string principalname = string.Empty;
using (DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}",userDN)))
{
using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
{
ds.SearchScope = SearchScope.Subtree;
ds.PropertiesToLoad.Clear();
ds.PropertiesToLoad.Add("directReports");
ds.PropertiesToLoad.Add("userPrincipalName");
ds.PageSize = 10;
ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
SearchResult sr = ds.FindOne();
if (sr != null)
{
principalname = (string)sr.Properties["userPrincipalName"][0];
foreach (string s in sr.Properties["directReports"])
{
reports.Add(s);
}
}
}
}
if (!string.IsNullOrEmpty(principalname))
{
result.Add(principalname);
}
foreach (string s in reports)
{
long subElapsed = 0;
Collection<string> subResult = GetDirectReportsInternal(s, out subElapsed);
allSubElapsed += subElapsed;
foreach (string s2 in subResult)
{
result.Add(s2);
}
}
sw.Stop();
elapsedTime = sw.ElapsedMilliseconds + allSubElapsed;
return result;
}
Esencialmente, esta función toma un Nombre distinguido como entrada (CN = Michael Stum, OU = prueba, DC = sub, DC = dominio, DC = com), y con eso, la llamada a ds.FindOne () es lenta.
Descubrí que es mucho más rápido buscar UserPrincipalName. Mi problema: sr.Properties ["directReports"] es solo una lista de cadenas, y ese es el distinguishedName, que parece lento de buscar.
Me pregunto, ¿hay una manera rápida de convertir entre distinguishedName y userPrincipalName? ¿O hay una manera más rápida de buscar un usuario si solo tengo el distinguishedName para trabajar?
Editar: ¡ Gracias a la respuesta! La búsqueda en el Manager-Field mejoró la función de 90 Segundos a 4 Segundos. Aquí está el código nuevo y mejorado, que es más rápido y más legible (tenga en cuenta que lo más probable es que haya un error en la funcionalidad del tiempo transcurrido, pero el núcleo real de la función funciona):
private static Collection<string> GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime)
{
Collection<string> result = new Collection<string>();
Stopwatch sw = new Stopwatch();
sw.Start();
string principalname = string.Empty;
using (DirectoryEntry directoryEntry = new DirectoryEntry(ldapBase))
{
using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
{
ds.SearchScope = SearchScope.Subtree;
ds.PropertiesToLoad.Clear();
ds.PropertiesToLoad.Add("userPrincipalName");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PageSize = 10;
ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
ds.Filter = string.Format("(&(objectCategory=user)(manager={0}))",userDN);
using (SearchResultCollection src = ds.FindAll())
{
Collection<string> tmp = null;
long subElapsed = 0;
foreach (SearchResult sr in src)
{
result.Add((string)sr.Properties["userPrincipalName"][0]);
tmp = GetDirectReportsInternal(ldapBase, (string)sr.Properties["distinguishedName"][0], out subElapsed);
foreach (string s in tmp)
{
result.Add(s);
}
}
}
}
}
sw.Stop();
elapsedTime = sw.ElapsedMilliseconds;
return result;
}
En primer lugar, establecer Scope en "subárbol" no es necesario cuando ya tienes el DN que estás buscando.
Además, ¿qué le parece buscar todos los objetos cuya propiedad de "administrador" es la persona que busca, y luego iterarlos? En general, esto debería ser más rápido que al revés.
(&(objectCategory=user)(manager=<user-dn-here>))
EDITAR: Lo siguiente es importante, pero solo se ha mencionado en los comentarios a esta respuesta hasta el momento:
Cuando la cadena de filtro se construye como se indicó anteriormente, existe el riesgo de romperla con caracteres que son válidos para un DN, pero tienen un significado especial en un filtro. Estos deben ser escapados :
* as /2a
( as /28
) as /29
/ as /5c
NUL as /00
/ as /2f
// Arbitrary binary data can be represented using the same scheme.
EDITAR: Establecer SearchRoot
en el DN de un objeto y SearchScope
en Base
también es una forma rápida de sacar un objeto de AD.