Is it possible to complete a User Task with case insensitive UserId in BRMS 5.3.x ?
Environment
- Red Hat JBoss BRMS (BRMS)
- 5.3.x
Issue
- When authenticating users through
LDAP
it sometimes allows for mixed casing. For example the useriddemo" is the same as
Demoand is allowed to be used because
LDAPallows this. However it appears that if a user was to log-in to
Business Centralconsole of
BRMS 5.3.xas
demoand claim some user tasks they will be owned by
demo. Later if the same user decides to login as
Demo` and try to complete the same task it throws the following exception.
org.jbpm.task.service.PermissionDeniedException: User '[User:'Demo']' does not have permissions to execution operation 'Complete' on task id 4485
But then logging into demo
the user would be able to work with the task properly. Is there a way to tell jBPM5 Engine
to use a case insensitive version of the ActorId
for existing tasks instances?
Resolution
- It could be possible to make some changes to the internal
TaskService
calls to change the default behavior of howTaskService
checks whether a user is authorized to perform the task or not. Normally, after calling thecomplete(..)
operation from the user's code they call thecomplete(..)
operation onorg.jbpm.task.service.local.LocalTaskService.complete(..)
to complete a task, which is an implementation oforg.jbpm.task.TaskService
. So, in this case , first users need to provide their own implementation of thisorg.jbpm.task.TaskService
which would mock the wayorg.jbpm.task.service.local.LocalTaskService
works internally.
package org.jbpm.task.service.local;
...
public class LocalTaskService implements TaskService {
private org.jbpm.task.service.TaskService service;
private TaskServiceSession session;
public LocalTaskService(org.jbpm.task.service.TaskService taskService) {
this.service = taskService;
this.session = service.createSession();
}
...
public void complete(long taskId, String userId, ContentData outputData) {
session.taskOperation(Operation.Complete, taskId, userId, null, outputData, null);
}
...
Once it is done , just use this new implementation inside the user's code (which is being used to complete the tasks) to get taskService
object to complete the task.
Now the reason for making this change is that users can not only, give their own implementation of LocalTaskService.complete(...)
method , but internally LocalTaskService
needs a org.jbpm.task.service.TaskServiceSession
object to call the org.jbpm.task.service.TaskServiceSession.taskOperation(...)
method to get the User
and Group
ownership details of the task and check if they are allowed to perform the action on the task or not. This is where the root cause of the above mentioned problem lies.
package org.jbpm.task.service;
...
public class TaskServiceSession {
private final TaskPersistenceManager tpm;
private final TaskService service;
...
public void taskOperation(final Operation operation, final long taskId, final String userId,
final String targetEntityId, final ContentData data,
List<String> groupIds) throws TaskException {
OrganizationalEntity targetEntity = null;
groupIds = doUserGroupCallbackOperation(userId, groupIds);
doCallbackUserOperation(targetEntityId);
if (targetEntityId != null) {
targetEntity = getEntity(OrganizationalEntity.class, targetEntityId);
}
final Task task = getTask(taskId);
User user = getEntity(User.class, userId);
boolean transactionOwner = false;
try {
final List<OperationCommand> commands = service.getCommandsForOperation(operation);
transactionOwner = tpm.beginTransaction();
evalCommand(operation, commands, task, user, targetEntity, groupIds);
...
void evalCommand(final Operation operation, final List<OperationCommand> commands, final Task task,
final User user, final OrganizationalEntity targetEntity,
List<String> groupIds) throws PermissionDeniedException {
final TaskData taskData = task.getTaskData();
boolean statusMatched = false;
for (OperationCommand command : commands) {
// first find out if we have a matching status
if (command.getStatus() != null) {
for (Status status : command.getStatus()) {
if (taskData.getStatus() == status) {
statusMatched = true;
// next find out if the user can execute this doOperation
if (!isAllowed(command, task, user, groupIds)) { <<<--- This is where the problem occurs --->>>
String errorMessage = "User '" + user + "' does not have permissions to execution operation '" + operation + "' on task id " + task.getId();
throw new PermissionDeniedException(errorMessage);
}
...
private boolean isAllowed(final OperationCommand command, final Task task, final User user,
List<String> groupIds) {
final PeopleAssignments people = task.getPeopleAssignments();
final TaskData taskData = task.getTaskData();
boolean operationAllowed = false;
for (Allowed allowed : command.getAllowed()) {
if (operationAllowed) {
break;
}
switch (allowed) {
case Owner: {
operationAllowed = (taskData.getActualOwner() != null && taskData.getActualOwner().equals(user));
break;
}
case Initiator: {
operationAllowed = (taskData.getCreatedBy() != null &&
(taskData.getCreatedBy().equals(user))
|| (groupIds != null && groupIds.contains(taskData.getCreatedBy().getId())));
break;
}
case PotentialOwner: {
operationAllowed = isAllowed(user, groupIds, people.getPotentialOwners());
break;
}
...
private boolean isAllowed(final User user, final List<String> groupIds, final List<OrganizationalEntity> entities) {
// for now just do a contains, I'll figure out group membership later.
for (OrganizationalEntity entity : entities) {
if (entity instanceof User && entity.equals(user)) {
return true;
}
if (entity instanceof Group && groupIds != null && groupIds.contains(entity.getId())) {
return true;
}
}
return false;
}
...
As it can now be seen from the above code flow of TaskServiceSession
class, users would need to override the methods like isAllowed(...)
to allow ignore-case comparison of the User
and Group
details from the one which the user has supplied in their code to complete the task with the details that are registered with task's PeopleAssignments
.
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.
Comments