Ottenere tutti i report diretti da Active Directory

Sto cercando di ottenere tutti i report diretti di un utente tramite Active Directory, in modo ricorsivo. Quindi, dato un utente, finirò con un elenco di tutti gli utenti che hanno questa persona come manager o che hanno una persona come manager che ha una persona come manager … che alla fine ha l’utente di input come gestore.

Il mio attuale tentativo è piuttosto lento:

private static Collection GetDirectReportsInternal(string userDN, out long elapsedTime) { Collection result = new Collection(); Collection reports = new Collection(); 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 subResult = GetDirectReportsInternal(s, out subElapsed); allSubElapsed += subElapsed; foreach (string s2 in subResult) { result.Add(s2); } } sw.Stop(); elapsedTime = sw.ElapsedMilliseconds + allSubElapsed; return result; } 

In sostanza, questa funzione accetta come input un nome distinto (CN = Michael Stum, OU = test, DC = sub, DC = dominio, DC = com) e con questo la chiamata a ds.FindOne () è lenta.

Ho scoperto che è molto più veloce cercare l’utentePrincipalName. Il mio problema: sr.Properties [“directReports”] è solo una lista di stringhe, e questo è il distinguishedName, che sembra lento da cercare.

Mi chiedo, c’è un modo veloce per convertire tra distinguishedName e userPrincipalName? O c’è un modo più veloce per cercare un utente se ho solo il distinguishedName con cui lavorare?

Modifica: grazie alla risposta! La ricerca nel campo Manager ha migliorato la funzione da 90 secondi a 4 secondi. Ecco il codice nuovo e migliorato, che è più veloce e più leggibile (si noti che molto probabilmente c’è un bug nella funzionalità elapsedTime, ma il vero nucleo della funzione funziona):

 private static Collection GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime) { Collection result = new Collection(); 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 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; } 

Innanzitutto, impostando Scope su “sottostruttura” non è necessario quando hai già il DN che stai cercando.

Inoltre, che ne dici di trovare tutti gli oggetti la cui proprietà “manager” è la persona che cerchi, quindi iterandoli. Questo dovrebbe generalmente essere più veloce del contrario.

 (&(objectCategory=user)(manager=)) 

EDIT: Il seguente è importante ma è stato menzionato solo nei commenti a questa risposta fino ad ora:

Quando la stringa del filtro è costruita come indicato sopra, c’è il rischio di romperla con caratteri validi per un DN, ma con un significato speciale in un filtro. Questi devono essere sfuggiti :

 * as \2a ( as \28 ) as \29 \ as \5c NUL as \00 / as \2f // Arbitrary binary data can be represented using the same scheme. 

EDIT: Impostare SearchRoot sul DN di un object e SearchScope su Base è anche un modo rapido per estrarre un singolo object da AD.