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.

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:

  • Tabla de Usuarios. Es la tabla donde se almacenarán los usuarios que tendrán acceso a la aplicación. Una vez pasado el mecanismo de autenticación. Campos aconsejados:
    • Login: Campo de usuario identificador. Clave primaria.
  • Tabla de Roles. Es la tabla donde se almacenarán los distintos roles que puede tener un usuario (Administrador, Operario, etc). Campos aconsejados:
    • Id Rol: Clave primaria. Debe ser un campo de texto (VARCHAR2).
  • Tabla de que relacione los Roles y Usuarios. Campos aconsejados:
    • Login: Clave ajena al campo Login de la tabla Usuarios.
    • Id Rol: Clave ajena al campo Id Rol de la tabla Roles.
  • Tabla de Objetivos. Son los posibles objetos con los que va a interactuar el usuario / rol. Será una tabla con códigos/cadenas que representan por ejemplo 'página de asignaturas' “ , “asignatura'”, etc… Campos aconsejados:
    • Id Objetivo: Clave primaria (VARCHAR2).
  • Tabla de relación Usarios/Roles con Objetivos. Los permisos pueden ser asignados a un usuario en concreto o bien a un rol. Es posible tener una tabla con relación de permisos para usuarios y otra tabla con relación de permisos para roles separadas o bien tener una tabla en la que pongas las dos a la vez. O incluso una sola de estas opciones, pero debe de existir al menos UNA. Campos aconsejados:
    • Id Rol. Rol al que se le da el permiso. Debe de ser una cadena de texto. Opcional si tienes Id Usuario.
    • Id Usuario. Usuario al que se le da el permiso. Opcional si tienes Id Rol o puede existir un unico campo que los englobe (a roles y usuarios).
    • Id Objetivo. Clave ajena a la clave primaria de la tabla Objetivos en caso de que exista o un campo normal en caso de que no. Debe de ser una cadena de texto.
    • Id Action. Acción permitida al objetivo. Opcional si solo se va a permitir una acción.
    • Discriminator. En caso de que haya un unico campo ebglobando roles /usuarios. Este campo indicaría que función está realizando.

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.

  • @UserPrincipal. Sobre el campo Login. La propiedad mapeada debe de coincidir con el campo de base de datos. Por ejemplo, si el campo de base de base de datos es ID_LOGIN, aunque al generar el campo de mapeo suele ser getIdLogin hay que cambiarlo a getId_login.
  • @UserPassword. Tiene el password asociado al usuario. Como usualmente la validación en la Universidad se hace a través de Radius NO sería necesario poner este campo, sin embargo para Seam es obligatorio. Solución, hacer un campo Transient que devuelva un valor fijo.OBLIGATORIO AÑADIR:
	// 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) {
	}
  • @UserRoles. Debe de estar sobre un campo ManyToMany. NO se genera automaticamente. Así que hay que eliminar la generación del conjunto OneToMany de Roles ( habrá una colección de roles OneToMany que hay que borrar). En vez de esa hay que añadir una relación ManyToMany. Por ejemplo si la tabla de Roles es ADWROLES y la de Usuarios/Roles es ADW_USUARIOS_ROLES, el identificador de la tabla de Usuarios es ADMINWEB_ID y el identificador de la tabla Roles COD_ROL, habría que escribir:
/////////////
	@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.

  • @RoleName. Se corresponde con el campo “Id. Rol” de la tabla de Roles. OBLIGATORIO.

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.

  • @PermissionTarget. Se corresponde con el campo “Id Objetivo” de la Tabla/s de relación Usarios/Roles con Objetivos. OBLIGATORIO. Debe de ser de tipo String.
  • @PermissionAction. Se corresponde con el campo Id Accion de la la Tabla/s de relación Usarios/Roles con Objetivos. En caso de que no haya campo accion (en base de datos) porque o se tiene permiso o no sobre un objeto/página, pero no es necesario especificar acción, se puede obviar este campo añadiendo lo siguiente:
       // 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) {
	}
  • @PermissionUser. Id Usuario de la tabla Tabla/s de relación Usarios/Roles con Objetivos.Usuario al que se le da el permiso. Es Obligatorio. En caso de que el campo no exista en base de datos (porque los permisos se estén asociando SOLO a roles). Añadir:
   // 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) {
	}
  • @PermissionRole. Id Rol de la tabla Tabla/s de relación Usarios/Roles con Objetivos. Opcional.
  • @PermissionDiscriminator. Se corresponde con el campo Discriminator de la tabla Tabla/s de relación Usarios/Roles con Objetivos. Es el menos útil de los especificados y es OBLIGATORIO. Asi que añadir:
@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>
  • fdw2.0/fundeweb2.0/gt/gestion_de_roles_y_usuarios_con_tablas_de_base_de_datos.txt
  • Última modificación: 11/03/2020 08:09
  • por JUAN MIGUEL BERNAL GONZALEZ