Превышен предел размера поиска PHP ldap


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

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

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

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

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

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

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

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

Обновление:
- Этот связанный с этим вопрос содержит информацию о том, почему расширение Простых результатов с выгружаемыми страницами не работает.
- Веб-сервер работает на сервере Linux, поэтому COM-объекты/ADODB являются это не вариант.

Author: Community, 2011-12-26

4 answers

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

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

 1
Author: Terry Gardner, 2011-12-27 00:19:21

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

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

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

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

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

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

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

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

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

С-1-5-21-3188256696-111411151-3922474875-1158
и
С-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
Author: Envyrus, 2012-02-22 14:46:06

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

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

С-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
Author: Андрей Иванов, 2014-07-29 13:20:25

Я смог обойти ограничение по размеру, используя 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;
    }

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

 0
Author: Fokwa Best, 2016-12-29 16:22:22