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
LDAPit sometimes allows for mixed casing. For example the useriddemo" is the same asDemoand is allowed to be used becauseLDAPallows this. However it appears that if a user was to log-in toBusiness Centralconsole ofBRMS 5.3.xasdemoand claim some user tasks they will be owned bydemo. Later if the same user decides to login asDemo` 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
TaskServicecalls to change the default behavior of howTaskServicechecks 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.TaskServicewhich would mock the wayorg.jbpm.task.service.local.LocalTaskServiceworks 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.
Welcome! Check out the Getting Started with Red Hat page for quick tours and guides for common tasks.
