Home

This site also has an english version.

Intro

Este plugin integra Activiti con Shiro Security en una aplicación Grails. Se encuentra basado en el trabajo de Lim Chee Kin (plugins para Activiti y Spring Security) y Peter Ledbrook (plugin para Shiro). Esta licenciado bajo la Apache Software License v2.0.

Descripción

Modelos de dominio

Activiti usa un modelo de seguridad con tres entidades (User, Role y el enlace entre las dos), para ello define dos interfaces que deben ser implementadas por los modelos de dominio User y Group, la tercera es la asociacion m:n entre ellas (UserRole).

El siguiente modelo es una ligera adaptación del modelo de dominio base de Spring Security:

Modelo

No todos los atributos de las clases son necesarios (solmente los definidos por las interfaces de Activiti y en ShiroUser passwordChangeRequiredOnNextLogon). Éstas clases se pueden crear a partir del plugin de shiro para grails.

El plugin usa convenciones para generar las consultas dinámicas de permisos, por tanto es neceserio que en UserRole los nombres de los atributos user y role conincidan con los nombres de las clases User y Role usadas, en este caso ShiroUser y ShiroRole.

Activiti necesita realizar consultas basado en el nombre de usuario y el identificador de grupo por ello los atributos id de los modelos son de tipo String y deben ser mapeados de la siguiente manera:

Para User

static mapping = {
		password column: '`password`'
		id generator: 'uuid'
	}

Para Group

static mapping = {
                id generator: 'assigned'
	}

Para UserRole

static mapping = {
		id composite: ['shiroRole', 'shiroUser']
		version false
	}

Lo que implica que los identificadores de los roles deben ser asignados, éstos identificadores deben contener los nombres de los roles usados en las expresiones de selección de psoibles usuarios durante la definición de tareas del proceso, el siguiente fragmento es extraído del proceso VacationRequest:

<userTask id="initiateVacationRequest" activiti:formKey="/vacationRequest/create"
			name="Initiate Vacation Request">
			<documentation>Vacation request by ${username}</documentation>
			<potentialOwner>
				<resourceAssignmentExpression>
					<formalExpression>user</formalExpression>
				</resourceAssignmentExpression>
			</potentialOwner>
		</userTask>
		<sequenceFlow id="flow1" targetRef="handleVacationRequest"
			sourceRef="initiateVacationRequest" />
		<userTask id="handleVacationRequest" activiti:formKey="/vacationRequest/approval"
			name="Handle Vacation Request">
			<documentation>Vacation request by ${username}</documentation>
			<potentialOwner>
				<resourceAssignmentExpression>
					<formalExpression>management</formalExpression>
				</resourceAssignmentExpression>
			</potentialOwner>
		</userTask>

debido a que el Activiti realiza la búsqueda de posibles usuarios basándose en las expresiones formales (formalExpression) del xml del proceso.

Inicialización

El plugin implementa las interfaces GroupManager y UserManager de Activiti y configura el IdentityService para usar las implementaciones, usa las propiedades de Config.groovy (Ver instalación) para generar las consultas dinámicas.

Es necesario registrar en la sesión de usuario el nombre de usuario para que Activiti busque los grupos y usuarios posibles para las tareas para ello el plugin implementa el método attachUsername2Session() del servicio ShiroActivitiSessionService, este servicio debe ser inyectado en la aplicación cliente y ser llamado una vez se inicie sesión, es posible realizar esto con un listener de Shiro o en el controlador de grails que realiza la autenticación de usuario.

Instalación

  • Instalar el plugin en la aplicacion
grails install-plugin /path/plgin/grails-activiti-shiro-0.1.zip
  • Configurar los nombres de los modelos y propiedades usadas en Config.groovy
securityConfig.userLookup.usernamePropertyName = 'username' //username property
securityConfig.userLookup.userDomainClassName = 'ShiroUser' //domain classname without package
securityConfig.userLookup.authorityJoinClassName = 'UserRole' //domain classname without package
securityConfig.userLookup.authority.className = 'ShiroRole' //domain classname without package
  • Declarar el servicio ShiroActivitiSessionService y llamar al método attachUsername2Session() una vez se haya iniciado sesión (probablemente en el AuthController de Shiro - linea 45 -)

Lista de Todas las Tareas

El plugin de Activiti para Grails contiene tres vistas de tareas (myTasks, unassignedTasks y allTasks), en esta última vista (en la versión 5.7 del plugin) por defecto no se presentan los usuarios que pueden ejecutar la tareas, para ello es necesario modificar en allTaskList.gsp el scriptlet que inicia en la linea 66 por el siguiente código:

def users = []
																		def userList=[:]
																		def userIds = ActivitiUtils.activitiService.getCandidateUserIds(taskInstance.id)
																		def groups
																		def groupIds
																		if (!applicationContext.getBean('pluginManager').hasGrailsPlugin('activitiSpringSecurity')) {
																		
																		def User = grailsApplication.getDomainClass(grailsApplication.config.securityConfig.userLookup.userDomainClassName).clazz
																		  users = User."findAllBy${GrailsNameUtils.getClassNameRepresentation(grailsApplication.config.securityConfig.userLookup.usernamePropertyName)}InList"(userIds)
																		
																			for (id in userIds) {
									                        groups = ActivitiUtils.identityService.createGroupQuery().groupMember(id).orderByGroupId().asc().list()
									                        
									                                        }		       
																		} else {
																		  def User = grailsApplication.getDomainClass(grailsApplication.config.grails.plugins.springsecurity.userLookup.userDomainClassName).clazz
																		  users = User."findAllBy${GrailsNameUtils.getClassNameRepresentation(grailsApplication.config.grails.plugins.springsecurity.userLookup.usernamePropertyName)}InList"(userIds)
																			
																		}
																		for (user in users) {
															          groups = ActivitiUtils.identityService.createGroupQuery().groupMember(user.id).orderByGroupId().asc().list()
															          groupIds = groups?" ${groups.collect{it.name}}":""
																				userList[user.username]="${user.username}${groupIds}"
																			}

Scripts

Tests

Versiones

0.1

29/11/2011 - Versión inicial

  • Implementada inicialización de IdentityService con GroupManager y UserManager
  • Implementado servicio de enlace de nombre de usuario en sesion
  • Prueba de integración con Grails-Activiti VacationRequest

ToDo

Tareas pendientes

Updated

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.