Intereting Posts
Нужна помощь с PHP и MySQL Glob не работает, когда имя каталога имеет специальные символы, такие как квадратные скобки "" Как создать штрих-код с помощью PHP и отобразить его как изображение на одной странице Как получить результат echo () в переменной на PHP? Запуск тестового примера PHPUnit Selenium программно («внутри PHP») PHP-браузер и os-обнаружение, показывающие хром вместо оперы PHP pThreads – Как вы выполняете сборку мусора? Как передать параметры CustomType в поле `collection` Symfony 2.1? Возвращение модели Eloquent в качестве JSON в Laravel 4 Создать отсортированный массив из нескольких предварительно отсортированных массивов Как отсортировать результаты полнотекстового поиска MYSQL по релевантности Как отлаживать (и исправлять) Symfony2 | 3 маршрута? Как получить окончательный, перенаправленный, канонический URL-адрес веб-сайта с использованием PHP? Код для просмотра, если сайт не работает? Узнать путь error_log

Перечислить всех пользователей в LDAP с помощью PHP

Я хотел бы создать php-скрипт, который работает как ежедневный cron. То, что я хотел бы сделать, – перечислить всех пользователей в Active Directory, извлечь определенные поля из каждой записи и использовать эту информацию для обновления полей в базе данных MySQL.

В основном, что я хочу сделать, это синхронизировать определенную пользовательскую информацию между Active Directory и таблицей MySQL.

Проблема заключается в том, что sizelimit на сервере Active Directory часто устанавливается в 1000 записей для каждого результата поиска. Я надеялся, что функция php «ldap_next_entry» обойдется вокруг этого, только выбирая одну запись за раз, но прежде чем вы сможете вызвать «ldap_next_entry», вам сначала нужно вызвать «ldap_search», который может вызвать превышение ошибки в SizeLimit.

Есть ли способ помимо удаления sizelimit с сервера? Могу ли я как-то получить «страницы» результатов?

BTW. В настоящее время я не использую сторонние библиотеки или код. Просто методы PHP ldap. Хотя, я, конечно, открыт для использования библиотеки, если это поможет.

Related of "Перечислить всех пользователей в LDAP с помощью PHP"

Я был поражен той же проблемой при разработке Zend_Ldap для Zend Framework. Я попытаюсь объяснить, какова настоящая проблема, но сделать ее коротким: до PHP 5.4 невозможно было использовать выгружаемые результаты из Active Directory с невыпущенной версией PHP ( ext/ldap ) из-за ограничений в точности это расширение .

Давайте попробуем разгадать все это … Microsoft Active Directory использует так называемый серверный элемент управления для выполнения поискового вызова на стороне сервера. Этот элемент управления описан в RFC 2696 «Расширение LDAP для простого манипулирования результатами» .

ext/php предлагает доступ к дополнительным расширениям LDAP через параметры ldap_set_option() и LDAP_OPT_SERVER_CONTROLS и LDAP_OPT_CLIENT_CONTROLS соответственно. Чтобы установить пейджинговый элемент управления, вам понадобится control-oid, который равен 1.2.840.113556.1.4.319 , и нам нужно знать, как кодировать контрольное значение (это описано в RFC ). Значение представляет собой строку октета, обертывающую BER-кодированную версию следующей SEQUENCE (скопированной из RFC):

 realSearchControlValue ::= SEQUENCE { size INTEGER (0..maxInt), -- requested page size from client -- result set size estimate from server cookie OCTET STRING } 

Поэтому мы можем установить соответствующий серверный контроль до выполнения запроса LDAP:

 $pageSize = 100; $pageControl = array( 'oid' => '1.2.840.113556.1.4.319', // the control-oid 'iscritical' => true, // the operation should fail if the server is not able to support this control 'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0) // the required BER-encoded control-value ); 

Это позволяет нам отправлять постраничный запрос на сервер LDAP / AD. Но как мы узнаем, есть ли больше страниц, и как мы укажем, с какой контрольной стоимостью мы должны отправить наш следующий запрос?

Это то место, где мы застреваем … Сервер отвечает набором результатов, который включает в себя необходимую информацию поискового вызова, но PHP не имеет метода для получения именно этой информации из набора результатов. PHP предоставляет оболочку для функции LDAP API ldap_parse_result() но требуемый последний параметр serverctrlsp не подвергается функции PHP, поэтому нет способа получить требуемую информацию. Отчет об ошибке был отправлен по этой проблеме, но с 2005 года ответа не было. Если ldap_parse_result() предоставила требуемый параметр, использование постраничных результатов будет работать подобно

 $l = ldap_connect('somehost.mydomain.com'); $pageSize = 100; $pageControl = array( 'oid' => '1.2.840.113556.1.4.319', 'iscritical' => true, 'value' => sprintf ("%c%c%c%c%c%c%c", 48, 5, 2, 1, $pageSize, 4, 0) ); $controls = array($pageControl); ldap_set_option($l, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_bind($l, 'CN=bind-user,OU=my-users,DC=mydomain,DC=com', 'bind-user-password'); $continue = true; while ($continue) { ldap_set_option($l, LDAP_OPT_SERVER_CONTROLS, $controls); $sr = ldap_search($l, 'OU=some-ou,DC=mydomain,DC=com', 'cn=*', array('sAMAccountName'), null, null, null, null); ldap_parse_result ($l, $sr, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls); // (*) if (isset($serverctrls)) { foreach ($serverctrls as $i) { if ($i["oid"] == '1.2.840.113556.1.4.319') { $i["value"]{8} = chr($pageSize); $i["iscritical"] = true; $controls = array($i); break; } } } $info = ldap_get_entries($l, $sr); if ($info["count"] < $pageSize) { $continue = false; } for ($entry = ldap_first_entry($l, $sr); $entry != false; $entry = ldap_next_entry($l, $entry)) { $dn = ldap_get_dn($l, $entry); } } 

Как вы видите, есть одна строка кода (*) которая делает все это бесполезным. На моем пути, хотя и редкая информация по этому вопросу, я нашел патч против PHP 4.3.10 ext/ldap от Iñaki Arenaza, но я тоже не пробовал его, и не знаю, может ли патч применяться к PHP5 ext/ldap . Патч расширяет ldap_parse_result() чтобы открыть 7-й параметр для PHP:

  --- ldap.c 2004-06-01 23: 05: 33.000000000 +0200
 +++ /usr/src/php4/php4-4.3.10/ext/ldap/ldap.c 2005-09-03 17: 02: 03.000000000 +0200
 @@ -74,7 +74,7 @@
  ZEND_DECLARE_MODULE_GLOBALS (LDAP)

  static unsigned char third_argument_force_ref [] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE};
 -static unsigned char arg3to6of6_force_ref [] = {6, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE};
 + static unsigned char arg3to7of7_force_ref [] = {7, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE, BYREF_FORCE};

  static int le_link, le_result, le_result_entry, le_ber_entry;

 @@ -124,7 +124,7 @@
  #if (LDAP_API_VERSION> 2000) ||  HAVE_NSLDAP
   PHP_FE (ldap_get_option, third_argument_force_ref)
   PHP_FE (ldap_set_option, NULL)
 - PHP_FE (ldap_parse_result, arg3to6of6_force_ref)
 + PHP_FE (ldap_parse_result, arg3to7of7_force_ref)
   PHP_FE (ldap_first_reference, NULL)
   PHP_FE (ldap_next_reference, NULL)
  #ifdef HAVE_LDAP_PARSE_REFERENCE
 @@ -1775,14 +1775,15 @@
     Извлечение информации из результата * /
  PHP_FUNCTION (ldap_parse_result) 
  {
 - pval ** link, ** result, ** errcode, ** matcheddn, ** errmsg, ** рефералы;
 + pval ** link, ** result, ** errcode, ** matcheddn, ** errmsg, ** рефералы, ** serverctrls;
   ldap_linkdata * ld;
   LDAPMessage * ldap_result;
 + LDAPControl ** lserverctrls, ** ctrlp, * ctrl;
   char ** lreferrals, ** refp;
   char * lmatcheddn, * lerrmsg;
   int rc, lerrcode, myargcount = ZEND_NUM_ARGS ();

 - if (myargcount 6 || zend_get_parameters_ex (myargcount, & link, & result, & errcode, & matcheddn, & errmsg, & referrals) == FAILURE) {
 + if (myargcount 7 || zend_get_parameters_ex (myargcount, & link, & result, & errcode, & matcheddn, & errmsg, & referrals, & serverctrls) == FAILURE) {
    WRONG_PARAM_COUNT;
   }

 @@ -1793,7 +1794,7 @@
      myargcount> 3?  & lmatcheddn: NULL,
      myargcount> 4?  & lerrmsg: NULL,
      myargcount> 5?  & lpferrals: NULL,
 - NULL / * & serverctrls * /,
 + myargcount> 6?  & lserverctrls: NULL,
      0);
   if (rc! = LDAP_SUCCESS) {
    php_error (E_WARNING, "% s (): не удается проанализировать результат:% s", get_active_function_name (TSRMLS_C), ldap_err2string (rc));
 @@ -1805,6 +1806,29 @@

   / * Обратный -> падение через * /
   switch (myargcount) {
 + случай 7:
 + zval_dtor (* serverctrls);
 +
 + if (lserverctrls! = NULL) {
 + array_init (* serverctrls);
 + ctrlp = lserverctrls;
 +
 + while (* ctrlp! = NULL) {
 + zval * ctrl_array;
 +
 + ctrl = * ctrlp;
 + MAKE_STD_ZVAL (ctrl_array);
 + array_init (ctrl_array);
 +
 + add_assoc_string (ctrl_array, "oid", ctrl-> ldctl_oid, 1);
 + add_assoc_bool (ctrl_array, "iscritical", ctrl-> ldctl_iscritical);
 + add_assoc_stringl (ctrl_array, "value", ctrl-> ldctl_value.bv_val,
 + ctrl-> ldctl_value.bv_len, 1);
 + add_next_index_zval (* serverctrls, ctrl_array);
 + ctrlp ++;
 +}
 + ldap_controls_free (lserverctrls);
 +}
    случай 6:
     zval_dtor (* рефералы);
     if (array_init (* рефералы) == FAILURE) { 

Фактически единственным оставшимся вариантом было бы изменение конфигурации Active Directory и повышение максимального предела результата. Соответствующий параметр называется MaxPageSize и может быть изменен с помощью ntdsutil.exe – см. «Как просмотреть и установить политику LDAP в Active Directory с помощью Ntdsutil.exe» .

EDIT (ссылка на COM):

Или вы можете пойти наоборот и использовать COM-подход через ADODB, как это предлагается в ссылке, предоставленной eykanal .

Поддержка вычисленных результатов была добавлена ​​в PHP 5.4.

Подробнее см. Ldap_control_paged_result .

Это не полный ответ, но этот парень смог это сделать. Однако я не понимаю, что он сделал.

Кстати, частичный ответ заключается в том, что вы можете получить «страницы» результатов. Из документации :

 resource ldap_search ( resource $link_identifier , string $base_dn , string $filter [, array $attributes [, int $attrsonly [, int $sizelimit [, int $timelimit [, int $deref ]]]]] ) ... 

sizelimit Позволяет ограничить количество выбранных записей. Установка этого значения в 0 означает отсутствие ограничений.

Примечание. Этот параметр НЕ может переопределять sizelimit на стороне сервера. Однако вы можете установить его ниже. Некоторые хосты сервера каталогов будут настроены на возврат не более чем заданного количества записей. Если это произойдет, сервер укажет, что он только вернул частичный набор результатов. Это также происходит, если вы используете этот параметр для ограничения количества выбранных записей.

Однако я не знаю, как указать, что вы хотите искать STARTING с определенной позиции. Т.е., после того, как вы получите свой первый 1000, я не знаю, как указать, что теперь вам нужен следующий 1000. Надеюсь, кто-то еще может вам помочь 🙂

Вот альтернатива (которая работает до PHP 5.4). Если у вас есть 10 000 записей, которые вам нужно получить, но ваш сервер AD только возвращает 5000 на страницу:

 $ldapSearch = ldap_search($ldapResource, $basedn, $filter, array('member;range=0-4999')); $ldapResults = ldap_get_entries($dn, $ldapSearch); $members = $ldapResults[0]['member;range=0-4999']; $ldapSearch = ldap_search($ldapResource, $basedn, $filter, array('member;range=5000-10000')); $ldapResults = ldap_get_entries($dn, $ldapSearch); $members = array_merge($members, $ldapResults[0]['member;range=5000-*']); 

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

ldap_control_paged_result используется для включения разбивки на страницы LDAP, отправив элемент управления разбиением на страницы. Функция ниже работала отлично в моем случае.

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