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

Многие основанные на агенте пользователя ответы на этой странице не слишком надежны, потому что Opera часто отождествляет себя со строкой агента пользователя, содержащей "MSIE 6.0", такой как:

Mozilla/4.0 (compatible; MSIE 6.0; X11; Linux i686; en) Opera 9.51

Это влияет на все версии Opera 5 - 9 и даже Opera 10 и может быть включено или выключено из Opera. Видят эту страницу .

общий подход, который я видел, должен протестировать на "MSIE" и против "Opera". Например,

if (preg_match('/\bmsie 6/i', $ua) && !preg_match('/\bopera/i', $ua))
  echo "We have IE6!";

12
задан Bart 24 September 2009 в 17:25
поделиться

2 ответа

I've been struck by the same problem while developing Zend_Ldap for the Zend Framework. I'll try to explain what the real problem is, but to make it short: until PHP 5.4, it wasn't possible to use paged results from an Active Directory with an unpatched PHP (ext/ldap) version due to limitations in exactly this extension.

Let's try to unravel the whole thing... Microsoft Active Directory uses a so called server control to accomplish server-side result paging. This control ist described in RFC 2696 "LDAP Control Extension for Simple Paged Results Manipulation" .

ext/php offers an access to LDAP control extensions via its ldap_set_option() and the LDAP_OPT_SERVER_CONTROLS and LDAP_OPT_CLIENT_CONTROLS option respectively. To set the paged control you do need the control-oid, which is 1.2.840.113556.1.4.319, and we need to know how to encode the control-value (this is described in the RFC). The value is an octet string wrapping the BER-encoded version of the following SEQUENCE (copied from the RFC):

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

So we can set the appropriate server control prior to executing the LDAP query:

$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
);

This allows us to send a paged query to the LDAP/AD server. But how do we know if there are more pages to follow and how do we specify with which control-value we have to send our next query?

This is where we're getting stuck... The server responds with a result set that includes the required paging information but PHP lacks a method to retrieve exactly this information from the result set. PHP provides a wrapper for the LDAP API function ldap_parse_result() but the required last parameter serverctrlsp is not exposed to the PHP function, so there is no way to retrieve the required information. A bug report has been filed for this issue but there has been no response since 2005. If the ldap_parse_result() function provided the required parameter, using paged results would work like

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

As you see there is a single line of code (*) that renders the whole thing useless. On my way though the sparse information on this subject I found a patch against the PHP 4.3.10 ext/ldap by Iñaki Arenaza but neither did I try it nor do I know if the patch can be applied on a PHP5 ext/ldap. The patch extends ldap_parse_result() to expose the 7th parameter to 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 @@
    Extract information from result */
 PHP_FUNCTION(ldap_parse_result) 
 {
- pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals;
+ pval **link, **result, **errcode, **matcheddn, **errmsg, **referrals, **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 ? &lreferrals : NULL,
-    NULL /* &serverctrls */,
+    myargcount > 6 ? &lserverctrls : NULL,
     0 );
  if (rc != LDAP_SUCCESS ) {
   php_error(E_WARNING, "%s(): Unable to parse result: %s", get_active_function_name(TSRMLS_C), ldap_err2string(rc));
@@ -1805,6 +1806,29 @@

  /* Reverse -> fall through */
  switch(myargcount) {
+  case 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);
+   }
   case 6 :
    zval_dtor(*referrals);
    if (array_init(*referrals) == FAILURE) {

Actually the only option left would be to change the Active Directory configuration and raise the maximum result limit. The relevant option is called MaxPageSize and can be altered by using ntdsutil.exe - please see "How to view and set LDAP policy in Active Directory by using Ntdsutil.exe".

EDIT (reference to COM):

Or you can go the other way round and use the COM-approach via ADODB as suggested in the link provided by eykanal.

15
ответ дан 2 December 2019 в 06:26
поделиться

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

Между прочим, частичный ответ заключается в том, что вы МОЖЕТЕ получить "страницы" результатов. Из документации :

 ресурс ldap_search (ресурс $ link_identifier, строка $ base_dn,
 строка $ filter [, массив $ attributes [, int $ attrsonly [, int $ sizelimit [, 
 int $ timelimit [, int $ deref]]]]])
...

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

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

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

3
ответ дан 2 December 2019 в 06:26
поделиться
Другие вопросы по тегам:

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