Tabla de Contenidos

Gestión de Roles y Usuarios con tablas de Base de Datos

PEDRO DELGADO YARZA 2014/01/29 13:55

Seam proporciona un sistema para la gestión de roles/permisos/usuarios. Para Seam un permiso es un par Objetivo/Acción, distinguiendo entre el objeto al que vamos a dar permisos (Objetivo), que podría ser por ejemplo “pantalla de actividades” y el permiso otorgado (Accion): “Creacion” “Consulta” etc…

Este mecanismo nos permite, por tanto, otorgar diferentes acciones posibles a realizar a los usuarios de nuestras aplicaciones, de manera que haya usuarios que tengan determinadas acciones restringidas y no las puedan realizar (ni visualizar). Así pues podemos crear distintos roles: Administrador, Gestor, Usuario, Invitado, etc.., y asignar acciones a esos roles, de manera que, por ejemplo, el usuario Invitado sólo pueda hacer consultas, mientras que el usuario Administrador puede consultar, crear y modificar.

Este comportamiento puede realizarse gracias a la clase Identity la cual contiene métodos predefinidos para facilitar el desarrollo de esta lógica y hacer que sea la misma entre aplicaciones.

Normalmente, esta relación entre Roles/Permisos/Usuarios la tendremos almacenada en base de datos, por lo tanto deberemos implementar el siguiente diseño para poder aplicarlo en nuestra aplicación.

El script para crear el esquema de tablas que se describe a continuación lo podéis descargar en este enlace: creación esquema autorización una vez descargado deberéis modificar [MIESQUEMA] por el nombre de vuestro esquema de base de datos. También deberéis comprobar que los tablespace están acordes a los creados para el esquema.

Modelo de base de datos

Lo primero que debemos hacer es crear un esquema de base de datos acorde a la infraestructura que hace falta para la implementación de este mecanismo de permisos. Este esquema debe contener las siguientes tablas junto con las propiedades recomendadas:

La manera de llamar a esta tablas no está fijado de antemano, pero hay que tener en cuenta que cuando se creen los bean de entidad que los mapean, a través de anotaciones se le indicará a Seam, qué tabla está 'interpretando' la función de tabla de Usuarios, Roles, etc. y qué campos son cada cual.

Para crear las tablas y secuencias necesarias, tenemos los script para crear esta estructura en BBDD lo tienes en permisos_y_usuarios.zip, solo hay que sustituir el texto <MI_ESQUEMA> por el nombre de nuestro esquema de BBDD.

Una vez creadas las tablas ejecutamos el generador como indica la guía Generar entidades a partir de la BBDD, para crear los beans de entidad.

Veamos las anotaciones que son obligatorias poner en los beans que mapean nuestras tablas de gestión de autorización:

Entidad Usuarios. Estas anotaciones se colocaran en el bean de entidad que hace el mapeo a la tabla Usuarios.

	// OBLIGATORIO POR SEAM. LO DEVOLVEMOS VACIO. AÑADIR A BEAN DE ENTIDAD USUARIOS///////////
	@Transient
	@UserPassword(hash="none") 
	public String getPassword(){
		return "null";
	}
	public void setPassword(String nombre) {
	}
/////////////
	@ManyToMany(targetEntity = AdwRoles.class)
	@JoinTable(name = "ADMINWEB.ADW_USUARIOS_ROLES", 
		joinColumns = @JoinColumn(name = "ADMINWEB_ID"),
		inverseJoinColumns = {@JoinColumn(name = "COD_ROL")})
	@UserRoles
	public List<AdwRoles> getAdwRoles() {
		return this.adwRoles;
	}
 
	public void setAdwRoles(List<AdwRoles> adwRoles) {
		this.adwRoles = adwRoles;
	}
 

Entidad Roles. Estas anotaciones se colocaran en el bean de entidad que hace el mapeo a la Tabla de Roles.

Entidad Usuarios/Roles con Permisos. Estas anotaciones se colocaran en el bean de entidad que hace el mapeo a la tabla/s de relación Usarios/Roles con Permisos.

       // OBLIGATORIO POR SEAM. LO DEVOLVEMOS VACIO. AÑADIR A BEAN DE ENTIDAD USUARIOS///////////
	@Transient
	@PermissionAction 
	public String getAction(){
		return "null";
	}
       //Debe de ser de tipo String.
	public void setAction(String action) {
	}
   // OBLIGATORIO POR SEAM. LO DEVOLVEMOS VACIO. AÑADIR A BEAN DE ENTIDAD USUARIOS///////////
	@Transient
	@PermissionUser
	public String getUser(){
		return "null";
	}
	public void setUser(String user) {
	}
@Transient
	@PermissionDiscriminator
	public String getDiscriminator(){
		return "role";
	}
	public void setDiscriminator(String discriminator) {
	}
 

Con todas estas anotaciones tendríamos definido nuestro modelo de datos, sólo nos queda configurarlo como veremos en el siguiente apartado.

Configuraciones

Si la entidad de Usuarios es AdwUsuarios y la de Roles AdwPerfiles. Añadir en el fichero web\src\main\webapp\WEB-INF\components.xml

<security:jpa-identity-store 
    	user-class="model.entity.adminweb.AdwUsuarios"
    	role-class="model.entity.adminweb.AdwPerfiles"/>

Descargarse la clase: UmuPermissionStore del fichero fundeweb_security.rar. Descomentar la linea 33 y especificar la clase que contiene la tabla relacion Roles/Usuarios con permisos. Esta clase es necesario para solucionar un bug de Seam y cachear los permisos para que sea más eficiente la ejecución.

	setUserPermissionClass(AdwPerfilesMenus.class);

La clase Identity viene con un método addRoles para añadir los roles de cada usuario. En el momento en el que el usuario se autentique, es el momento más adecuado para hacer esta carga. Así que en un principio añadir en el método authenticate, de la clase AuthenticatorAction justo después de haber sido autenticado exitosamente. También reemplazamos la inyección de la clase Identity por la clase UmuIdentity:

        @In
	UmuIdentity identity;
 
	if(!RadiusIdent.autentica(credentials.getUsername(),credentials.getPassword(), "FUNDEWEB")){
			autenticado = true;
		}
 
		if (autenticado) {
			log.info("Ejecutando autenticación");
 
			identity.setEmail(credentials.getUsername());
			List<String> roles = IdentityManager.instance().getGrantedRoles(identity.getEmail());
			for (String rol:roles){
				identity.addRole(rol);
			}
	    } 
 

Uso en la Aplicación

Una vez configurada la aplicación podemos limitar el acceso a una página, o ocultar una opción de menu, etc. accediendo al método identity.hasPermission. Este método recibe dos parámetros el “objetivo” y la “accion”, explicado anteriormente. Para hacer una opción de menu visible o no, según los permisos, habrá que añadir el atributo rendered, de la siguiente manera:

		<p:menuitem id="homeMenuItem" value="#{messages['value.link.home']}"
			icon="ui-icon-home" ajax="false" styleClass="bloqueoFundeweb" rendered="#{identity.hasPermission('1010_MEADCAMCLA','CON')}"
			action="#{menuManagerBean.clickAndKillLastConversation('/paginas/home.xhtml')}" />

La opción de menú anterior se mostrará , si el usuario tiene permiso de consulta ('CON') sobre la opción de menú '1010_MEADCAMCLA' . A parte en el fichero page.xml de la página habrá que añadir una opción para restringir el acceso, ya que aunque no exista la opción de menú, algún usuario podría copiar la URL para acceder directamente a la página. Añadir al pages.xml de la página:

	<restrict>#{identity.hasPermission('1010_MEADCAMCLA','CON')}</restrict>