blob: 9ddae5d0afe065323ba9218a060bd39ee604b828 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.core.flowable.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.request.PasswordPatch;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.UserRequest;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.to.UserRequestFormProperty;
import org.apache.syncope.common.lib.to.UserRequestForm;
import org.apache.syncope.common.lib.to.UserRequestFormPropertyValue;
import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
import org.apache.syncope.core.flowable.api.DropdownValueProvider;
import org.apache.syncope.core.flowable.api.UserRequestHandler;
import org.apache.syncope.core.flowable.support.DomainProcessEngine;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.workflow.api.WorkflowException;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.engine.form.FormProperty;
import org.flowable.engine.form.FormType;
import org.flowable.engine.form.TaskFormData;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.HistoricFormPropertyEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = { Throwable.class })
public class FlowableUserRequestHandler implements UserRequestHandler {
protected static final Logger LOG = LoggerFactory.getLogger(UserRequestHandler.class);
protected final UserDataBinder dataBinder;
protected final String adminUser;
protected final DomainProcessEngine engine;
protected final UserDAO userDAO;
protected final EntityFactory entityFactory;
public FlowableUserRequestHandler(
final UserDataBinder dataBinder,
final String adminUser,
final DomainProcessEngine engine,
final UserDAO userDAO,
final EntityFactory entityFactory) {
this.dataBinder = dataBinder;
this.adminUser = adminUser;
this.engine = engine;
this.userDAO = userDAO;
this.entityFactory = entityFactory;
}
protected StringBuilder createProcessInstanceQuery(final String userKey) {
StringBuilder query = new StringBuilder().
append("SELECT DISTINCT ID_,BUSINESS_KEY_,PROC_DEF_ID_,PROC_INST_ID_,START_TIME_ FROM ").
append(engine.getManagementService().getTableName(ExecutionEntity.class)).
append(" WHERE BUSINESS_KEY_ NOT LIKE '").
append(FlowableRuntimeUtils.getProcBusinessKey(FlowableRuntimeUtils.WF_PROCESS_ID, "%")).
append('\'');
if (userKey != null) {
query.append(" AND BUSINESS_KEY_ LIKE '").
append(FlowableRuntimeUtils.getProcBusinessKey("%", userKey)).
append('\'');
}
query.append(" AND PARENT_ID_ IS NULL");
return query;
}
protected int countProcessInstances(final StringBuilder processInstanceQuery) {
return (int) engine.getRuntimeService().createNativeProcessInstanceQuery().
sql("SELECT COUNT(ID_) FROM "
+ StringUtils.substringAfter(processInstanceQuery.toString(), " FROM ")).
count();
}
protected UserRequest getUserRequest(final ProcessInstance procInst) {
Pair<String, String> split = FlowableRuntimeUtils.splitProcBusinessKey(procInst.getBusinessKey());
UserRequest userRequest = new UserRequest();
userRequest.setBpmnProcess(split.getLeft());
userRequest.setStartTime(procInst.getStartTime());
userRequest.setUsername(userDAO.find(split.getRight()).getUsername());
userRequest.setExecutionId(procInst.getId());
final Task task = engine.getTaskService().createTaskQuery()
.processInstanceId(procInst.getProcessInstanceId()).singleResult();
userRequest.setActivityId(task.getTaskDefinitionKey());
userRequest.setTaskId(task.getId());
userRequest.setHasForm(StringUtils.isNotBlank(task.getFormKey()));
return userRequest;
}
@Transactional(readOnly = true)
@Override
public Pair<Integer, List<UserRequest>> getUserRequests(
final String userKey,
final int page,
final int size,
final List<OrderByClause> orderByClauses) {
StringBuilder query = createProcessInstanceQuery(userKey);
Integer count = countProcessInstances(query);
if (!orderByClauses.isEmpty()) {
query.append(" ORDER BY");
for (OrderByClause clause : orderByClauses) {
boolean sorted = true;
switch (clause.getField().trim()) {
case "bpmnProcess":
query.append(" PROC_DEF_ID_");
break;
case "startTime":
query.append(" START_TIME_");
break;
case "executionId":
query.append(" PROC_INST_ID_");
break;
default:
LOG.warn("User request sort request by {}: unsupported, ignoring", clause.getField().trim());
sorted = false;
}
if (sorted) {
if (clause.getDirection() == OrderByClause.Direction.ASC) {
query.append(" ASC,");
} else {
query.append(" DESC,");
}
}
}
query.setLength(query.length() - 1);
}
List<UserRequest> result = engine.getRuntimeService().createNativeProcessInstanceQuery().
sql(query.toString()).
listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
map(this::getUserRequest).
collect(Collectors.toList());
return Pair.of(count, result);
}
protected User lazyLoad(final User user) {
// using BeanUtils to access all user's properties and trigger lazy loading - we are about to
// serialize a User instance for availability within workflow tasks, and this breaks transactions
BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
return user;
}
@Override
public UserRequest start(final String bpmnProcess, final User user, final WorkflowTaskExecInput inputVariables) {
Map<String, Object> variables = new HashMap<>();
variables.put(FlowableRuntimeUtils.WF_EXECUTOR, AuthContextUtils.getUsername());
variables.put(FlowableRuntimeUtils.USER, lazyLoad(user));
variables.put(FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
if (inputVariables != null) {
variables.putAll(inputVariables.getVariables());
}
ProcessInstance procInst = null;
try {
procInst = engine.getRuntimeService().startProcessInstanceByKey(bpmnProcess, variables);
} catch (FlowableException e) {
FlowableRuntimeUtils.throwException(e, "While starting " + bpmnProcess + " instance");
}
engine.getRuntimeService().updateBusinessKey(
Objects.requireNonNull(procInst).getProcessInstanceId(),
FlowableRuntimeUtils.getProcBusinessKey(bpmnProcess, user.getKey()));
return getUserRequest(engine.getRuntimeService().createProcessInstanceQuery().
processInstanceId(procInst.getProcessInstanceId()).singleResult());
}
@Override
public Pair<ProcessInstance, String> parse(final String executionId) {
ProcessInstance procInst = null;
try {
procInst = engine.getRuntimeService().
createProcessInstanceQuery().processInstanceId(executionId).singleResult();
if (procInst == null) {
throw new FlowableIllegalArgumentException("ProcessInstance with id " + executionId);
}
} catch (FlowableException e) {
LOG.error("Could find execution ProcessInstance with id {}", executionId, e);
throw new NotFoundException("User request execution with id " + executionId);
}
return Pair.of(procInst, getUserKey(procInst.getProcessInstanceId()));
}
@Override
public void cancel(final ProcessInstance procInst, final String reason) {
if (FlowableRuntimeUtils.WF_PROCESS_ID.equals(procInst.getProcessDefinitionKey())) {
throw new WorkflowException(new IllegalArgumentException(
"Cannot cancel a " + FlowableRuntimeUtils.WF_PROCESS_ID + " execution"));
}
engine.getRuntimeService().deleteProcessInstance(procInst.getId(), reason);
}
@Override
public void cancelByProcessDefinition(final String processDefinitionId) {
engine.getRuntimeService().
createProcessInstanceQuery().processDefinitionId(processDefinitionId).list().
forEach(procInst -> engine.getRuntimeService().deleteProcessInstance(
procInst.getId(), "Cascade Delete process definition " + processDefinitionId));
}
@Override
public void cancelByUser(final AnyDeletedEvent event) {
if (AuthContextUtils.getDomain().equals(event.getDomain()) && event.getAnyTypeKind() == AnyTypeKind.USER) {
String username = event.getAnyName();
engine.getRuntimeService().createNativeProcessInstanceQuery().
sql(createProcessInstanceQuery(event.getAnyKey()).toString()).
list().forEach(procInst -> engine.getRuntimeService().deleteProcessInstance(
procInst.getId(), "Cascade Delete user " + username));
}
}
protected static UserRequestFormPropertyType fromFlowableFormType(final FormType flowableFormType) {
UserRequestFormPropertyType result = UserRequestFormPropertyType.String;
if (null != flowableFormType.getName()) {
switch (flowableFormType.getName()) {
case "long":
result = UserRequestFormPropertyType.Long;
break;
case "enum":
result = UserRequestFormPropertyType.Enum;
break;
case "date":
result = UserRequestFormPropertyType.Date;
break;
case "boolean":
result = UserRequestFormPropertyType.Boolean;
break;
case "dropdown":
result = UserRequestFormPropertyType.Dropdown;
break;
case "password":
result = UserRequestFormPropertyType.Password;
break;
case "string":
default:
break;
}
}
return result;
}
protected UserRequestForm getForm(final Task task) {
return Optional.ofNullable(task).
map(t -> getForm(t, engine.getFormService().getTaskFormData(t.getId()))).
orElse(null);
}
protected UserRequestForm getForm(final Task task, final TaskFormData fd) {
UserRequestForm formTO =
getForm(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
formTO.setCreateTime(task.getCreateTime());
formTO.setDueDate(task.getDueDate());
formTO.setExecutionId(task.getExecutionId());
formTO.setFormKey(task.getFormKey());
formTO.setAssignee(task.getAssignee());
return formTO;
}
protected UserRequestForm getForm(final HistoricTaskInstance task) {
List<HistoricFormPropertyEntity> props = engine.getHistoryService().
createHistoricDetailQuery().taskId(task.getId()).list().stream().
filter(HistoricFormPropertyEntity.class::isInstance).
map(HistoricFormPropertyEntity.class::cast).
collect(Collectors.toList());
UserRequestForm formTO = getHistoricFormTO(
task.getProcessInstanceId(), task.getId(), task.getFormKey(), props);
formTO.setCreateTime(task.getCreateTime());
formTO.setDueDate(task.getDueDate());
formTO.setExecutionId(task.getExecutionId());
formTO.setFormKey(task.getFormKey());
formTO.setAssignee(task.getAssignee());
HistoricActivityInstance historicActivityInstance = engine.getHistoryService().
createHistoricActivityInstanceQuery().
executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult();
if (historicActivityInstance != null) {
formTO.setCreateTime(historicActivityInstance.getStartTime());
formTO.setDueDate(historicActivityInstance.getEndTime());
}
return formTO;
}
protected String getUserKey(final String procInstId) {
String procBusinessKey = engine.getRuntimeService().createProcessInstanceQuery().
processInstanceId(procInstId).singleResult().getBusinessKey();
return StringUtils.substringAfter(procBusinessKey, ":");
}
protected UserRequestForm getHistoricFormTO(
final String procInstId,
final String taskId,
final String formKey,
final List<HistoricFormPropertyEntity> props) {
UserRequestForm formTO = new UserRequestForm();
formTO.setBpmnProcess(engine.getRuntimeService().createProcessInstanceQuery().
processInstanceId(procInstId).singleResult().getProcessDefinitionKey());
User user = userDAO.find(getUserKey(procInstId));
if (user == null) {
throw new NotFoundException("User for process instance id " + procInstId);
}
formTO.setUsername(user.getUsername());
formTO.setTaskId(taskId);
formTO.setFormKey(formKey);
formTO.setUserTO(engine.getRuntimeService().
getVariable(procInstId, FlowableRuntimeUtils.USER_TO, UserTO.class));
formTO.setUserUR(engine.getRuntimeService().
getVariable(procInstId, FlowableRuntimeUtils.USER_UR, UserUR.class));
formTO.getProperties().addAll(props.stream().map(prop -> {
UserRequestFormProperty propertyTO = new UserRequestFormProperty();
propertyTO.setId(prop.getPropertyId());
propertyTO.setName(prop.getPropertyId());
propertyTO.setValue(prop.getPropertyValue());
return propertyTO;
}).collect(Collectors.toList()));
return formTO;
}
@SuppressWarnings("unchecked")
protected UserRequestForm getForm(
final String procInstId,
final String taskId,
final String formKey,
final List<FormProperty> props) {
UserRequestForm formTO = new UserRequestForm();
formTO.setBpmnProcess(engine.getRuntimeService().createProcessInstanceQuery().
processInstanceId(procInstId).singleResult().getProcessDefinitionKey());
User user = userDAO.find(getUserKey(procInstId));
if (user == null) {
throw new NotFoundException("User for process instance id " + procInstId);
}
formTO.setUsername(user.getUsername());
formTO.setTaskId(taskId);
formTO.setFormKey(formKey);
formTO.setUserTO(engine.getRuntimeService().
getVariable(procInstId, FlowableRuntimeUtils.USER_TO, UserTO.class));
formTO.setUserUR(engine.getRuntimeService().
getVariable(procInstId, FlowableRuntimeUtils.USER_UR, UserUR.class));
formTO.getProperties().addAll(props.stream().map(fProp -> {
UserRequestFormProperty propertyTO = new UserRequestFormProperty();
propertyTO.setId(fProp.getId());
propertyTO.setName(fProp.getName());
propertyTO.setReadable(fProp.isReadable());
propertyTO.setRequired(fProp.isRequired());
propertyTO.setWritable(fProp.isWritable());
propertyTO.setValue(fProp.getValue());
propertyTO.setType(fromFlowableFormType(fProp.getType()));
switch (propertyTO.getType()) {
case Date:
propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
break;
case Enum:
((Map<String, String>) fProp.getType().getInformation("values")).forEach((key, value) -> {
propertyTO.getEnumValues().add(new UserRequestFormPropertyValue(key, value));
});
break;
case Dropdown:
String valueProviderBean = (String) fProp.getType().getInformation(DropdownValueProvider.NAME);
try {
DropdownValueProvider valueProvider = ApplicationContextProvider.getApplicationContext().
getBean(valueProviderBean, DropdownValueProvider.class);
valueProvider.getValues().forEach((key, value) -> {
propertyTO.getDropdownValues().add(new UserRequestFormPropertyValue(key, value));
});
} catch (Exception e) {
LOG.error("Could not find bean {} of type {} for form property {}",
valueProviderBean, DropdownValueProvider.class.getName(), propertyTO.getId(), e);
}
break;
default:
}
return propertyTO;
}).collect(Collectors.toList()));
return formTO;
}
@Override
public UserRequestForm getForm(final String userKey, final String taskId) {
TaskQuery query = engine.getTaskService().createTaskQuery().taskId(taskId);
if (userKey != null) {
query.processInstanceBusinessKeyLike(FlowableRuntimeUtils.getProcBusinessKey("%", userKey));
}
String authUser = AuthContextUtils.getUsername();
return adminUser.equals(authUser)
? getForm(getTask(taskId))
: getForm(query.or().taskCandidateUser(authUser).taskAssignee(authUser).endOr().singleResult());
}
@Transactional(readOnly = true)
@Override
public Pair<Integer, List<UserRequestForm>> getForms(
final String userKey,
final int page,
final int size,
final List<OrderByClause> ob) {
TaskQuery query = engine.getTaskService().createTaskQuery().taskWithFormKey();
if (userKey != null) {
query.processInstanceBusinessKeyLike(FlowableRuntimeUtils.getProcBusinessKey("%", userKey));
}
String authUser = AuthContextUtils.getUsername();
return adminUser.equals(authUser)
? getForms(query, page, size, ob)
: getForms(query.or().taskCandidateUser(authUser).taskAssignee(authUser).endOr(), page, size, ob);
}
protected Pair<Integer, List<UserRequestForm>> getForms(
final TaskQuery query, final int page, final int size, final List<OrderByClause> orderByClauses) {
for (OrderByClause clause : orderByClauses) {
boolean sorted = true;
switch (clause.getField().trim()) {
case "bpmnProcess":
query.orderByProcessDefinitionId();
break;
case "executionId":
query.orderByExecutionId();
break;
case "taskId":
query.orderByTaskId();
break;
case "createTime":
query.orderByTaskCreateTime();
break;
case "dueDate":
query.orderByTaskDueDate();
break;
case "assignee":
query.orderByTaskAssignee();
break;
default:
LOG.warn("Form sort request by {}: unsupported, ignoring", clause.getField().trim());
sorted = false;
}
if (sorted) {
if (clause.getDirection() == OrderByClause.Direction.ASC) {
query.asc();
} else {
query.desc();
}
}
}
List<UserRequestForm> result = query.listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
map(task -> task instanceof HistoricTaskInstance
? FlowableUserRequestHandler.this.getForm((HistoricTaskInstance) task)
: FlowableUserRequestHandler.this.getForm(task)).
collect(Collectors.toList());
return Pair.of((int) query.count(), result);
}
protected Pair<Task, TaskFormData> parseTask(final String taskId) {
Task task = getTask(taskId);
TaskFormData formData;
try {
formData = engine.getFormService().getTaskFormData(task.getId());
} catch (FlowableException e) {
throw new NotFoundException("Form for Flowable Task " + taskId, e);
}
return Pair.of(task, formData);
}
protected Task getTask(final String taskId) throws NotFoundException {
Task task;
try {
task = engine.getTaskService().createTaskQuery().taskWithFormKey().taskId(taskId).singleResult();
if (task == null) {
throw new FlowableException("NULL result");
}
} catch (FlowableException e) {
throw new NotFoundException("Flowable Task " + taskId, e);
}
return task;
}
@Override
public UserRequestForm claimForm(final String taskId) {
Pair<Task, TaskFormData> parsed = parseTask(taskId);
String authUser = AuthContextUtils.getUsername();
if (!adminUser.equals(authUser)) {
List<Task> tasksForUser = engine.getTaskService().createTaskQuery().
taskWithFormKey().taskId(taskId).
or().taskCandidateUser(authUser).taskAssignee(authUser).endOr().list();
if (tasksForUser.isEmpty()) {
throw new WorkflowException(
new IllegalArgumentException(authUser + " is not candidate nor assignee of task " + taskId));
}
}
boolean hasAssignees = engine.getTaskService().getIdentityLinksForTask(taskId).stream().
anyMatch(identityLink -> IdentityLinkType.ASSIGNEE.equals(identityLink.getType()));
if (hasAssignees) {
try {
engine.getTaskService().unclaim(taskId);
} catch (FlowableException e) {
throw new WorkflowException("While unclaiming task " + taskId, e);
}
}
Task task;
try {
engine.getTaskService().claim(taskId, authUser);
task = engine.getTaskService().createTaskQuery().taskWithFormKey().taskId(taskId).singleResult();
} catch (FlowableException e) {
throw new WorkflowException("While reading task " + taskId, e);
}
return FlowableUserRequestHandler.this.getForm(task, parsed.getRight());
}
@Override
public UserRequestForm unclaimForm(final String taskId) {
Pair<Task, TaskFormData> parsed = parseTask(taskId);
Task task;
try {
engine.getTaskService().unclaim(taskId);
task = engine.getTaskService().createTaskQuery().taskWithFormKey().taskId(taskId).singleResult();
} catch (FlowableException e) {
throw new WorkflowException("While unclaiming task " + taskId, e);
}
return FlowableUserRequestHandler.this.getForm(task, parsed.getRight());
}
protected Map<String, String> getPropertiesForSubmit(final UserRequestForm form) {
Map<String, String> props = new HashMap<>();
form.getProperties().stream().
filter(UserRequestFormProperty::isWritable).
forEach(prop -> props.put(prop.getId(), prop.getValue()));
return Collections.unmodifiableMap(props);
}
protected <T> T getHistoricVariable(
final List<HistoricVariableInstance> historicVariables, final String name, final Class<T> valueRef) {
return historicVariables.stream().filter(v -> name.equals(v.getVariableName())).
findFirst().map(v -> valueRef.cast(v.getValue())).orElse(null);
}
@Override
@SuppressWarnings("unchecked")
public UserWorkflowResult<UserUR> submitForm(final UserRequestForm form) {
Pair<Task, TaskFormData> parsed = parseTask(form.getTaskId());
String authUser = AuthContextUtils.getUsername();
if (!parsed.getLeft().getAssignee().equals(authUser)) {
throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
+ parsed.getLeft().getAssignee() + " but submitted by " + authUser));
}
String procInstID = parsed.getLeft().getProcessInstanceId();
User user = userDAO.find(getUserKey(procInstID));
if (user == null) {
throw new NotFoundException("User with key " + getUserKey(procInstID));
}
Set<String> preTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID);
engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.TASK, "submit");
engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.FORM_SUBMITTER, authUser);
engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.USER, lazyLoad(user));
try {
engine.getFormService().submitTaskFormData(form.getTaskId(), getPropertiesForSubmit(form));
} catch (FlowableException e) {
FlowableRuntimeUtils.throwException(e, "While submitting form for task " + form.getTaskId());
}
Set<String> postTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID);
postTasks.removeAll(preTasks);
postTasks.add(form.getTaskId());
if (procInstID.equals(FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey()))) {
FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
}
user = userDAO.save(user);
PropagationByResource<String> propByRes;
PropagationByResource<Pair<String, String>> propByLinkedAccount;
String clearPassword = null;
UserUR userUR;
if (engine.getRuntimeService().
createProcessInstanceQuery().processInstanceId(procInstID).singleResult() == null) {
List<HistoricVariableInstance> historicVariables = engine.getHistoryService().
createHistoricVariableInstanceQuery().processInstanceId(procInstID).list();
// see if there is any propagation to be done
propByRes = getHistoricVariable(
historicVariables, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
propByLinkedAccount = getHistoricVariable(
historicVariables, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
// fetch - if available - the encrypted password
String encryptedPwd = getHistoricVariable(
historicVariables, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
if (StringUtils.isNotBlank(encryptedPwd)) {
clearPassword = FlowableRuntimeUtils.decrypt(encryptedPwd);
}
userUR = getHistoricVariable(historicVariables, FlowableRuntimeUtils.USER_UR, UserUR.class);
} else {
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.FORM_SUBMITTER);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_TO);
// see if there is any propagation to be done
propByRes = engine.getRuntimeService().getVariable(
procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
propByLinkedAccount = engine.getRuntimeService().getVariable(
procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
// fetch - if available - the encrypted password
String encryptedPwd = engine.getRuntimeService().getVariable(
procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD);
if (StringUtils.isNotBlank(encryptedPwd)) {
clearPassword = FlowableRuntimeUtils.decrypt(encryptedPwd);
}
Boolean enabled = engine.getRuntimeService().getVariable(
procInstID, FlowableRuntimeUtils.ENABLED, Boolean.class);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.ENABLED);
// supports approval chains
FlowableRuntimeUtils.saveForFormSubmit(
engine,
procInstID,
dataBinder.getUserTO(user, true),
clearPassword,
enabled,
propByRes,
propByLinkedAccount);
userUR = engine.getRuntimeService().getVariable(
procInstID, FlowableRuntimeUtils.USER_UR, UserUR.class);
engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_UR);
}
if (userUR == null) {
userUR = new UserUR();
userUR.setKey(user.getKey());
userUR.setPassword(new PasswordPatch.Builder().onSyncope(true).value(clearPassword).build());
Set<String> pwdResources = new HashSet<>();
if (propByRes != null) {
pwdResources.addAll(propByRes.get(ResourceOperation.CREATE));
}
if (propByLinkedAccount != null) {
pwdResources.addAll(propByLinkedAccount.get(ResourceOperation.CREATE).stream().
map(Pair::getLeft).collect(Collectors.toList()));
}
userUR.getPassword().getResources().addAll(pwdResources);
}
return new UserWorkflowResult<>(userUR, propByRes, propByLinkedAccount, postTasks);
}
}