Вопрос по active-directory, ldap, c# – Получение всех прямых отчетов из Active Directory

12

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

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

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

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

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

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

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

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;
}
Есть ли конкретная причина, по которой вы бы хотели явно установить размер страницы на 10? Cody
Очень полезные вопросы и ответ, спасибо! Peter C
Я думаю, что это должно работать. Объекты всегда передаются как ref, AFAIK. Увидеть:stackoverflow.com/questions/186891/... Tomalak
Вы можете получить дополнительную скорость, убрав DirectoryEntry и DirectorySearcher из рекурсии. Они неони не меняются, не так ли? Tomalak

Ваш Ответ

1   ответ
10

поддерево» не требуется, если у вас уже есть DN, которое вы ищете.

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

(&(objectCategory=user)(manager=<user-dn-here>))
</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.

РЕДАКТИРОВАТЬ: НастройкаSearchRoot к DN объекта, аSearchScope вBase также это быстрый способ вытащить один объект из AD.

Возможно, вам также необходимо проверить наличие циклических ссылок в иерархии менеджера. Я сомневаюсь, что в AD есть механизм, который мешает им. Tomalak
Кто-нибудь знает сайт, в котором подробно описывается, почему поиск по менеджерам работает лучше, чем поиск по репортажам? Это кажется нелогичным; чтобы искать по менеджеру, вы должны просмотреть все LDAP для записей, где поле менеджера равно вашему DN, для второго вы вытягиваете один объект по DN, а затем получаете доступ к свойству, в котором перечислены все прямые отчеты. JohnLBevan
На самом деле, я думаю, что я посмотрел не на тот источник. Возвращенное DN всегда будет правильно экранировано для DN, но вы ссылались на Фильтр:msdn.microsoft.com/en-us/library/aa746475.aspx => "Специальные символы", Я'добавлюEscape DN for Filter " функция к моему коду тогда. Michael Stum
Помните, что при таком подходе вам нужно перенести риск повреждения строки фильтра символами, которые допустимы в DN, но зарезервированы в строке фильтра. В верхней части моей головы, по крайней мере, "#" нужно сбежать. Tomalak

Похожие вопросы