G2/ConsideracionesTecnicas/LDAP

Autenticación

La autenticación y en particular la gestión de usuarios es un proceso muy dependiente de cada institución, más aun si se consideran de cara al futuro, mecanismos de single sign on (SSO). Por este motivo se decidió modularizar y desacoplar la autenticación en el proyecto para que pueda adaptarse fácilmente a la estructura existente/deseada de cada instalación.

Diseño

Responsabilidades:

  • El núcleo del proyecto delega la autenticación a un authentication_provider.
  • Un proveedor de autenticación/authentication_provider tiene la responsabilidad de pedirle credenciales al usuario, procesarlas y determinar su identidad.
  • Una vez identificada la persona, el núcleo abre una sesión en el proyecto y carga la persona que representa al usuario actual.
  • El proyecto interactua sólo con esa sesión y objeto de la persona.

Configuración

└── instalacion
    └── login.php

Toda la configuración de la autenticación se realiza en el archivo de configuración /instalacion/login.php. Aquí se debe retornar un arreglo de los mecanismos de autenticación disponibles en el proyecto.

Notar que cada entrada tiene 2 partes. El identificador del arreglo, y los parámetros 'activo' y 'clase' son para uso del núcleo, mientras que el parámetro 'parametros' es totalmente libre y el núcleo solo se limita a hacerlo llegar al constructor de la clase en el momento apropiado. Con esto se puede manejar la configuración de los distintos providers desde este mismo archivo sin tener que modificar otras clases del proyecto.

return array(
	'saml'     => array(
		'activo'     => true,
		'clase'      => 'modelo\\autenticacion\\auth_saml',
		'parametros' => array(),
	),
	'form'     => array(
		'activo'     => true,
		'clase'      => 'modelo\\autenticacion\\auth_form',
		'parametros' => array( 

		)
	),

Parámetros

  • Llave del arreglo: Se utiliza para referenciar al provider en distintas fases del proceso, y también aparece en la URL.
  • Activo: Si está activo este proveedor en el proyecto.
  • Clase: Clase que extiende de kernel\acceso\authentication_provider. En guaraní preferentemente extender de auth_guarani.
  • Parámetros: Se entrega al constructor de la clase, y cada clase lo maneja a discreción.

authentication_provider

Por convención las fuentes de autenticación se ubican en la siguiente carpeta. Estas clases deben heredar de la clase abstracta kernel\acceso\authentication_provider que define la interfaz y sólo implementa el constructor y métodos utilitarios para acceder al identificador, y a los parámetros de configuración, ambos definidos en el archivo de configuración login.php del inciso anterior.

└── siu
    └── modelo
        └── autenticacion
            └── auth_<TIPO>.php

Implementar un authentication_provider propio

Referirse a la propia clase para los detalles de cada método.

Asimismo pueden tomarse como referencia las otras clases de la carpeta para tener una idea del funcionamiento. En el archivo /siu/lib/kernel/acceso/auth_con_sesion.php se puede ver una implementación parcial que utiliza la propia sesión (compartida con el núcleo) para mantener la autenticación. Este es el caso base para muchos otros tipos de autenticación donde se hace solo una identificación inicial, y luego se continua con la sesión local; por ejemplo autenticación por un form (user:pass), u OpenID, y por otro lado mecanismos como saml o basic (de apache) se verifica por fuera de la sesión si el usuario está logueado.

Funcionamiento

Lo principal que hay que tener en cuenta es el flujo de llamados.

  • 1) Se inicia con un usuario no identificado - sin sesión en el núcleo. Si una operación se requiere autenticación, se redirige al login. Operación 'acceso'.
  • 2) En la operación de acceso, para todos los mecanismos de login activos se pide el método get_modelo(). En base a esos datos, se renderizan los elementos necesarios para autenticar al usuario con cada proveedor. Ej. Si es usuario:password, el modelo debería indicar la url de la acción, y los nombres de los campos para que luego pueda procesarlos. Si es saml posiblemente se quiera proveer la URL del Service Provider, o bien una URL donde se haga la redirección al SP.
  • 3) El usuario elige un mecanismo y generalmente se procesa en /acceso?auth=[id_proveedor]&[otros parámetros sacados del modelo]

  • 4) El núcleo da control al proveedor adecuado para que procese las credenciales, y llama a autenticar(), que retorna el id de la persona o nulo.
  • Si el id no es nulo, se abre una sesión en el núcleo con el mismo.
  • En caso contrario, se vuelve al paso 2 con la diferencia de que al invocar get_modelo(), se pueden agregar errores para que sean visualizados en la operación de acceso.

Notas:

  • Cada request se invocará a esta_logueado(), que se puede utilizar para cerrar la sesión cuando se vence en el mecanismo externo (saml), o bien si se excede un tiempo X desde el último request.

  • Todos los request se instancia una persona, haya sido exitosa o no la autenticacion, y se notifica al proveedor para que lo configure, registre, etc (si el id es nulo, se crea una persona anonónima).

fuente_usuarios_guarani

Esta clase tiene por objetivo hacer de puente entre los auth providers y la base de datos de guarani. Cuando un provider deba consultar usuarios de guarani sería conveniente que lo haga a traves de este objeto para mantener la convención. Por ejemplo, buscar una persona por mail para asociarla desde openid, o validar usuario/password, o en general cualquier consulta a la base de datos de guarani.

Personalizar

Se puede seguir el mismo proceso. En resumen:

  • 1 Agregar la configuración al login.php de la instalación. En 'parametros' puede pasarse lo que se considere oportuno.
  • 2 Personalizar la operación de operaciones/acceso para que muestre adecuadamente los mecanismos de autenticación deseados. Actualmente llega un arreglo con el get_modelo() de todos los proveedores activos hasta el acceso/login/pagelet_login.php. Solo sería necesario personalizar el twig, y quizás el javascript.
  • 3 Agregar o personalizar (extender) la clase auth_provider apropiada.
  • 4 (covención) agregar las consultas a la base de guaraní en la clase fuente_usuarios_guarani.

Ejemplo (LDAP)

Si en la institución se deseara utilizar ldap en lugar de la base de datos de guaraní se puede observar el ejemplo de personalización incluido en /src/pers/comun/modelo/autenticacion.

En el mismo se extienden 2 archivos solamente. En primer lugar observar que autenticar con ldap es esencialmente lo mismo que autenticar con el formulario tradicional, solo que la consulta final se hace en un ldap en lugar de un informix/postgres. Para esto - paso (3) - extender auth_form y cambiar el comportamiento del método validar_user_pass($usuario, $password). Tomar como referencia la implementación original y reemplazar el resto.

    protected function validar_user_pass($usuario, $password)
    {
        $parametros_conexion = $this->get_parametros();
        $id_persona = $this->get_clase_usuarios()->autenticar_ldap($parametros_conexion, $usuario, $password);
        intentos_login::eliminar($usuario);
        return $id_persona;
    }

Por convención - paso (4) - las consultas sobre usuarios se realizan en la clase 'fuente_usuarios_guarani', que se obtiene con el helper $this->get_clase_usuarios(), que también se debe extender y agregar el método autenticar_ldap(). Allí mismo se pueden realizar los mapeos y lógica particulares de cada institución libremente, sólo hay que retornar el id de la persona en base al usuario y password. Esta es la principal ventaja del esquema ya que desde la versión de guaraní no es posible cubrir todos los casos por configuración; quizás los passwords están hasheados con otro algoritmo, quizás el id_persona se mapea con un cn, con un uid, o simplemente otro atributo - no hay motivo para poner limitaciones.

Para mantener la configuración ordenada - paso (1) - se provee el helper $this->get_parametros(). Los mismos se obtienen del archivo de configuración /instalacion/login.php

	'form'   => array(
		'activo'     => true,
		'clase'      => 'modelo\\autenticacion\\auth_ldap',
		'parametros' => array(
                            ///parametros libres que se obtienen en la clase con get_parametros().
	         )
	),

OBS: notar que el id sigue siendo 'form'. De este modo se utilizan el renderizado del form existente, de esta forma no es preciso realizar el paso (2)