Получение всех прямых отчетов из Active Directory - PullRequest
12 голосов
/ 10 октября 2008

Я пытаюсь получить все прямые отчеты пользователя через Active Directory, рекурсивно. Поэтому, учитывая пользователя, я получу список всех пользователей, у которых есть этот человек в качестве менеджера или у кого есть человек в качестве менеджера, у которого есть человек в качестве менеджера ... у которого в конечном итоге есть входной пользователь в качестве менеджера.

Моя текущая попытка довольно медленная:

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;
}

По сути, эта функция принимает в качестве входных данных выделенное имя (CN = Michael Stum, OU = test, DC = sub, DC = domain, DC = com), и при этом вызов ds.FindOne () является медленным .

Я обнаружил, что поиск по userPrincipalName намного быстрее. Моя проблема: sr.Properties ["directReports"] - это просто список строк, и это - значимое имя, которое кажется медленным для поиска.

Интересно, есть ли быстрый способ конвертировать между отличительным именем и userPrincipalName? Или есть более быстрый способ поиска пользователя, если у меня есть только одно имя для работы?

Редактировать: Спасибо за ответ! Поиск в поле «Менеджер» улучшил функцию с 90 до 4 секунд. Вот новый и улучшенный код, который быстрее и более читабелен (обратите внимание, что, скорее всего, есть ошибка в функциональности elapsedTime, но фактическое ядро ​​функции работает):

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;
}

1 Ответ

10 голосов
/ 10 октября 2008

Прежде всего, установка Scope на «поддерево» не требуется, если у вас уже есть искомое DN.

Кроме того, как насчет поиска всех объектов, чье свойство "manager" - это человек, которого вы ищете, и затем их итерирование. Это обычно должно быть быстрее, чем наоборот.

(&(objectCategory=user)(manager=<user-dn-here>))

РЕДАКТИРОВАТЬ: важно следующее, но упоминалось только в комментариях к этому ответу:

Когда строка фильтра построена, как указано выше, есть риск ее разбить символами, которые действительны для DN, но имеют особое значение в фильтре. Эти должны быть экранированы :

*   as  \2a
(   as  \28
)   as  \29
\   as  \5c
NUL as  \00
/   as  \2f

// Arbitrary binary data can be represented using the same scheme.

EDIT: установка SearchRoot в DN объекта, а SearchScope в Base также является быстрым способом вытащить один объект из AD.

...