Me ha sorprendido el mismo problema al desarrollar Zend_Ldap para el Zend Framework. Trataré de explicar cuál es el problema real, pero para abreviar: hasta PHP 5.4, no fue posible usar resultados paginados de un Active Directory con una versión de PHP no enmascarada (ext/ldap
) debido a las limitaciones en exactamente esto extensión.
Tratemos de resolver todo ... Microsoft Active Directory usa el llamado control de servidor para lograr la paginación de resultados del lado del servidor. Este control se describe en RFC 2696 "LDAP Control Extension for Simple Paged Results Manipulation".
ext/php
ofrece un acceso a extensiones de control LDAP a través de su y la opción ldap_set_option()
LDAP_OPT_SERVER_CONTROLS
y LDAP_OPT_CLIENT_CONTROLS
respectivamente. Para establecer el control paginado necesita el control-oid, que es 1.2.840.113556.1.4.319
, y necesitamos saber cómo codificar el valor de control (esto se describe en el RFC).El valor es una cadena de octetos envolver la versión REC codificada de la siguiente secuencia (copiado del RFC):
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
para que podamos establecer el control de servidor apropiado antes de ejecutar la consulta 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
);
Esto nos permite enviar una consulta paginada al servidor LDAP/AD. ¿Pero cómo sabemos si hay más páginas para seguir y cómo especificamos con qué valor de control tenemos que enviar nuestra próxima consulta?
Aquí es donde nos trabamos ... El servidor responde con un conjunto de resultados que incluye la información de paginación requerida, pero PHP no tiene un método para recuperar exactamente esta información del conjunto de resultados. PHP proporciona un contenedor para la función LDAP API ldap_parse_result()
pero el último parámetro requerido serverctrlsp
no está expuesto a la función PHP, por lo que no hay forma de recuperar la información requerida. Un bug report se ha presentado para este problema, pero no ha habido ninguna respuesta desde el año 2005. Si la función ldap_parse_result()
proporciona el parámetro requerido, utilizando los resultados paginados funcionaría como
$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);
}
}
Como se puede ver hay una sola línea de código (*)
eso hace que todo el asunto sea inútil. En el camino a través de la escasa información sobre este tema encontré un parche contra el PHP 4.3.10 ext/ldap
por Iñaki Arenaza, pero tampoco lo intenté ni sé si el parche se puede aplicar en un PHP5 ext/ldap
. El parche se extiende ldap_parse_result()
para exponer el séptimo parámetro para 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) {
En realidad, la única opción que queda sería cambiar la configuración de Active Directory y elevar el límite máximo resultado. La opción relevante se llama MaxPageSize
y puede modificarse usando ntdsutil.exe
- consulte "How to view and set LDAP policy in Active Directory by using Ntdsutil.exe".
EDITAR (referencia a COM):
O puede ir al revés y utilizar el COM-enfoque a través de ADODB como se sugiere en la link proporcionada por eykanal.
¡Respuesta! Muchas gracias, ¡esto me ayudó mucho! – Christian
¿Cuál es el estado de esto ahora que PHP 5.4 admite resultados de LDAP paginado? – Squazic
@Squazic: vea la respuesta http://stackoverflow.com/a/10140166/11354 abajo. Parece factible a partir de PHP 5.4. –