TigerZF
🌐Español

Capítulo 41. Zend_Ldap

Tabla de contenidos

41.1. Introducción
41.1.1. Teoría de funcionamiento
41.1.1.1. Canonicalización automática del nombre de usuario al enlazar
41.1.1.2. Canonicalización del nombre de cuenta
41.1.1.3. Autenticación multidominio y conmutación por error
41.2. Resumen de la API
41.2.1. Configuración / opciones
41.2.2. Referencia de la API
41.2.2.1. Zend_Ldap
41.2.2.1.1. Zend_Ldap_Collection
41.2.2.2. Zend_Ldap_Attribute
41.2.2.3. Zend_Ldap_Dn
41.2.2.4. Zend_Ldap_Filter
41.2.2.5. Zend_Ldap_Node
41.2.2.6. Zend_Ldap_Node_RootDse
41.2.2.6.1. OpenLDAP
41.2.2.6.2. ActiveDirectory
41.2.2.6.3. eDirectory
41.2.2.7. Zend_Ldap_Node_Schema
41.2.2.7.1. OpenLDAP
41.2.2.7.2. ActiveDirectory
41.2.2.8. Zend_Ldif_Encoder
41.3. Escenarios de uso
41.3.1. Escenarios de autenticación
41.3.1.1. OpenLDAP
41.3.1.2. ActiveDirectory
41.3.2. Operaciones CRUD básicas
41.3.2.1. Recuperar datos del LDAP
41.3.2.2. Añadir datos al LDAP
41.3.2.3. Eliminar del LDAP
41.3.2.4. Actualizar el LDAP
41.3.3. Operaciones extendidas
41.3.3.1. Copiar y mover entradas en el LDAP
41.4. Herramientas
41.4.1. Creación y modificación de cadenas DN
41.4.2. Uso de la API de filtros para crear filtros de búsqueda
41.4.3. Modificar entradas LDAP usando la API de Attribute
41.5. Acceso orientado a objetos al árbol LDAP usando Zend_Ldap_Node
41.5.1. Operaciones CRUD básicas
41.5.1.1. Recuperar datos del LDAP
41.5.1.1.1. Obtener un nodo por su DN
41.5.1.1.2. Buscar en el subárbol de un nodo
41.5.1.2. Añadir un nuevo nodo al LDAP
41.5.1.3. Eliminar un nodo del LDAP
41.5.1.4. Actualizar un nodo en el LDAP
41.5.2. Operaciones extendidas
41.5.2.1. Copiar y mover nodos en el LDAP
41.5.3. Recorrido del árbol
41.6. Obtener información del servidor LDAP
41.6.1. RootDSE
41.6.2. Exploración de esquemas
41.6.2.1. OpenLDAP
41.6.2.2. ActiveDirectory
41.7. Serializar datos LDAP hacia y desde LDIF
41.7.1. Serializar una entrada LDAP a LDIF
41.7.2. Deserializar una cadena LDIF en una entrada LDAP

41.1. Introducción

Zend_Ldap es una clase para realizar operaciones LDAP que incluyen, entre otras, el enlace (binding), la búsqueda y la modificación de entradas en un directorio LDAP.

41.1.1. Teoría de funcionamiento

Este componente consiste actualmente en la clase principal Zend_Ldap, que representa conceptualmente un enlace a un único servidor LDAP y permite ejecutar operaciones contra un servidor LDAP como OpenLDAP o servidores ActiveDirectory (AD). Los parámetros para el enlace pueden proporcionarse explícitamente o en forma de un array de opciones. Zend_Ldap_Node proporciona una interfaz orientada a objetos para nodos LDAP individuales y puede usarse para formar la base de una interfaz similar a un registro activo para un modelo de dominio basado en LDAP.

El componente proporciona varias clases auxiliares para realizar operaciones en entradas LDAP (Zend_Ldap_Attribute) tales como establecer y recuperar atributos (valores de fecha, contraseñas, valores booleanos, ...), para crear y modificar cadenas de filtro LDAP (Zend_Ldap_Filter) y para manipular nombres distinguidos (DN) de LDAP (Zend_Ldap_Dn).

Adicionalmente el componente abstrae la exploración de esquemas LDAP para servidores OpenLDAP y ActiveDirectoy Zend_Ldap_Node_Schema y la recuperación de información del servidor para servidores OpenLDAP, ActiveDirectory y Novell eDirectory (Zend_Ldap_Node_RootDse).

El uso de la clase Zend_Ldap depende del tipo de servidor LDAP y se resume mejor con algunos ejemplos sencillos.

Si está usando OpenLDAP, un ejemplo sencillo se ve como sigue (tenga en cuenta que la opción bindRequiresDn es importante si no está usando AD):

$options = array(
    'host'              => 's0.foo.net',
    'username'          => 'CN=user1,DC=foo,DC=net',
    'password'          => 'pass1',
    'bindRequiresDn'    => true,
    'accountDomainName' => 'foo.net',
    'baseDn'            => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('abaker',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";

Si está usando Microsoft AD un ejemplo sencillo es:

$options = array(
    'host'                   => 'dc1.w.net',
    'useStartTls'            => true,
    'username'               => 'user1@w.net',
    'password'               => 'pass1',
    'accountDomainName'      => 'w.net',
    'accountDomainNameShort' => 'W',
    'baseDn'                 => 'CN=Users,DC=w,DC=net',
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('bcarter',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";

Tenga en cuenta que usamos el método getCanonicalAccountName() para recuperar el DN de la cuenta aquí solamente porque es lo que ejercita la mayor parte del poco código actualmente presente en esta clase.

41.1.1.1. Canonicalización automática del nombre de usuario al enlazar

Si se llama a bind() con un nombre de usuario que no es un DN pero bindRequiresDN es TRUE y no se proporcionó ningún nombre de usuario en forma de DN como opción, el enlace fallará. Sin embargo, si se proporciona un nombre de usuario en forma de DN en el array de opciones, Zend_Ldap primero se enlazará con ese nombre de usuario, recuperará el DN de la cuenta para el nombre de usuario proporcionado a bind() y luego se volverá a enlazar con ese DN.

Este comportamiento es crítico para Zend_Auth_Adapter_Ldap, que pasa el nombre de usuario proporcionado por el usuario directamente a bind().

El siguiente ejemplo ilustra cómo el nombre de usuario que no es un DN 'abaker' puede usarse con bind():

$options = array(
        'host'              => 's0.foo.net',
        'username'          => 'CN=user1,DC=foo,DC=net',
        'password'          => 'pass1',
        'bindRequiresDn'    => true,
        'accountDomainName' => 'foo.net',
        'baseDn'            => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$ldap->bind('abaker', 'moonbike55');
$acctname = $ldap->getCanonicalAccountName('abaker',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";

La llamada a bind() en este ejemplo ve que el nombre de usuario 'abaker' no está en forma de DN, encuentra que bindRequiresDn es TRUE, usa 'CN=user1,DC=foo,DC=net' y 'pass1' para enlazar, recupera el DN de 'abaker', desenlaza y luego vuelve a enlazar con el recién descubierto 'CN=Alice Baker,OU=Sales,DC=foo,DC=net'.

41.1.1.2. Canonicalización del nombre de cuenta

Las opciones accountDomainName y accountDomainNameShort se usan para dos propósitos: (1) facilitan la capacidad de autenticación multidominio y conmutación por error, y (2) también se usan para canonicalizar los nombres de usuario. Específicamente, los nombres se canonicalizan a la forma especificada por la opción accountCanonicalForm. Esta opción puede ser uno de los siguientes valores:

Tabla 41.1. Opciones para accountCanonicalForm

Nombre Valor Ejemplo
ACCTNAME_FORM_DN 1 CN=Alice Baker,CN=Users,DC=example,DC=com
ACCTNAME_FORM_USERNAME 2 abaker
ACCTNAME_FORM_BACKSLASH 3 EXAMPLE\abaker
ACCTNAME_FORM_PRINCIPAL 4 abaker@example.com

La canonicalización predeterminada depende de qué opciones de nombre de dominio de cuenta se hayan proporcionado. Si se proporcionó accountDomainNameShort, el valor predeterminado de accountCanonicalForm es ACCTNAME_FORM_BACKSLASH. De lo contrario, si se proporcionó accountDomainName, el valor predeterminado es ACCTNAME_FORM_PRINCIPAL.

La canonicalización del nombre de cuenta garantiza que la cadena usada para identificar una cuenta sea consistente independientemente de lo que se haya proporcionado a bind(). Por ejemplo, si el usuario proporciona un nombre de cuenta de abaker@example.com o simplemente abaker y accountCanonicalForm se establece en 3, el nombre canonicalizado resultante sería EXAMPLE\abaker.

41.1.1.3. Autenticación multidominio y conmutación por error

El componente Zend_Ldap por sí mismo no intenta autenticarse con múltiples servidores. Sin embargo, Zend_Ldap está diseñado específicamente para manejar este escenario con elegancia. La técnica requerida es simplemente iterar sobre un array de arrays de opciones de servidor e intentar enlazar con cada servidor. Como se describió anteriormente, bind() canonicalizará automáticamente cada nombre, por lo que no importa si el usuario pasa abaker@foo.net o W\bcarter o cdavis: el método bind() solo tendrá éxito si las credenciales se usaron correctamente en el enlace.

Considere el siguiente ejemplo que ilustra la técnica requerida para implementar autenticación multidominio y conmutación por error:

$acctname = 'W\\user2';
$password = 'pass2';

$multiOptions = array(
    'server1' => array(
        'host'                   => 's0.foo.net',
        'username'               => 'CN=user1,DC=foo,DC=net',
        'password'               => 'pass1',
        'bindRequiresDn'         => true,
        'accountDomainName'      => 'foo.net',
        'accountDomainNameShort' => 'FOO',
        'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
        'baseDn'                 => 'OU=Sales,DC=foo,DC=net',
    ),
    'server2' => array(
        'host'                   => 'dc1.w.net',
        'useSsl'                 => true,
        'username'               => 'user1@w.net',
        'password'               => 'pass1',
        'accountDomainName'      => 'w.net',
        'accountDomainNameShort' => 'W',
        'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
        'baseDn'                 => 'CN=Users,DC=w,DC=net',
    ),
);

$ldap = new Zend_Ldap();

foreach ($multiOptions as $name => $options) {

    echo "Trying to bind using server options for '$name'\n";

    $ldap->setOptions($options);
    try {
        $ldap->bind($acctname, $password);
        $acctname = $ldap->getCanonicalAccountName($acctname);
        echo "SUCCESS: authenticated $acctname\n";
        return;
    } catch (Zend_Ldap_Exception $zle) {
        echo '  ' . $zle->getMessage() . "\n";
        if ($zle->getCode() === Zend_Ldap_Exception::LDAP_X_DOMAIN_MISMATCH) {
            continue;
        }
    }
}

Si el enlace falla por cualquier motivo, se intenta con el siguiente conjunto de opciones de servidor.

La llamada a getCanonicalAccountName() obtiene el nombre de cuenta canónico que la aplicación presumiblemente usaría para asociar datos, como las preferencias. El accountCanonicalForm = 4 en todas las opciones de servidor garantiza que la forma canónica sea consistente independientemente de qué servidor se haya usado finalmente.

La excepción especial LDAP_X_DOMAIN_MISMATCH ocurre cuando se proporcionó un nombre de cuenta con un componente de dominio (por ejemplo, abaker@foo.net o FOO\abaker y no solamente abaker) pero el componente de dominio no coincidió con ninguno de los dominios en las opciones de servidor actualmente seleccionadas. Esta excepción indica que el servidor no es una autoridad para la cuenta. En este caso, el enlace no se realizará, eliminando así la comunicación innecesaria con el servidor. Tenga en cuenta que la instrucción continue no tiene efecto en este ejemplo, pero en la práctica, por motivos de manejo de errores y depuración, probablemente querrá comprobar LDAP_X_DOMAIN_MISMATCH así como LDAP_NO_SUCH_OBJECT y LDAP_INVALID_CREDENTIALS.

El código anterior es muy similar al código usado dentro de Zend_Auth_Adapter_Ldap. De hecho, recomendamos que simplemente use ese adaptador de autenticación para la autenticación LDAP multidominio + conmutación por error (o copie el código).