Превышен лимит размера PHP ldap_search - PullRequest
4 голосов
/ 26 декабря 2011

Я довольно плохо знаком с Microsoft Active Directory и сталкиваюсь с некоторыми трудностями:

AD имеет ограничение размера 1000 элементов на запрос. Я не могу изменить ограничение размера. Похоже, что PHP не поддерживает подкачку страниц (я использую версию 5.2, и нет способа обновить рабочий сервер.)

До сих пор я сталкивался с двумя возможными решениями:

  1. Сортировка записей по objectSid и использование фильтров для получения всех объектов. Пример кода
    Мне это не нравится по нескольким причинам:
    • Кажется непредсказуемым связываться с objectSid, так как вы должны разобрать его, преобразовать в десятичную, преобразовать обратно ...
    • Я не понимаю, как вы можете сравнить эти идентификаторы.
      (Я пробовал: '& ((objectClass = user) (objectSid> = 0))')

  2. Фильтр после первых букв имен объектов (как предложено здесь ):
    Это не оптимальное решение, так как многие пользователи / группы в нашей системе имеют префикс одинаковых букв.

Итак, мой вопрос:

Какой подход лучше использовать здесь?
Если это первый, как я могу быть уверен, что правильно обработал objectSid?

Есть ли другие возможности? Я что-то упускаю из виду?

Обновление:
- Этот вопрос содержит информацию о том, почему не работает расширение Simple Paged Results.
- Веб-сервер работает на сервере Linux, поэтому COM-объекты / adoDB не подходят.

Ответы [ 4 ]

1 голос
/ 29 декабря 2016

Мне удалось обойти ограничение размера с помощью ldap_control_paged_result

ldap_control_paged_result используется для включения пагинации LDAP путем отправки элемента управления нумерацией страниц.Функция ниже работает отлично в моем случае.Это будет работать для (PHP 5> = 5.4.0, PHP 7)

function retrieves_users($conn)
    {
        $dn        = 'ou=,dc=,dc=';
        $filter    = "(&(objectClass=user)(objectCategory=person)(sn=*))";
        $justthese = array();

        // enable pagination with a page size of 100.
        $pageSize = 100;

        $cookie = '';

        do {
            ldap_control_paged_result($conn, $pageSize, true, $cookie);

            $result  = ldap_search($conn, $dn, $filter, $justthese);
            $entries = ldap_get_entries($conn, $result);

            if(!empty($entries)){
                for ($i = 0; $i < $entries["count"]; $i++) {
                    $data['usersLdap'][] = array(
                            'name' => $entries[$i]["cn"][0],
                            'username' => $entries[$i]["userprincipalname"][0]
                    );
                }
            }
            ldap_control_paged_result_response($conn, $result, $cookie);

        } while($cookie !== null && $cookie != '');

        return $data;
    }

Если вы уже успешно обновили свой сервер, то функция выше может получить все записи.Я использую эту функцию, чтобы получить всех пользователей в нашей AD.

1 голос
/ 13 января 2012

Поскольку я не нашел чистых решений, я решил использовать первый подход: фильтрация по объектам-сторонам.

Этот обходной путь имеет свои ограничения:

  1. Это толькоработает для объектов с objectid, то есть для пользователей и групп.
  2. Предполагается, что все пользователи / группы создаются с одинаковыми правами доступа.
  3. Предполагается, что относительных идентификаторов безопасности SID не больше, чемограничение размера.

Идея состоит в том, чтобы сначала прочитать все возможные объекты и выбрать объект с наименьшим относительным SID.Относительный SID - это последний блок в SID:

S-1-5-21-3188256696-111411151-3922474875- 1158

Предположим, что это самый низкийотносительный SID в поиске, который только возвратил «Частичные результаты поиска».Далее предположим, что ограничение размера равно 1000.

Затем программа выполняет следующие действия: выполняет поиск всех объектов с SID в диапазоне

S-1-5-21-3188256696-111411151-3922474875- 1158
и
S-1-5-21-3188256696-111411151-3922474875- 0159

затем все между

S-1-5-21-3188256696-111411151-3922474875- 1158
и
S-1-5-21-3188256696-111411151-3922474875- 2157

и т. Д., Пока один из поисков не вернет ноль объектов.

У этого подхода есть несколько проблем, но его достаточно для моих целей.
Код:

$filter = '(objectClass=Group)';
$attributes = array('objectsid','cn'); //objectsid needs to be set

$result = array();

$maxPageSize = 1000;
$searchStep = $maxPageSize-1;

$adResult = @$adConn->search($filter,$attributes); //Supress warning for first query (because it exceeds the size limit)

//Read smallest RID from the resultset
$minGroupRID = '';

for($i=0;$i<$adResult['count'];$i++){
    $groupRID = unpack('V',substr($adResult[$i]['objectsid'][0],24));
    if($minGroupRID == '' || $minGroupRID>$groupRID[1]){
        $minGroupRID = $groupRID[1];
    }    
}

$sidPrefix =  substr($adResult[$i-1]['objectsid'][0],0,24);   //Read last objectsid and cut off the prefix

$nextStepGroupRID = $minGroupRID;

do{ //Search for all objects with a lower objectsid than minGroupRID
    $adResult = $adConn->search('(&'.$filter.'(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID-$searchStep))).'))', $attributes);

    for($i=0;$i<$adResult['count'];$i++){
        $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24));    //Extract the relative SID from the SID
        $RIDs[] = $RID[1];

        $resultSet = array();
        foreach($attributes as $attribute){
            $resultSet[$attribute] = $adResult[$i][$attribute][0];
        }
        $result[$RID[1]] = $resultSet;
    }

    $nextStepGroupRID = $nextStepGroupRID-$searchStep;

}while($adResult['count']>1);

$nextStepGroupRID = $minGroupRID;

do{ //Search for all object with a higher objectsid than minGroupRID  
    $adResult = $adConn->search('(&'.$filter.'(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID+$searchStep))).'))', $attributes);

    for($i=0;$i<$adResult['count'];$i++){
        $RID = unpack('V',substr($adResult[$i]['objectsid'][0],24));    //Extract the relative SID from the SID
        $RIDs[] = $RID[1];

        $resultSet = array();
        foreach($attributes as $attribute){
            $resultSet[$attribute] = $adResult[$i][$attribute][0];
        }
        $result[$RID[1]] = $resultSet;
    }

    $nextStepGroupRID = $nextStepGroupRID+$searchStep;

}while($adResult['count']>1);

var_dump($result);

Метод поиска $ adConn-> выглядит следующим образом:

function search($filter, $attributes = false, $base_dn = null) {
        if(!isset($base_dn)){
            $base_dn = $this->baseDN;
        }

        $entries = false;
        if (is_string($filter) && $this->bind) {
                if (is_array($attributes)) {
                        $search  = ldap_search($this->resource, $base_dn, $filter, $attributes);
                } else {
                        $search  = ldap_search($this->resource, $base_dn, $filter);
                }
                if ($search !== false) {
                        $entries = ldap_get_entries($this->resource, $search);
                }
        }
        return $entries;
}
1 голос
/ 29 июля 2014

Предыдущая ошибка скрипта может возникнуть, когда расстояние между ближайшими SID больше 999. Пример:

S-1-5-21-3188256696-111411151-3922474875- 1158

S-1-5-21-3188256696-111411151-3922474875- 3359

3359-1158> 999

во избежание этого необходимо использовать жесткие конструкции

Пример:

$tt = '1';
do {
    ...
    $nextStepGroupRID = $nextStepGroupRID - $searchStep;
    $tt++;
} while ($tt < '30');

В этом примере мы вынуждены проверить 999 * 30 * 2 = 59940 значений.

1 голос
/ 27 декабря 2011

Никогда не делайте предположений о серверах или конфигурации серверов, это приводит к хрупкому коду и неожиданным, иногда впечатляющим сбоям. То, что сегодня это AD, не означает, что это будет завтра, или что Microsoft не изменит ограничение по умолчанию на сервере. Недавно я имел дело с ситуацией, когда клиентский код был написан с ведомым знанием, что ограничение размера было 2000, и когда администраторы по собственным причинам изменили ограничение размера, код клиента ужасно потерпел неудачу.

Вы уверены, что PHP не поддерживает элементы управления запросами (простое постраничное расширение результата является элементом управления запросом)? Я написал статью о «LDAP: простые постраничные результаты» , и, хотя пример кода статьи - Java, важны концепции, а не язык. См. Также «LDAP: Практика программирования» .

...