Este artículo explica cómo autenticar contra un servidor LDAP desde PHP. De yapa explica cómo enviar datos de un formulario a través del método POST de HTTP utilizando JavaScript puro.



Luego de montar mi servidor LDAP con OpenLDAP, se me ocurrió implementar una especie de microservicio de autenticación contra LDAP sobre HTTP, utilizando PHP. La idea detrás de este script PHP es recibir los datos de usuario y contraseña a través del POST y contestar si se autentica con éxito en el servidor LDAP, utilizando el formato JSON para la respuesta. Este microservicio permitiría autenticar de manera simple contra el directorio desde cualquier aplicación Web.

Contando con un servidor Web Apache instalado y funcionando, el primer paso consiste en configurar el nuevo alias para hospedar el microservicio. En este ejemplo "/auth":

# nano /etc/apache2/sites-available/auth.conf

Definir el nuevo alias con la siguiente configuración:

Alias /auth /var/www/auth

Luego habilitar el sitio:

# a2ensite auth.conf
# service apache2 reload

Finalmente crear el directorio donde se hospedará el microservicio:

# mkdir -p /var/www/auth

Script PHP para autenticar contras un servidor LDAP

Cambiar al directorio base del microservicio y crear el script ldap.php:

# cd /var/www/auth
# nano ldap.php
<?php

// Credenciales de prueba
$user = "xxxx";
$pass = "xxxx";

// Datos de acceso al servidor LDAP
$host = "localhost";
$port = "389";

// Conexto donde se encuentran los usuarios
$basedn = "ou=people,dc=linuxito,dc=com";

// Atributos a recuperar
$searchAttr = array("dn", "cn", "sn", "givenName");

// Atributo para incorporar en la respuesta
$displayAttr = "cn";

// Respuesta por defecto
$status = 1;
$msg = "";
$userDisplayName = "null";

// Recuperar datos del POST
if (isset($_POST['user'])) {
        $user = $_POST['user'];
}
if (isset($_POST['pass'])) {
        $pass = $_POST['pass'];
}

// Establecer la conexión con el servidor LDAP
$ad = ldap_connect("ldap://{$host}:{$port}") or die("No se pudo conectar al servidor LDAP.");

// Autenticar contra el servidor LDAP
ldap_set_option($ad, LDAP_OPT_PROTOCOL_VERSION, 3);
if (@ldap_bind($ad, "uid={$user},{$basedn}", $pass)) {
        // En caso de éxito, recuperar los datos del usuario
        $result = ldap_search($ad, $basedn, "(uid={$user})", $searchAttr);
        $entries = ldap_get_entries($ad, $result);
        if ($entries["count"]>0) {
                // Si hay resultados en la búsqueda
                $status = 0;
                if (isset($entries[0][$displayAttr])) {
                        // Recuperar el atributo a incorporar en la respuesta
                        $userDisplayName = $entries[0][$displayAttr][0];
                        $msg = "Autenticado como {$userDisplayName}";
                }
                else {
                        // Si el atributo no está definido para el usuario
                        $userDisplayName = "-";
                        $msg = "Atributo no disponible ({$displayAttr})";
                }
        }
        else {
                // Si no hay resultados en la búsqueda, retornar error
                $msg = "Error desconocido";
        }
}
else {
        // Si falla la autenticación, retornar error
        $msg = "Usuario y/o contraseña inválidos";
}

// Respuesta en formato JSON
header('Content-Type: application/json');
echo "{\"uid\": \"{$user}\", \"estado\": \"{$status}\", \"nombre\": \"{$userDisplayName}\", \"debug\": \"{$msg}\"}";

El script se conecta contra el servidor LDAP y luego intenta autenticar con las credenciales recibidas vía POST. Se evita el uso de GET para que los datos sensibles (usuario y contraseña) no queden "escrachados" en los logs del servidor Web.

Luego obtiene algunos datos del usuario (en caso de haber autenticado con éxito) desde el directorio, tales como nombre y apellido, para agregar a la respuesta.

El código está suficientemente documentado a fin de explicar claramente qué se hace. Al mismo tiempo se incluye un mensaje indicando el resultado de la autenticación.

Las variables $user y $pass permiten almacenar credenciales de prueba para testear el script sin necesidad de enviar datos via POST:

De esta forma es posible comprobar el formato JSON de la respuesta.

Enviar datos de un formulario POST a través de JavaScript

A fin de testear el microservicio, es posible desarrollar un pequeño formulario HTML con un código JavaScript que implemente una interacción AJAX (más bien "AJAJ", ya que la respuesta del script PHP es en formato JSON) para enviar usuario/clave y mostrar la respuesta.

# nano login.html
<html>

<head>

<meta charset="utf-8" />
<title>LDAP login</title>

<script>

function auth() {

        var xhttp = new XMLHttpRequest();

        var user = document.getElementById('user').value;
        var pass = document.getElementById('pass').value;
        var data = "user="+user+"&pass="+pass;

        xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                        var obj = JSON.parse(this.responseText);
                        alert(obj.debug);
                }
        };

        xhttp.open("POST", "ldap.php", true);
        xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhttp.send(data);
}

</script>

</head>

<body>

<div>
<center>
<form method="POST">
<table>
<tr><td>Usuario:</td><td><input id="user" type="text"></td></tr>
<tr><td>Contraseña:</td><td><input id="pass" type="password" onkeydown="if (event.keyCode == 13) auth()"></td></tr>
</table>
<br />
<input type="button" name="login" value="Login" onclick="auth()">
</center>
</form>
</div>

</body>

La función auth() es la encargada de enviar los datos a través del método POST y mostrar la respuesta del servidor. Se trata del clásico ejemplo de interacción AJAX utilizando un objeto XMLHttpRequest, salvo que los datos del POST se deben enviar como un parámetro al método send(). Luego muestra uno de los campos de la respuesta JSON en un alert(), aunque se puede reemplazar por cualquier otra acción.

Veamos el funcionamiento con credenciales inválidas:

Ahora con credenciales válidas:

Espero que sea de utilidad.

Referencias


Tal vez pueda interesarte


Compartí este artículo