Zend_Auth_Adapter_Ldap permite la autenticación de aplicaciones web
con servicios LDAP. Sus características incluyen la canonicalización
de nombres de usuario y de dominio, autenticación multi-dominio y capacidades de conmutación
por error (failover). Ha sido probado con
Microsoft
Active Directory y OpenLDAP,
pero también debería funcionar con otros proveedores de servicios LDAP.
Esta documentación incluye una guía para usar
Zend_Auth_Adapter_Ldap, una exploración de su
API, un esquema de las diversas opciones disponibles, información
de diagnóstico para solucionar problemas de autenticación, y opciones de ejemplo tanto
para servidores Active Directory como OpenLDAP.
Para incorporar rápidamente la autenticación con Zend_Auth_Adapter_Ldap en su
aplicación, incluso si no está usando Zend_Controller,
la parte esencial de su código debería tener un aspecto similar al siguiente:
$username = $this->_request->getParam('username');
$password = $this->_request->getParam('password');
$auth = Zend_Auth::getInstance();
$config = new Zend_Config_Ini('../application/config/config.ini',
'production');
$log_path = $config->ldap->log_path;
$options = $config->ldap->toArray();
unset($options['log_path']);
$adapter = new Zend_Auth_Adapter_Ldap($options, $username,
$password);
$result = $auth->authenticate($adapter);
if ($log_path) {
$messages = $result->getMessages();
$logger = new Zend_Log();
$logger->addWriter(new Zend_Log_Writer_Stream($log_path));
$filter = new Zend_Log_Filter_Priority(Zend_Log::DEBUG);
$logger->addFilter($filter);
foreach ($messages as $i => $message) {
if ($i-- > 1) { // $messages[2] and up are log messages
$message = str_replace("\n", "\n ", $message);
$logger->log("Ldap: $i: $message", Zend_Log::DEBUG);
}
}
}
Por supuesto, el código de registro (logging) es opcional, pero es muy recomendable usar
un registrador (logger). Zend_Auth_Adapter_Ldap registrará prácticamente
toda la información que cualquiera pudiera desear en $messages (más abajo), lo cual
es una característica interesante en sí misma para algo que tiene fama de ser notoriamente difícil
de depurar.
El código de Zend_Config_Ini se utiliza arriba para cargar las
opciones del adaptador. También es opcional. Un array normal funcionaría igual de bien. A continuación se
muestra un archivo de ejemplo application/config/config.ini que tiene opciones para
dos servidores separados. Con varios conjuntos de opciones de servidor, el adaptador intentará cada uno, en
orden, hasta que las credenciales se autentiquen con éxito. Los nombres de los servidores
(por ejemplo, 'server1' y 'server2') son en gran medida arbitrarios. Para más detalles sobre el array
de opciones, consulte la sección Opciones del servidor más abajo. Tenga en cuenta que
Zend_Config_Ini requiere que cualquier valor con caracteres "igual"
(=) deba estar entrecomillado (como los DN mostrados abajo).
[production] ldap.log_path = /tmp/ldap.log ; Typical options for OpenLDAP ldap.server1.host = s0.foo.net ldap.server1.accountDomainName = foo.net ldap.server1.accountDomainNameShort = FOO ldap.server1.accountCanonicalForm = 3 ldap.server1.username = "CN=user1,DC=foo,DC=net" ldap.server1.password = pass1 ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net" ldap.server1.bindRequiresDn = true ; Typical options for Active Directory ldap.server2.host = dc1.w.net ldap.server2.useStartTls = true ldap.server2.accountDomainName = w.net ldap.server2.accountDomainNameShort = W ldap.server2.accountCanonicalForm = 3 ldap.server2.baseDn = "CN=Users,DC=w,DC=net"
La configuración anterior indicará a Zend_Auth_Adapter_Ldap que
intente autenticar a los usuarios primero con el servidor OpenLDAP s0.foo.net.
Si la autenticación falla por cualquier motivo, se intentará con el servidor AD
dc1.w.net.
Con servidores en dominios diferentes, esta configuración ilustra la autenticación multi-dominio. También puede tener varios servidores en el mismo dominio para proporcionar redundancia.
Tenga en cuenta que en este caso, aunque OpenLDAP no necesita el nombre de dominio corto estilo NetBIOS utilizado por Windows, lo proporcionamos aquí con fines de canonicalización de nombres (descrito en la sección Canonicalización de nombres de usuario más abajo).
El constructor de Zend_Auth_Adapter_Ldap acepta tres parámetros.
El parámetro $options es obligatorio y debe ser un array que contenga
uno o más conjuntos de opciones. Tenga en cuenta que es un array de arrays de
opciones de Zend_Ldap. Incluso si
va a usar un único servidor LDAP, las opciones deben seguir estando dentro de
otro array.
A continuación se muestra la salida de print_r()
de un ejemplo de parámetro de opciones que contiene dos conjuntos de opciones de servidor para
servidores LDAP s0.foo.net y
dc1.w.net (las mismas opciones que la representación
INI anterior):
Array
(
[server2] => Array
(
[host] => dc1.w.net
[useStartTls] => 1
[accountDomainName] => w.net
[accountDomainNameShort] => W
[accountCanonicalForm] => 3
[baseDn] => CN=Users,DC=w,DC=net
)
[server1] => Array
(
[host] => s0.foo.net
[accountDomainName] => foo.net
[accountDomainNameShort] => FOO
[accountCanonicalForm] => 3
[username] => CN=user1,DC=foo,DC=net
[password] => pass1
[baseDn] => OU=Sales,DC=foo,DC=net
[bindRequiresDn] => 1
)
)
La información proporcionada en cada conjunto de opciones anterior es diferente principalmente porque AD no requiere que un nombre de usuario esté en forma de DN al enlazar (bind) (consulte la opción bindRequiresDn en la sección Opciones del servidor más abajo), lo que significa que podemos omitir varias opciones asociadas con la obtención del DN de un nombre de usuario que se está autenticando.
![]() |
¿Qué es un Distinguished Name? |
|---|---|
Un DN o "nombre distintivo" (distinguished name) es una cadena que representa la ruta a un objeto dentro del directorio LDAP. Cada componente separado por comas es un atributo y valor que representa un nodo. Los componentes se evalúan en orden inverso. Por ejemplo, la cuenta de usuario CN=Bob Carter,CN=Users,DC=w,DC=net se encuentra directamente dentro del contenedor CN=Users,DC=w,DC=net. Esta estructura se explora mejor con un navegador LDAP como el complemento (snap-in) MMC de ADSI Edit para Active Directory o phpLDAPadmin. |
Los nombres de los servidores (por ejemplo, 'server1' y 'server2' mostrados arriba) son en gran medida
arbitrarios, pero por el bien del uso de Zend_Config, los identificadores deben
estar presentes (en lugar de ser índices numéricos) y no deben contener ningún carácter especial
usado por los formatos de archivo asociados (por ejemplo, el separador de propiedades '.'
de INI, '&' para las
referencias de entidad XML, etc).
Con varios conjuntos de opciones de servidor, el adaptador puede autenticar usuarios en múltiples dominios y proporcionar conmutación por error (failover) de forma que si un servidor no está disponible, se consulte otro.
![]() |
Los detalles sórdidos: ¿qué ocurre en el método authenticate? |
|---|---|
Cuando se llama al método |
Los parámetros de nombre de usuario y contraseña del constructor de
Zend_Auth_Adapter_Ldap representan las credenciales
que se están autenticando (es decir, las credenciales proporcionadas por el usuario a través de su
formulario de inicio de sesión HTML). Alternativamente, también se pueden establecer con los
métodos setUsername() y setPassword().
Cada conjunto de opciones de servidor en el contexto de
Zend_Auth_Adapter_Ldap consta de las siguientes
opciones, que se pasan, en gran medida sin modificar, a
Zend_Ldap::setOptions():
Tabla 15.2. Opciones del servidor
| Nombre | Descripción |
|---|---|
| host | El nombre de host del servidor LDAP que representan estas opciones. Esta opción es obligatoria. |
| port |
El puerto en el que escucha el servidor LDAP. Si
useSsl es TRUE, el valor
por defecto de port es 636. Si useSsl
es FALSE, el valor por defecto de port
es 389.
|
| useStartTls |
Indica si el cliente LDAP debe usar
transporte cifrado TLS (también conocido como
SSLv2). Un valor de TRUE es muy
recomendable en entornos de producción para evitar que las contraseñas se
transmitan en texto claro. El valor por defecto es FALSE, ya que
los servidores frecuentemente requieren que se instale un certificado por
separado después de la instalación. Las opciones useSsl y
useStartTls son mutuamente excluyentes. Se debería
favorecer la opción useStartTls sobre
useSsl, pero no todos los servidores soportan este
mecanismo más reciente.
|
| useSsl | Indica si el cliente LDAP debe usar transporte cifrado SSL. Las opciones useSsl y useStartTls son mutuamente excluyentes, pero se debería favorecer useStartTls si el servidor y la biblioteca cliente LDAP lo soportan. Este valor también cambia el valor por defecto de port (consulte la descripción de port arriba). |
| username |
El DN de la cuenta usada para realizar búsquedas de DN de cuentas.
Los servidores LDAP que requieren que el nombre de usuario esté en forma
de DN al realizar el "bind" requieren esta opción. Es decir, si
bindRequiresDn es TRUE, esta
opción es obligatoria. Esta cuenta no necesita ser una cuenta con privilegios;
una cuenta con acceso de solo lectura a los objetos bajo
baseDn es todo lo necesario (y lo preferible según el
Principio del Mínimo Privilegio).
|
| password | La contraseña de la cuenta usada para realizar búsquedas de DN de cuentas. Si esta opción no se proporciona, el cliente LDAP intentará un "bind anónimo" al realizar las búsquedas de DN de cuentas. |
| bindRequiresDn |
Algunos servidores LDAP requieren que el nombre de usuario usado
para el bind esté en forma de DN como
CN=Alice Baker,OU=Sales,DC=foo,DC=net (básicamente
todos los servidores excepto AD). Si esta opción es
TRUE, esto indica a
Zend_Ldap que recupere automáticamente el DN
correspondiente al nombre de usuario que se está autenticando, si aún no está
en forma de DN, y luego vuelva a enlazar (bind) con el DN correcto. El valor por defecto es
FALSE. Actualmente solo se sabe que Microsoft Active Directory
Server (ADS) no requiere que los
nombres de usuario estén en forma de DN al hacer bind, por lo que esta
opción puede ser FALSE con AD (y debería serlo, ya que
recuperar el DN requiere un viaje de ida y vuelta adicional al servidor). En caso
contrario, esta opción debe establecerse en TRUE (por ejemplo,
para OpenLDAP). Esta opción también controla el valor por defecto de
acountFilterFormat usado al buscar
cuentas. Consulte la opción accountFilterFormat.
|
| baseDn |
El DN bajo el cual se ubican todas las cuentas que se autentican. Esta
opción es obligatoria. Si no está seguro sobre el valor correcto de
baseDn, debería ser suficiente derivarlo
del dominio DNS del usuario usando
componentes DC=. Por ejemplo, si el nombre principal del usuario
es alice@foo.net, un
baseDn de DC=foo,DC=net
debería funcionar. No obstante, una ubicación más precisa (por ejemplo,
OU=Sales,DC=foo,DC=net) será más eficiente.
|
| accountCanonicalForm |
Un valor de 2, 3 o 4 que indica la forma a la que deben canonicalizarse
los nombres de cuenta tras una autenticación exitosa. Los valores son los
siguientes: 2 para nombres estilo nombre de usuario tradicional (por ejemplo,
alice), 3 para nombres estilo barra invertida (por ejemplo,
FOO\alice) o 4 para nombres de usuario estilo principal
(por ejemplo, alice@foo.net). El valor por defecto es 4
(por ejemplo, alice@foo.net). Por ejemplo, con un valor
de 3, la identidad devuelta por
Zend_Auth_Result::getIdentity() (y
Zend_Auth::getIdentity(), si se usó
Zend_Auth) será siempre
FOO\alice, independientemente de la forma que Alice haya
proporcionado, ya sea alice,
alice@foo.net, FOO\alice,
FoO\aLicE, foo.net\alice,
etc. Consulte la sección Canonicalización de nombres de cuenta
en la documentación de Zend_Ldap para más detalles. Tenga en cuenta
que al usar varios conjuntos de opciones de servidor se recomienda, aunque
no es obligatorio, que se use el mismo accountCanonicalForm
con todas las opciones de servidor, de modo que los nombres de usuario resultantes
se canonicalicen siempre a la misma forma (por ejemplo, si canonicaliza a
EXAMPLE\username con un servidor AD pero a
username@example.com con un servidor OpenLDAP, eso
puede resultar incómodo para la lógica de alto nivel de la aplicación).
|
| accountDomainName |
El nombre de dominio FQDN para el cual el servidor
LDAP de destino es una autoridad (por ejemplo,
example.com). Esta opción se usa para canonicalizar
nombres de forma que el nombre de usuario proporcionado por el usuario pueda convertirse
según sea necesario para el bind. También se usa para determinar si el servidor es una
autoridad para el nombre de usuario proporcionado (por ejemplo, si
accountDomainName es foo.net
y el usuario proporciona bob@bar.net, no se consultará
el servidor y se producirá un fallo). Esta opción no es
obligatoria, pero si no se proporciona, no se admiten los nombres de usuario en forma
de nombre principal (por ejemplo, alice@foo.net). Se
recomienda encarecidamente proporcionar esta opción, ya que hay muchos
casos de uso que requieren generar la forma de nombre principal.
|
| accountDomainNameShort |
El dominio 'corto' para el cual el servidor LDAP de destino
es una autoridad (por ejemplo, FOO). Tenga en cuenta que existe
una correspondencia 1:1 entre accountDomainName y
accountDomainNameShort. Esta opción debería usarse
para especificar el nombre de dominio NetBIOS de redes Windows, pero también puede
ser usada por servidores no AD (por ejemplo, por consistencia cuando hay varios conjuntos de
opciones de servidor con el accountCanonicalForm estilo
barra invertida). Esta opción no es obligatoria, pero si no se proporciona, no se
admiten los nombres de usuario en forma de barra invertida (por ejemplo,
FOO\alice).
|
| accountFilterFormat |
El filtro de búsqueda LDAP usado para buscar cuentas.
Esta cadena es una expresión estilo printf()
que debe contener un '%s' para
acomodar el nombre de usuario. El valor por defecto es
'(&(objectClass=user)(sAMAccountName=%s))',
a menos que bindRequiresDn esté establecido en
TRUE, en cuyo caso el valor por defecto es
'(&(objectClass=posixAccount)(uid=%s))'. Por
ejemplo, si por alguna razón quisiera usar
bindRequiresDn = true con AD, necesitaría
establecer accountFilterFormat =
'(&(objectClass=user)(sAMAccountName=%s))'.
|
| optReferrals |
Si se establece en TRUE, esta opción indica al
cliente LDAP que se deben seguir las referencias (referrals). El
valor por defecto es FALSE.
|
![]() |
Nota |
|---|---|
Si habilita useStartTls = |
Zend_Auth_Adapter_Ldap recopila información de depuración dentro de su
método authenticate(). Esta información se almacena en el
objeto Zend_Auth_Result como mensajes. El array devuelto por
Zend_Auth_Result::getMessages() se describe a continuación
Tabla 15.3. Mensajes de depuración
| Índice del array de mensajes | Descripción |
|---|---|
| Índice 0 | Un mensaje genérico y amigable para el usuario, adecuado para mostrarse a los usuarios (por ejemplo, "Credenciales no válidas"). Si la autenticación tiene éxito, esta cadena está vacía. |
| Índice 1 | Un mensaje de error más detallado que no es adecuado para mostrarse a los usuarios pero que debería registrarse en beneficio de los operadores del servidor. Si la autenticación tiene éxito, esta cadena está vacía. |
| Índices 2 y superiores | Todos los mensajes de registro en orden, empezando por el índice 2. |
En la práctica, el índice 0 debería mostrarse al usuario (por ejemplo, usando el ayudante FlashMessenger), el índice 1 debería registrarse y, si se está recopilando información de depuración, los índices 2 y superiores también podrían registrarse (aunque el mensaje final siempre incluye la cadena del índice 1).
Para ADS, las siguientes opciones son destacables:
Tabla 15.4. Opciones para Active Directory
| Nombre | Notas adicionales |
|---|---|
| host | Como con todos los servidores, esta opción es obligatoria. |
| useStartTls |
Por motivos de seguridad, esto debería ser TRUE
si el servidor tiene instalado el certificado necesario.
|
| useSsl | Posiblemente se use como alternativa a useStartTls (ver arriba). |
| baseDn | Como con todos los servidores, esta opción es obligatoria. Por defecto AD coloca todas las cuentas de usuario bajo el contenedor Users (por ejemplo, CN=Users,DC=foo,DC=net), pero el valor por defecto no es habitual en organizaciones grandes. Pregunte a su administrador de AD cuál sería el mejor DN para las cuentas de su aplicación. |
| accountCanonicalForm |
Casi con toda seguridad querrá que esto sea 3 para nombres estilo barra
invertida (por ejemplo, FOO\alice), que son los más familiares
para los usuarios de Windows. No debería usar la
forma no cualificada 2 (por ejemplo, alice), ya que esto puede
conceder acceso a su aplicación a usuarios con el mismo nombre de usuario en
otros dominios de confianza (por ejemplo, BAR\alice y
FOO\alice se tratarán como el mismo usuario).
(Vea también la nota más abajo.)
|
| accountDomainName | Esto es obligatorio con AD a menos que se use accountCanonicalForm 2, lo cual, de nuevo, se desaconseja. |
| accountDomainNameShort | El nombre NetBIOS del dominio en el que están los usuarios y para el cual el servidor AD es una autoridad. Esto es obligatorio si se usa el accountCanonicalForm estilo barra invertida. |
![]() |
Nota |
|---|---|
Técnicamente no debería haber peligro de autenticación cruzada accidental entre dominios
con la implementación actual de |
Para OpenLDAP o un servidor LDAP genérico que use un esquema típico estilo posixAccount, las siguientes opciones son destacables:
Tabla 15.5. Opciones para OpenLDAP
| Nombre | Notas adicionales |
|---|---|
| host | Como con todos los servidores, esta opción es obligatoria. |
| useStartTls |
Por motivos de seguridad, esto debería ser TRUE
si el servidor tiene instalado el certificado necesario.
|
| useSsl | Posiblemente se use como alternativa a useStartTls (ver arriba). |
| username | Obligatorio y debe ser un DN, ya que OpenLDAP requiere que los nombres de usuario estén en forma de DN al realizar un bind. Intente usar una cuenta sin privilegios. |
| password | La contraseña correspondiente al nombre de usuario anterior, pero puede omitirse si el servidor LDAP permite un bind anónimo para consultar cuentas de usuario. |
| bindRequiresDn |
Obligatorio y debe ser TRUE, ya que OpenLDAP
requiere que los nombres de usuario estén en forma de DN al realizar un bind.
|
| baseDn | Como con todos los servidores, esta opción es obligatoria e indica el DN bajo el cual se ubican todas las cuentas que se autentican. |
| accountCanonicalForm |
Opcional, pero el valor por defecto es 4 (nombres estilo principal como
alice@foo.net), lo cual puede no ser ideal si sus
usuarios están acostumbrados a nombres estilo barra invertida (por ejemplo,
FOO\alice). Para nombres estilo barra invertida use el
valor 3.
|
| accountDomainName | Obligatorio a menos que esté usando accountCanonicalForm 2, lo cual no se recomienda. |
| accountDomainNameShort |
Si AD tampoco se está usando, este valor no es obligatorio.
De lo contrario, si se usa accountCanonicalForm 3,
esta opción es obligatoria y debería ser un nombre corto que se corresponda
adecuadamente con accountDomainName (por ejemplo, si
su accountDomainName es
foo.net, un buen valor de
accountDomainNameShort podría ser
FOO).
|
![[Note]](images/note.png)