Control de acceso en gvSIG 2.1.0

Hola a todos!

De nuevo aquí para contaros sobre el sistema de permisos de gvSIG.
En versiones anteriores de gvSIG, cuando un desarrollador tenía que realizar una personalización en la que en función del usuario este tuviese que tener acceso a unas u otras herramientas, o tener acceso de solo lectura a determinadas fuentes de datos, capas o tablas, no había nada especifico en gvSIG que le ayudase a abordar ese desarrollo y tenía que hacerlo él todo. En muchas ocasiones teniendo que reescribir código de gvSIG para introducir sus comprobaciones de permisos.
Con gvSIG 2.1.0 se han introducido mecanismos para gestionar el acceso a las distintas herramientas, así como a fuentes de datos. Poco a poco iremos extendiendo el sistema de permisos a otras funcionalidades de gvSIG como pueden ser :

  • Geoprocesos
  • Páginas de preferencias
  • Documentos del proyecto
  • Acceso a snappers

En este artículo vamos a ver cómo podemos usar estos mecanismos.
Lo primero que hay que tener claro es que gvSIG no viene con una gestión de usuarios integrada.
Pero… ¿Qué quiere decir esto?
Pues simplemente que gvSIG no dispone de herramientas para dar de alta usuarios o asignarles permisos a estos. Normalmente cuando precisamos dotar a gvSIG de un sistema de permisos es porque queremos integrarlo en una organización que ya tiene su propio sistema de gestión de usuarios. Así que no vamos a tener que duplicarlo para usarlo en gvSIG.
En gvSIG se ha definido un API que permite autenticar a un usuario, y comprobar si tiene autorización para realizar determinadas acciones. Este API es muy simple. Consta de dos entidades:

  • SimpleIdentityManager, que nos permite autenticar a un usuario.
  • SimpleIdentity, que representa a un usuario autenticado, y nos permite verificar si este está autorizado a acceder a una acción determinada.

La distribución estándar de gvSIG viene con una implementación base de estos servicios en la que existe un usuario predeterminado, “guest“, que cuando se le pregunta, responde siempre que sí esta autorizado a acceder a un recurso; pero podemos reemplazar esta implementación por una nuestra, y ahí esta lo interesante.

En el proyecto org.gvsig.tools están definidos los interfaces de SimpleIdentityManager y SimpleIdentity, su implementación por defecto, así como alguna clase abstracta para facilitarnos la implementación de estos interfaces, además de disponer en el “locator” de tools de métodos para recuperar y registrar implementaciones de ellos.
Para ilustrar como podemos aportar nuestra implementación del SimpleIdentityManager vamos a preparar un plugin para gvSIG que:

  • Registre una implementación propia de SimpleIdentityManager, la cual valide los usuarios contra una BBDD simple en ficheros “property” en los que se encuentre la información de permisos asociada a cada usuario.
  • Y presente un diálogo de identificación de usuario al arrancar gvSIG y valide este contra el SimpleIdentityManager registrado en gvSIG.

Lo de menos es el soporte usado como BBDD, cada cual tendrá el suyo propio en su organización, lo importante es ver como se encajan las piezas para poder disponer de un control de acceso a acciones y recursos personalizado en gvSIG.

En el SVN del proyecto “templates” del redmine de gvSIG encontraremos los fuentes del proyecto org.gvsig.trivialidentitymanagement, con la implementación que voy a ir comentando. Podéis descargarlo desde :

http://devel.gvsig.org/svn/gvsig-plugintemplates/org.gvsig.trivialidentitymanagement/trunk/org.gvsig.trivialidentitymanagement

En él encontraremos básicamente tres proyectos:

  • org.gvsig.trivialidentitymanagement.app.mainplugin
  • org.gvsig.trivialidentitymanagement.lib.impl
  • org.gvsig.trivialidentitymanagement.lib.api

org.gvsig.trivialidentitymanagement.lib.api
En él tendremos la definición de nuestro API, que debe extender de el de gvSIG.
Tendremos los interfaces:

  • TrivialIdentityManager
  • TrivialIdentity

org.gvsig.trivialidentitymanagement.lib.impl
Aquí tendremos las clases que implementan nuestro API. Tendremos:

  • DefaultTrivialIdentityManager
  • DefaultTrivialIdentity

La implementación de estos es muy sencilla. Solamente hacer mención a unos pocos detalles:

  • El SimpleIdentityManager siempre tiene que devolver, en su método getCurrentIdentity, un SimpleIdentity, nunca null, aunque no haya un usuario registrado. Bien podemos devolver un usuario que responde siempre que está autorizado, o que responde siempre que no lo está. Dependerá de lo que nos interese en nuestra personalización.
  • El método “isAuthorized(actionName)”, recibe un nombre de acción y debe responder si el usuario está autorizado a ejecutar la acción asociada a ese nombre.
  • El método “isAuthorized(actionName, resource,resourceid)“, deberá devolver si el usuario está autorizado o no a realizar la acción indicada sobre el recurso indicado. Actualmente, con gvSIG 2.1.0, solo el acceso a fuentes de datos, DataStore de DAL, utiliza este mecanismos, pasando como recurso el DataParameter asociado a la acción que se intenta realizar. Las acciones que usa el DAL son:
    • create-store
    • open-store
    • read-store

Antes de ver la implementaron de ejemplo vamos a ver en que consistiría la BBDD de ficheros properties que usaremos. Dentro de la carpeta de la instalación del plugin tendremos una carpeta  “dbusers” y en ella un fichero property por usuario, que llamaremos con el nombre del usuario seguido de “.property”. Así, si tenemos un usuario “user01” tendremos un fichero “user01.properties”.

En el ejemplo tendremos dado de alta un usuario “user01” con la siguiente configuración:

attr.password=user01
attr.fullname=Test user 01
# El usuario no puede cargar recursos cuyo nombre de fichero termine en dxf
action.dal-read-store.parameter.name=file
action.dal-read-store.parameter.pattern=.*[.]dxf
action.dal-read-store.ifmatches=false
# Tampoco puede acceder a la herramienta de información de un punto.
action.layer-info-by-point=false

La implementación de nuestro ejemplo, básicamente contiene un Map para cada usuario, e indexando por el nombre de la acción obtenemos un booleano que nos indica está autorizado a ejecutar esa acción, diciendo que si están todas las que no conoce:

@Override
public boolean isAuthorized(String actionName) {
    try {
	String value = (String) this.properties.get(ACTION_PREFIX+actionName);
	if( StringUtils.isBlank(value) ) {
	    return true;
	}
	boolean  b = BooleanUtils.toBoolean(value);
	return b;

    } catch(Throwable th) {
	return true;
    }
}

El método que comprueba si tenemos acceso a un recurso, es algo mas complicado, pero también muy básico. Solo gestiona recursos de DAL, así que el recurso es siempre un DataParameter. Para cada acción tiene la siguiente información:

  • name, un nombre de parámetro del DataParameter que recibe.
  • pattern, una expresión regular que aplicar sobre el parámetro indicado.
  • ifmatches, un valor booleano que nos indica si debemos retornar true o false cuando el valor del parámetro concuerde con la expresión regular.

Con algo tan simple ya podemos restringir el acceso a los recursos en función del usuario. Vamos a ir viendo poco a poco el código de este método:

@Override
public boolean isAuthorized(String actionName, Object resource, String resourceName) {
    try {

	if( resource == null ) {
	    return this.isAuthorized(actionName);
	}
	if( !DataManager.CREATE_STORE_AUTHORIZATION.equalsIgnoreCase(actionName) &&
	    !DataManager.READ_STORE_AUTHORIZATION.equalsIgnoreCase(actionName) &&
	    !DataManager.WRITE_STORE_AUTHORIZATION.equalsIgnoreCase(actionName) ) {
	    // Si no es una accion conocida no la tratamos de forma especial
	    return this.isAuthorized(actionName);
	}
	if( !(resource instanceof DataParameters) ) {
	    return true;
	}
	...

Lo primero que hacemos es comprobar si hemos recibido un recurso. Si no lo hemos recibido delegamos en el método isAuthorized que solo recibe el nombre de acción.
Luego nos cercioramos que la acción que se quiere realizar es una de las que soporta nuestro sistema de autorización. En nuestro caso solo soportamos las peticiones de acciones de DAL, así que si no es ninguna de ellas también delegamos en el método isAuthorized que solo recibe el nombre de acción. Y por ultimo comprobamos que el recurso es del tipo DataParameters, que es con los que vamos a trabajar, y si no lo es, como no sabemos manejarlo, simplemente decimos que sí que esta autorizado.

Sea cual sea el tipo de validación que utilicemos estas primeras lineas serán siempre muy parecidas, mientras que el resto de lineas del método ya serán muy dependientes de contra qué y cómo realicemos la comprobación de si un usuario tiene o no autorización para realizar una acción sobre un recurso.

Vamos a ver muy rápido el código del ejemplo:

        ...
	String ifmatchesValue = (String) this.properties.get(ACTION_PREFIX+actionName+".ifmatches");
	if( StringUtils.isBlank(ifmatchesValue) ) {
	    return true;
	}     

	DataParameters params = (DataParameters) resource;
	String parameterValue = null;
	String parameterPattern = (String) this.properties.get(ACTION_PREFIX+actionName+".parameter.pattern");
	if( StringUtils.isBlank(parameterPattern) ) {
	    return true;
	}
	String parameterName = (String) this.properties.get(ACTION_PREFIX+actionName+".parameter.name");
	if( StringUtils.isBlank(parameterName) ) {
	    return true;
	}
	if( parameterName.equalsIgnoreCase("all") ) {
	    parameterValue = params.toString();
	} else {
	    if( resource instanceof FilesystemStoreParameters && parameterName.equalsIgnoreCase("file") ) {
		parameterValue = ((FilesystemStoreParameters) resource).getFile().getAbsolutePath();
	    } else {
		if( params.hasDynValue(parameterName) ) {
		    Object x = params.getDynValue(parameterName);
		    if( x == null ) {
			return true;
		    }
		    parameterValue = x.toString();
		}
	    }
	}
	...

Lo que hace en estas lineas de código es recuperar los valores de los tres datos, “name“, “pattern” y “ifmatches” asociados a la acción que se ha solicitado, y luego intenta recuperar de los DataParameter el valor indicado por “name“, usando como casos especiales los nombres de parámetro “all” y “file“.

Una vez ya hemos recopilado esta información, la cosa es ya mas simple:

        ...
	if( StringUtils.isBlank(parameterValue) ) {
	    return true;
	}
	if( !parameterValue.matches(parameterPattern) ) {
	    return true;
	}
	return BooleanUtils.toBoolean(ifmatchesValue);

    } catch(Throwable th) {
	return true;
    }
}

Comprobamos si concuerda el valor del parámetro con la expresión regular y devolvemos el valor de “ifmaches” en caso de que lo haga.

Hay algún detalle más como los métodos:

  • getAttributeNames, que devuelve una lista de nombres de atributos extra que tiene asociado ese usuario
  • O getAttribute, que nos permite recuperar el valor de un atributo extra del usuario.

Pero no son de mucha importancia, y se explican por si solos.

org.gvsig.trivialidentitymanagement.app.mainplugin
Bueno, pues una vez ya tenemos nuestras clases …. ¿cómo le decimos a gvSIG que las utilice?

Para eso tendremos que crear un plugin y dejar en el algo de configuración adicional.
Por un lado crearemos en el plugin una clase que implemente Runnable, en la que meteremos el código de inicialización de nuestro sistema de permisos. Y por otro deberemos crear en el raíz de nuestro plugin en la carpeta de la instalación el fichero “identity-management.ini”. Este fichero solo precisa de dos lineas, aunque puede contener mas información si la precisamos para nuestra implementación. Vemos esas dos lineas;

IdentityManager=org.gvsig.trivialidentitymanagement.impl.DefaultTrivialIdentityManager
IdentityManagementInitializer=org.gvsig.trivialidentitymanagement.Initializer

La entrada IdentityManager, contendrá el nombre de la clase a utilizar como la implementación del SimpleIdentityManager de gvSIG. Esta clase y todo lo que precise deberá estar en el class-path de nuestro plugin. gvSIG, al iniciarse, comprobara si algún plugin tiene este fichero “identity-management.ini”, y utilizara la clase indicada en la entrada IdentityManager para cargarla y registrarla en el locator de tools. De esta forma cualquier porción de gvSIG que acceda al locator de tools para recuperar el SimpleIdentityManager obtendrá el nuestro.

La otra entrada, IdentityManagementInitializer, contendrá el nombre de una clase de nuestro plugin que implemente el interface runable. Una vez registrado nuestro SimpleIdentityManagement, se cargara esa clase y se invocara a su método run para que esta se encargue de realizar las inicializaciones que precisemos para que nuestro sistema funciones correctamente. Veamos que tenemos en el código de nuestro ejemplo:

public void run() {
    TrivialIdentityManager identityManager = (TrivialIdentityManager) ToolsLocator.getIdentityManager();
    PluginsManager pluginsManager = PluginsLocator.getManager();
    PluginServices plugin = pluginsManager.getPlugin(this);

    // Initialize the folder database  for the TrivialIdentityManager
    File pluginFolder = plugin.getPluginDirectory();
    File dbfolder = new File(pluginFolder, "dbusers");
    identityManager.setdbFolder(dbfolder);

    // Show login dialog
    // Do not return if user cancel login.
    LoginDialog loginDialog = new LoginDialog(pluginFolder);
    loginDialog.showDialog();
    logger.info("User logged as '"+identityManager.getCurrentIdentity().getID()+"'.");
}

Como vemos, hace basicamente dos cosas:

  • Inicializar en nuestro TrivialIdentityManager la BBDD donde esta almacenada la información de permisos.
  • Presentar un cuadro de diálogo para autenticar al usuario.

A partir de aquí, y según sea nuestra aplicacion y organización podemos adaptar este proyecto de ejemplo para que valide contra una BBDD relacional, un servicio web o un servidor de LDAP. No debería ser complicado hacerlo. La única precaución a tener es que la información de los permisos que tiene un usuario debería cargarse al instanciar la clase SimpleIdentity y cuando se llame a los métodos isAuthorized trabajar contra esa información ya en memoria. Si tenemos que acceder a un servicio externo cada vez que se compruebe si el usuario tiene permiso para acceder a una acción puede relentizarse mucho la ejecución de gvSIG.

Vamos a ver el proyecto de ejemplo en acción.

Con la configuración que hemos visto el usuario  user01 no podrá cargar ficheros DXF ni podrá acceder a la herramienta de información.

Con el plugin org.gvsig.trivialidentitymanagement.app.mainplugin instalado, al arrancar gvSIG nos presentra la siguiente pantalla:

login

Nos identificaremos con usuario “user01” y clave “user01” y continuara la carga de gvSIG.
Si una vez arrancado gvSIG intentamos cargar un fichero DXF nos presentara un mensaje diciendo que no estamos autorizados a realizar esa acción:

no-autorizado-dxf

Y si cargamos una capa vectorial, observaremos que no tenemos la herramienta de información.

sin-herramienta-de-informacion
Bueno, espero que os sirva, y a ver cuando tengo otro ratito y cuento algún que otro truquito que me queda en la manga sobre el control de acceso en gvSIG 2.1.0.

Me gustaría agradecer los comentarios de Juan Carlos Gutiérrez que en su momento me ayudaron en el diseño al hacerme participe de sus necesidades relacionadas con el control de acceso a los datos en gvSIG.

Un saludo a todos

About Joaquin del Cerro

Development and software arquitecture manager at gvSIG Team. gvSIG Association
This entry was posted in development, gvSIG Association, gvSIG Desktop, spanish and tagged . Bookmark the permalink.

4 Responses to Control de acceso en gvSIG 2.1.0

  1. Pingback: Control de acceso en gvSIG 2.1.0 | Geo-How-To News

  2. Buenas Joaquín, una consulta, se pueden crear usuarios con permisos de lectura para que no puedan modificar algo en el gvSIG? Se puede hacer por medio del programa? Muchas gracias.

    • Joaquin del Cerro says:

      Hola Javi,
      te comento…
      La distribución standard de gvSIG no tiene un gestor de usuario y permisos propiamente dicho.
      gvSIG incluye los mecanismos para que puedas desarrollar tu gestor de usuarios y permisos a medida de las necesidades de tu organización y gvSIG lo usara para determinar si un usuario puede hacer algunas acciones o restringir el acceso a algunos recursos (capas, tablas), bien para lectura solo o ni siquiera para lectura, todo ello sin que tengas que retocar cada herramienta o proveedor de datos para incluir los chequeos pertinentes.

      A partir de la versión 2.1 de gvSIG el acceso a datos y la ejecución de las acciones (menús o herramientas de la barra de botones) esta supeditada a lo que diga el gestor de permisos, lo que pasa es que el gestor de permisos que viene con gvSIG siempre dice que tienes permiso para todo.

      En este articulo se comenta como podría ser el desarrollo de un gestor de permisos alternativo y como cambiando el que trae gvSIG por defecto por el nuestro podemos controlar a lo que cada usuario tiene acceso.

      El desarrollo que se propone en el articulo es solo con fines didácticos. No hay una BBDD de usuarios centralizada detrás, ni herramientas para mantener los usuarios y permisos. Si precisas un sistema de permisos especifico para tu organización debes desarrollar el gestor de usuario y permisos de forma similar a como indico en el articulo pero adaptándolo a tu organización. Si no estas en condiciones de realizar ese desarrollo puedes ponerte en contacto con la asociación gvSIG, info@gvsig.com, para asesorarte sobre como podrías hacerlo o ponerte en contacto con alguien que pueda desarrollarlo.

Leave a comment