http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java new file mode 100644 index 0000000..4b3d38d --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java @@ -0,0 +1,892 @@ +/* + * 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.workflow.activiti; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import javax.ws.rs.NotFoundException; +import org.activiti.bpmn.converter.BpmnXMLConverter; +import org.activiti.bpmn.model.BpmnModel; +import org.activiti.editor.constants.ModelDataJsonConstants; +import org.activiti.editor.language.json.converter.BpmnJsonConverter; +import org.activiti.engine.ActivitiException; +import org.activiti.engine.FormService; +import org.activiti.engine.HistoryService; +import org.activiti.engine.RepositoryService; +import org.activiti.engine.RuntimeService; +import org.activiti.engine.TaskService; +import org.activiti.engine.form.FormProperty; +import org.activiti.engine.form.FormType; +import org.activiti.engine.form.TaskFormData; +import org.activiti.engine.history.HistoricActivityInstance; +import org.activiti.engine.history.HistoricDetail; +import org.activiti.engine.history.HistoricTaskInstance; +import org.activiti.engine.impl.persistence.entity.HistoricFormPropertyEntity; +import org.activiti.engine.query.Query; +import org.activiti.engine.repository.Model; +import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.task.Task; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO; +import org.apache.syncope.common.lib.to.WorkflowFormTO; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.WorkflowFormPropertyType; +import org.apache.syncope.core.misc.security.AuthContextUtil; +import org.apache.syncope.core.misc.security.UnauthorizedRoleException; +import org.apache.syncope.core.misc.spring.BeanUtils; +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.workflow.api.WorkflowDefinitionFormat; +import org.apache.syncope.core.workflow.api.WorkflowException; +import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +/** + * Activiti (http://www.activiti.org/) based implementation. + */ +public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter { + + /** + * Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ActivitiUserWorkflowAdapter.class); + + private static final String[] PROPERTY_IGNORE_PROPS = { "type" }; + + public static final String WF_PROCESS_ID = "userWorkflow"; + + public static final String WF_PROCESS_RESOURCE = "userWorkflow.bpmn20.xml"; + + public static final String WF_DGRM_RESOURCE = "userWorkflow.userWorkflow.png"; + + public static final String USER = "user"; + + public static final String WF_EXECUTOR = "wfExecutor"; + + public static final String FORM_SUBMITTER = "formSubmitter"; + + public static final String USER_TO = "userTO"; + + public static final String ENABLED = "enabled"; + + public static final String USER_MOD = "userMod"; + + public static final String EMAIL_KIND = "emailKind"; + + public static final String TASK = "task"; + + public static final String TOKEN = "token"; + + public static final String PASSWORD = "password"; + + public static final String PROP_BY_RESOURCE = "propByResource"; + + public static final String PROPAGATE_ENABLE = "propagateEnable"; + + public static final String ENCRYPTED_PWD = "encryptedPwd"; + + public static final String TASK_IS_FORM = "taskIsForm"; + + public static final String MODEL_DATA_JSON_MODEL = "model"; + + public static final String STORE_PASSWORD = "storePassword"; + + public static final String EVENT = "event"; + + @Resource(name = "adminUser") + private String adminUser; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private TaskService taskService; + + @Autowired + private FormService formService; + + @Autowired + private HistoryService historyService; + + @Autowired + private RepositoryService repositoryService; + + @Autowired + private ActivitiImportUtils importUtils; + + @Autowired + private UserDataBinder userDataBinder; + + @Override + public String getPrefix() { + return "ACT_"; + } + + private void throwException(final ActivitiException e, final String defaultMessage) { + if (e.getCause() != null) { + if (e.getCause().getCause() instanceof SyncopeClientException) { + throw (SyncopeClientException) e.getCause().getCause(); + } else if (e.getCause().getCause() instanceof ParsingValidationException) { + throw (ParsingValidationException) e.getCause().getCause(); + } else if (e.getCause().getCause() instanceof InvalidEntityException) { + throw (InvalidEntityException) e.getCause().getCause(); + } + } + + throw new WorkflowException(defaultMessage, e); + } + + private void updateStatus(final User user) { + List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list(); + if (tasks.isEmpty() || tasks.size() > 1) { + LOG.warn("While setting user status: unexpected task number ({})", tasks.size()); + } else { + user.setStatus(tasks.get(0).getTaskDefinitionKey()); + } + } + + private String getFormTask(final User user) { + String result = null; + + List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list(); + if (tasks.isEmpty() || tasks.size() > 1) { + LOG.warn("While checking if form task: unexpected task number ({})", tasks.size()); + } else { + try { + TaskFormData formData = formService.getTaskFormData(tasks.get(0).getId()); + if (formData != null && !formData.getFormProperties().isEmpty()) { + result = tasks.get(0).getId(); + } + } catch (ActivitiException e) { + LOG.warn("Could not get task form data", e); + } + } + + return result; + } + + private Set<String> getPerformedTasks(final User user) { + final Set<String> result = new HashSet<>(); + + for (HistoricActivityInstance task + : historyService.createHistoricActivityInstanceQuery().executionId(user.getWorkflowId()).list()) { + + result.add(task.getActivityId()); + } + + return result; + } + + /** + * Saves resources to be propagated and password for later - after form submission - propagation. + */ + private void saveForFormSubmit(final User user, final String password, + final PropagationByResource propByRes) { + + String formTaskId = getFormTask(user); + if (formTaskId != null) { + // SYNCOPE-238: This is needed to simplify the task query in this.getForms() + taskService.setVariableLocal(formTaskId, TASK_IS_FORM, Boolean.TRUE); + runtimeService.setVariable(user.getWorkflowId(), PROP_BY_RESOURCE, propByRes); + if (propByRes != null) { + propByRes.clear(); + } + + if (StringUtils.isNotBlank(password)) { + runtimeService.setVariable(user.getWorkflowId(), ENCRYPTED_PWD, encrypt(password)); + } + } + } + + @Override + public WorkflowResult<Map.Entry<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck, + final boolean storePassword) throws WorkflowException { + + return create(userTO, disablePwdPolicyCheck, null, storePassword); + } + + @Override + public WorkflowResult<Map.Entry<Long, Boolean>> create(UserTO userTO, boolean storePassword) throws + UnauthorizedRoleException, WorkflowException { + + return create(userTO, false, storePassword); + } + + @Override + public WorkflowResult<Map.Entry<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck, + final Boolean enabled, final boolean storePassword) throws WorkflowException { + + final Map<String, Object> variables = new HashMap<>(); + variables.put(WF_EXECUTOR, AuthContextUtil.getAuthenticatedUsername()); + variables.put(USER_TO, userTO); + variables.put(ENABLED, enabled); + variables.put(STORE_PASSWORD, storePassword); + + ProcessInstance processInstance = null; + try { + processInstance = runtimeService.startProcessInstanceByKey(WF_PROCESS_ID, variables); + } catch (ActivitiException e) { + throwException(e, "While starting " + WF_PROCESS_ID + " instance"); + } + + User user = + runtimeService.getVariable(processInstance.getProcessInstanceId(), USER, User.class); + + Boolean updatedEnabled = + runtimeService.getVariable(processInstance.getProcessInstanceId(), ENABLED, Boolean.class); + if (updatedEnabled != null) { + user.setSuspended(!updatedEnabled); + } + + // this will make UserValidator not to consider password policies at all + if (disablePwdPolicyCheck) { + user.removeClearPassword(); + } + + updateStatus(user); + user = userDAO.save(user); + + Boolean propagateEnable = + runtimeService.getVariable(processInstance.getProcessInstanceId(), PROPAGATE_ENABLE, Boolean.class); + if (propagateEnable == null) { + propagateEnable = enabled; + } + + PropagationByResource propByRes = new PropagationByResource(); + propByRes.set(ResourceOperation.CREATE, user.getResourceNames()); + + saveForFormSubmit(user, userTO.getPassword(), propByRes); + + return new WorkflowResult<Map.Entry<Long, Boolean>>( + new SimpleEntry<>(user.getKey(), propagateEnable), propByRes, getPerformedTasks(user)); + } + + private Set<String> doExecuteTask(final User user, final String task, + final Map<String, Object> moreVariables) throws WorkflowException { + + Set<String> preTasks = getPerformedTasks(user); + + final Map<String, Object> variables = new HashMap<>(); + variables.put(WF_EXECUTOR, AuthContextUtil.getAuthenticatedUsername()); + variables.put(TASK, task); + + // 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)); + variables.put(USER, user); + + if (moreVariables != null && !moreVariables.isEmpty()) { + variables.putAll(moreVariables); + } + + if (StringUtils.isBlank(user.getWorkflowId())) { + throw new WorkflowException(new NotFoundException("Empty workflow id for " + user)); + } + + List<Task> tasks = taskService.createTaskQuery().processInstanceId(user.getWorkflowId()).list(); + if (tasks.size() == 1) { + try { + taskService.complete(tasks.get(0).getId(), variables); + } catch (ActivitiException e) { + throwException(e, "While completing task '" + tasks.get(0).getName() + "' for " + user); + } + } else { + LOG.warn("Expected a single task, found {}", tasks.size()); + } + + Set<String> postTasks = getPerformedTasks(user); + postTasks.removeAll(preTasks); + postTasks.add(task); + return postTasks; + } + + @Override + protected WorkflowResult<Long> doActivate(final User user, final String token) + throws WorkflowException { + + Set<String> tasks = doExecuteTask(user, "activate", Collections.singletonMap(TOKEN, (Object) token)); + + updateStatus(user); + User updated = userDAO.save(user); + + return new WorkflowResult<>(updated.getKey(), null, tasks); + } + + @Override + protected WorkflowResult<Map.Entry<UserMod, Boolean>> doUpdate(final User user, final UserMod userMod) + throws WorkflowException { + + Set<String> tasks = doExecuteTask(user, "update", Collections.singletonMap(USER_MOD, (Object) userMod)); + + updateStatus(user); + User updated = userDAO.save(user); + + PropagationByResource propByRes = + runtimeService.getVariable(user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class); + + saveForFormSubmit(updated, userMod.getPassword(), propByRes); + + Boolean propagateEnable = runtimeService.getVariable(user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class); + + return new WorkflowResult<Map.Entry<UserMod, Boolean>>( + new SimpleEntry<>(userMod, propagateEnable), propByRes, tasks); + } + + @Override + @Transactional(rollbackFor = { Throwable.class }) + protected WorkflowResult<Long> doSuspend(final User user) throws WorkflowException { + Set<String> performedTasks = doExecuteTask(user, "suspend", null); + updateStatus(user); + User updated = userDAO.save(user); + + return new WorkflowResult<>(updated.getKey(), null, performedTasks); + } + + @Override + protected WorkflowResult<Long> doReactivate(final User user) throws WorkflowException { + Set<String> performedTasks = doExecuteTask(user, "reactivate", null); + updateStatus(user); + + User updated = userDAO.save(user); + + return new WorkflowResult<>(updated.getKey(), null, performedTasks); + } + + @Override + protected void doRequestPasswordReset(final User user) throws WorkflowException { + Map<String, Object> variables = new HashMap<>(2); + variables.put(USER_TO, userDataBinder.getUserTO(user)); + variables.put(EVENT, "requestPasswordReset"); + + doExecuteTask(user, "requestPasswordReset", variables); + userDAO.save(user); + } + + @Override + protected void doConfirmPasswordReset(final User user, final String token, final String password) + throws WorkflowException { + + Map<String, Object> variables = new HashMap<>(4); + variables.put(TOKEN, token); + variables.put(PASSWORD, password); + variables.put(USER_TO, userDataBinder.getUserTO(user)); + variables.put(EVENT, "confirmPasswordReset"); + + doExecuteTask(user, "confirmPasswordReset", variables); + userDAO.save(user); + } + + @Override + protected void doDelete(final User user) throws WorkflowException { + doExecuteTask(user, "delete", null); + + PropagationByResource propByRes = new PropagationByResource(); + propByRes.set(ResourceOperation.DELETE, user.getResourceNames()); + + saveForFormSubmit(user, null, propByRes); + + if (runtimeService.createProcessInstanceQuery(). + processInstanceId(user.getWorkflowId()).active().list().isEmpty()) { + + userDAO.delete(user.getKey()); + + if (!historyService.createHistoricProcessInstanceQuery(). + processInstanceId(user.getWorkflowId()).list().isEmpty()) { + + historyService.deleteHistoricProcessInstance(user.getWorkflowId()); + } + } else { + updateStatus(user); + userDAO.save(user); + } + } + + @Override + public WorkflowResult<Long> execute(final UserTO userTO, final String taskId) + throws UnauthorizedRoleException, WorkflowException { + + User user = userDAO.authFetch(userTO.getKey()); + + final Map<String, Object> variables = new HashMap<>(); + variables.put(USER_TO, userTO); + + Set<String> performedTasks = doExecuteTask(user, taskId, variables); + updateStatus(user); + User updated = userDAO.save(user); + + return new WorkflowResult<>(updated.getKey(), null, performedTasks); + } + + protected ProcessDefinition getProcessDefinition() { + try { + return repositoryService.createProcessDefinitionQuery().processDefinitionKey( + ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult(); + } catch (ActivitiException e) { + throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e); + } + + } + + protected Model getModel(final ProcessDefinition procDef) { + try { + Model model = repositoryService.createModelQuery().deploymentId(procDef.getDeploymentId()).singleResult(); + if (model == null) { + throw new NotFoundException("Could not find Model for deployment " + procDef.getDeploymentId()); + } + return model; + } catch (Exception e) { + throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e); + } + } + + protected void exportProcessResource(final String resourceName, final OutputStream os) { + ProcessDefinition procDef = getProcessDefinition(); + + InputStream procDefIS = repositoryService.getResourceAsStream(procDef.getDeploymentId(), resourceName); + try { + IOUtils.copy(procDefIS, os); + } catch (IOException e) { + LOG.error("While exporting workflow definition {}", procDef.getKey(), e); + } finally { + IOUtils.closeQuietly(procDefIS); + } + } + + protected void exportProcessModel(final OutputStream os) { + Model model = getModel(getProcessDefinition()); + + ObjectMapper objectMapper = new ObjectMapper(); + try { + ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); + modelNode.put(ModelDataJsonConstants.MODEL_ID, model.getId()); + modelNode.replace(MODEL_DATA_JSON_MODEL, + objectMapper.readTree(repositoryService.getModelEditorSource(model.getId()))); + + os.write(modelNode.toString().getBytes()); + } catch (IOException e) { + LOG.error("While exporting workflow definition {}", model.getId(), e); + } + } + + @Override + public void exportDefinition(final WorkflowDefinitionFormat format, final OutputStream os) + throws WorkflowException { + + switch (format) { + case JSON: + exportProcessModel(os); + break; + + case XML: + default: + exportProcessResource(WF_PROCESS_RESOURCE, os); + } + } + + @Override + public void exportDiagram(final OutputStream os) throws WorkflowException { + exportProcessResource(WF_DGRM_RESOURCE, os); + } + + @Override + public void importDefinition(final WorkflowDefinitionFormat format, final String definition) + throws WorkflowException { + + Model model = getModel(getProcessDefinition()); + switch (format) { + case JSON: + JsonNode definitionNode; + try { + definitionNode = new ObjectMapper().readTree(definition); + if (definitionNode.has(MODEL_DATA_JSON_MODEL)) { + definitionNode = definitionNode.get(MODEL_DATA_JSON_MODEL); + } + if (!definitionNode.has(BpmnJsonConverter.EDITOR_CHILD_SHAPES)) { + throw new IllegalArgumentException( + "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES); + } + + BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode); + importUtils.fromXML(new BpmnXMLConverter().convertToXML(bpmnModel)); + } catch (Exception e) { + throw new WorkflowException("While updating process " + + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e); + } + + importUtils.fromJSON(definitionNode.toString().getBytes(), getProcessDefinition(), model); + break; + + case XML: + default: + importUtils.fromXML(definition.getBytes()); + + importUtils.fromJSON(getProcessDefinition(), model); + } + } + + private WorkflowFormPropertyType fromActivitiFormType(final FormType activitiFormType) { + WorkflowFormPropertyType result = WorkflowFormPropertyType.String; + + if ("string".equals(activitiFormType.getName())) { + result = WorkflowFormPropertyType.String; + } + if ("long".equals(activitiFormType.getName())) { + result = WorkflowFormPropertyType.Long; + } + if ("enum".equals(activitiFormType.getName())) { + result = WorkflowFormPropertyType.Enum; + } + if ("date".equals(activitiFormType.getName())) { + result = WorkflowFormPropertyType.Date; + } + if ("boolean".equals(activitiFormType.getName())) { + result = WorkflowFormPropertyType.Boolean; + } + + return result; + } + + private WorkflowFormTO getFormTO(final Task task) { + return getFormTO(task, formService.getTaskFormData(task.getId())); + } + + private WorkflowFormTO getFormTO(final Task task, final TaskFormData fd) { + final WorkflowFormTO formTO = + getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties()); + + BeanUtils.copyProperties(task, formTO); + return formTO; + } + + private WorkflowFormTO getFormTO(final HistoricTaskInstance task) { + final List<HistoricFormPropertyEntity> props = new ArrayList<>(); + + for (HistoricDetail historicDetail : historyService.createHistoricDetailQuery().taskId(task.getId()).list()) { + + if (historicDetail instanceof HistoricFormPropertyEntity) { + props.add((HistoricFormPropertyEntity) historicDetail); + } + } + + final WorkflowFormTO formTO = getHistoricFormTO( + task.getProcessInstanceId(), task.getId(), task.getFormKey(), props); + BeanUtils.copyProperties(task, formTO); + + final HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery(). + executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult(); + + if (historicActivityInstance != null) { + formTO.setCreateTime(historicActivityInstance.getStartTime()); + formTO.setDueDate(historicActivityInstance.getEndTime()); + } + + return formTO; + } + + private WorkflowFormTO getHistoricFormTO( + final String processInstanceId, + final String taskId, + final String formKey, + final List<HistoricFormPropertyEntity> props) { + + WorkflowFormTO formTO = new WorkflowFormTO(); + + User user = userDAO.findByWorkflowId(processInstanceId); + if (user == null) { + throw new NotFoundException("User with workflow id " + processInstanceId); + } + formTO.setUserKey(user.getKey()); + + formTO.setTaskId(taskId); + formTO.setKey(formKey); + + for (HistoricFormPropertyEntity prop : props) { + WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO(); + propertyTO.setId(prop.getPropertyId()); + propertyTO.setName(prop.getPropertyId()); + propertyTO.setValue(prop.getPropertyValue()); + formTO.addProperty(propertyTO); + } + + return formTO; + } + + @SuppressWarnings("unchecked") + private WorkflowFormTO getFormTO( + final String processInstanceId, + final String taskId, + final String formKey, + final List<FormProperty> properties) { + + WorkflowFormTO formTO = new WorkflowFormTO(); + + User user = userDAO.findByWorkflowId(processInstanceId); + if (user == null) { + throw new NotFoundException("User with workflow id " + processInstanceId); + } + formTO.setUserKey(user.getKey()); + + formTO.setTaskId(taskId); + formTO.setKey(formKey); + + for (FormProperty fProp : properties) { + WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO(); + BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS); + propertyTO.setType(fromActivitiFormType(fProp.getType())); + + if (propertyTO.getType() == WorkflowFormPropertyType.Date) { + propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern")); + } + if (propertyTO.getType() == WorkflowFormPropertyType.Enum) { + propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values")); + } + + formTO.addProperty(propertyTO); + } + + return formTO; + } + + @Transactional(readOnly = true) + @Override + public List<WorkflowFormTO> getForms() { + List<WorkflowFormTO> forms = new ArrayList<>(); + + final String authUser = AuthContextUtil.getAuthenticatedUsername(); + if (adminUser.equals(authUser)) { + forms.addAll(getForms(taskService.createTaskQuery(). + taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE))); + } else { + User user = userDAO.find(authUser); + if (user == null) { + throw new NotFoundException("Syncope User " + authUser); + } + + forms.addAll(getForms(taskService.createTaskQuery(). + taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE). + taskCandidateOrAssigned(user.getKey().toString()))); + + List<String> candidateGroups = new ArrayList<>(); + for (Long roleId : user.getRoleKeys()) { + candidateGroups.add(roleId.toString()); + } + if (!candidateGroups.isEmpty()) { + forms.addAll(getForms(taskService.createTaskQuery(). + taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE). + taskCandidateGroupIn(candidateGroups))); + } + } + + return forms; + } + + @Override + public List<WorkflowFormTO> getForms(final String workflowId, final String name) { + List<WorkflowFormTO> forms = getForms( + taskService.createTaskQuery().processInstanceId(workflowId).taskName(name). + taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)); + + forms.addAll(getForms(historyService.createHistoricTaskInstanceQuery().taskName(name). + taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE))); + + return forms; + } + + private <T extends Query<?, ?>, U extends Object> List<WorkflowFormTO> getForms(final Query<T, U> query) { + List<WorkflowFormTO> forms = new ArrayList<>(); + + for (U obj : query.list()) { + try { + if (obj instanceof HistoricTaskInstance) { + forms.add(getFormTO((HistoricTaskInstance) obj)); + } else if (obj instanceof Task) { + forms.add(getFormTO((Task) obj)); + } else { + throw new ActivitiException( + "Failure retrieving form", new IllegalArgumentException("Invalid task type")); + } + } catch (ActivitiException e) { + LOG.debug("No form found for task {}", obj, e); + } + } + + return forms; + } + + @Override + public WorkflowFormTO getForm(final String workflowId) + throws NotFoundException, WorkflowException { + + Task task; + try { + task = taskService.createTaskQuery().processInstanceId(workflowId).singleResult(); + } catch (ActivitiException e) { + throw new WorkflowException("While reading form for workflow instance " + workflowId, e); + } + + TaskFormData formData; + try { + formData = formService.getTaskFormData(task.getId()); + } catch (ActivitiException e) { + LOG.debug("No form found for task {}", task.getId(), e); + formData = null; + } + + WorkflowFormTO result = null; + if (formData != null && !formData.getFormProperties().isEmpty()) { + result = getFormTO(task); + } + + return result; + } + + private Map.Entry<Task, TaskFormData> checkTask(final String taskId, final String authUser) { + Task task; + try { + task = taskService.createTaskQuery().taskId(taskId).singleResult(); + } catch (ActivitiException e) { + throw new NotFoundException("Activiti Task " + taskId, e); + } + + TaskFormData formData; + try { + formData = formService.getTaskFormData(task.getId()); + } catch (ActivitiException e) { + throw new NotFoundException("Form for Activiti Task " + taskId, e); + } + + if (!adminUser.equals(authUser)) { + User user = userDAO.find(authUser); + if (user == null) { + throw new NotFoundException("Syncope User " + authUser); + } + } + + return new SimpleEntry<>(task, formData); + } + + @Transactional + @Override + public WorkflowFormTO claimForm(final String taskId) + throws WorkflowException { + + final String authUser = AuthContextUtil.getAuthenticatedUsername(); + Map.Entry<Task, TaskFormData> checked = checkTask(taskId, authUser); + + if (!adminUser.equals(authUser)) { + List<Task> tasksForUser = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(authUser).list(); + if (tasksForUser.isEmpty()) { + throw new WorkflowException( + new IllegalArgumentException(authUser + " is not candidate for task " + taskId)); + } + } + + Task task; + try { + taskService.setOwner(taskId, authUser); + task = taskService.createTaskQuery().taskId(taskId).singleResult(); + } catch (ActivitiException e) { + throw new WorkflowException("While reading task " + taskId, e); + } + + return getFormTO(task, checked.getValue()); + } + + @Transactional + @Override + public WorkflowResult<UserMod> submitForm(final WorkflowFormTO form) + throws WorkflowException { + + final String authUser = AuthContextUtil.getAuthenticatedUsername(); + Map.Entry<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser); + + if (!checked.getKey().getOwner().equals(authUser)) { + throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to " + + checked.getKey().getOwner() + " but submitted by " + authUser)); + } + + User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId()); + if (user == null) { + throw new NotFoundException("User with workflow id " + checked.getKey().getProcessInstanceId()); + } + + Set<String> preTasks = getPerformedTasks(user); + try { + formService.submitTaskFormData(form.getTaskId(), form.getPropertiesForSubmit()); + runtimeService.setVariable(user.getWorkflowId(), FORM_SUBMITTER, authUser); + } catch (ActivitiException e) { + throwException(e, "While submitting form for task " + form.getTaskId()); + } + + Set<String> postTasks = getPerformedTasks(user); + postTasks.removeAll(preTasks); + postTasks.add(form.getTaskId()); + + updateStatus(user); + User updated = userDAO.save(user); + + // see if there is any propagation to be done + PropagationByResource propByRes = + runtimeService.getVariable(user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class); + + // fetch - if available - the encrypted password + String clearPassword = null; + String encryptedPwd = runtimeService.getVariable(user.getWorkflowId(), ENCRYPTED_PWD, String.class); + if (StringUtils.isNotBlank(encryptedPwd)) { + clearPassword = decrypt(encryptedPwd); + } + + // supports approval chains + saveForFormSubmit(user, clearPassword, propByRes); + + UserMod userMod = runtimeService.getVariable(user.getWorkflowId(), USER_MOD, UserMod.class); + if (userMod == null) { + userMod = new UserMod(); + userMod.setKey(updated.getKey()); + userMod.setPassword(clearPassword); + } + + return new WorkflowResult<>(userMod, propByRes, postTasks); + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeEntitiesVariableType.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeEntitiesVariableType.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeEntitiesVariableType.java new file mode 100644 index 0000000..a375a1c --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeEntitiesVariableType.java @@ -0,0 +1,35 @@ +/* + * 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.workflow.activiti; + +import org.activiti.engine.impl.variable.SerializableType; +import org.apache.syncope.core.persistence.api.entity.Entity; + +/** + * Activiti variable type for handling Syncope entities as Activiti variables. + * Main purpose: avoid Activiti to handle Syncope entities as JPA entities, + * since this can cause troubles with transactions. + */ +public class SyncopeEntitiesVariableType extends SerializableType { + + @Override + public boolean isAbleToStore(final Object value) { + return value instanceof Entity; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupManager.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupManager.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupManager.java new file mode 100644 index 0000000..96f0aeb --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupManager.java @@ -0,0 +1,122 @@ +/* + * 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.workflow.activiti; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.activiti.engine.identity.Group; +import org.activiti.engine.identity.GroupQuery; +import org.activiti.engine.impl.GroupQueryImpl; +import org.activiti.engine.impl.Page; +import org.activiti.engine.impl.persistence.entity.GroupEntity; +import org.activiti.engine.impl.persistence.entity.GroupIdentityManager; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.springframework.beans.factory.annotation.Autowired; + +public class SyncopeGroupManager implements GroupIdentityManager, SyncopeSession { + + @Autowired + private UserDAO userDAO; + + @Autowired + private RoleDAO roleDAO; + + @Override + public Class<?> getType() { + return GroupIdentityManager.class; + } + + @Override + public Group createNewGroup(final String groupId) { + throw new UnsupportedOperationException(); + } + + @Override + public GroupQuery createNewGroupQuery() { + return new SyncopeGroupQueryImpl(roleDAO); + } + + @Override + public void deleteGroup(final String groupId) { + throw new UnsupportedOperationException(); + } + + @Override + public List<Group> findGroupsByUser(final String userId) { + List<Group> result = Collections.emptyList(); + User user = userDAO.find(userId); + if (user != null) { + result = new ArrayList<>(); + for (Long roleId : user.getRoleKeys()) { + result.add(new GroupEntity(roleId.toString())); + } + } + + return result; + } + + @Override + public List<Group> findGroupByQueryCriteria(final GroupQueryImpl query, final Page page) { + throw new UnsupportedOperationException(); + } + + @Override + public long findGroupCountByQueryCriteria(final GroupQueryImpl query) { + throw new UnsupportedOperationException(); + } + + @Override + public List<Group> findGroupsByNativeQuery(final Map<String, Object> parameterMap, final int firstResult, + final int maxResults) { + + throw new UnsupportedOperationException(); + } + + @Override + public long findGroupCountByNativeQuery(final Map<String, Object> parameterMap) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertGroup(final Group group) { + throw new UnsupportedOperationException(); + } + + @Override + public void updateGroup(final Group updatedGroup) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isNewGroup(final Group group) { + throw new UnsupportedOperationException(); + } + + @Override + public void flush() { + } + + @Override + public void close() { + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java new file mode 100644 index 0000000..efa2f4d --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java @@ -0,0 +1,157 @@ +/* + * 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.workflow.activiti; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.activiti.engine.ActivitiException; +import org.activiti.engine.identity.Group; +import org.activiti.engine.identity.GroupQuery; +import org.activiti.engine.impl.persistence.entity.GroupEntity; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.role.Role; + +public class SyncopeGroupQueryImpl implements GroupQuery { + + private RoleDAO roleDAO; + + private Long roleId; + + private List<Group> result; + + public SyncopeGroupQueryImpl(final RoleDAO roleDAO) { + this.roleDAO = roleDAO; + } + + @Override + public GroupQuery groupId(final String groupId) { + try { + roleId = Long.valueOf(groupId); + } catch (NumberFormatException e) { + } + + return this; + } + + @Override + public GroupQuery groupName(final String groupName) { + return this; + } + + @Override + public GroupQuery groupNameLike(final String groupNameLike) { + return this; + } + + @Override + public GroupQuery groupType(final String groupType) { + return this; + } + + @Override + public GroupQuery groupMember(final String groupMemberUserId) { + return this; + } + + @Override + public GroupQuery orderByGroupId() { + return this; + } + + @Override + public GroupQuery orderByGroupName() { + return this; + } + + @Override + public GroupQuery orderByGroupType() { + return this; + } + + @Override + public GroupQuery asc() { + return this; + } + + @Override + public GroupQuery desc() { + return this; + } + + private Group fromSyncopeRole(Role role) { + return new GroupEntity(role.getKey().toString()); + } + + private void execute() { + if (roleId != null) { + Role role = roleDAO.find(roleId); + if (role == null) { + result = Collections.emptyList(); + } else { + result = Collections.singletonList(fromSyncopeRole(role)); + } + } + if (result == null) { + result = new ArrayList<Group>(); + for (Role role : roleDAO.findAll()) { + result.add(fromSyncopeRole(role)); + } + } + } + + @Override + public long count() { + if (result == null) { + execute(); + } + return result.size(); + } + + @Override + public Group singleResult() { + if (result == null) { + execute(); + } + if (result.isEmpty()) { + throw new ActivitiException("Empty result"); + } + + return result.get(0); + } + + @Override + public List<Group> list() { + if (result == null) { + execute(); + } + return result; + } + + @Override + public List<Group> listPage(final int firstResult, final int maxResults) { + return list(); + } + + @Override + public GroupQuery potentialStarter(final String procDefId) { + throw new UnsupportedOperationException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSession.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSession.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSession.java new file mode 100644 index 0000000..a83a597 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSession.java @@ -0,0 +1,26 @@ +/* + * 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.workflow.activiti; + +import org.activiti.engine.impl.interceptor.Session; + +public interface SyncopeSession extends Session { + + Class<?> getType(); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSessionFactory.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSessionFactory.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSessionFactory.java new file mode 100644 index 0000000..8aa6e00 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeSessionFactory.java @@ -0,0 +1,45 @@ +/* + * 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.workflow.activiti; + +import org.activiti.engine.impl.interceptor.Session; +import org.activiti.engine.impl.interceptor.SessionFactory; + +public class SyncopeSessionFactory implements SessionFactory { + + private SyncopeSession syncopeSession; + + @Override + public Class<?> getSessionType() { + return syncopeSession.getType(); + } + + @Override + public Session openSession() { + return syncopeSession; + } + + public SyncopeSession getSyncopeSession() { + return syncopeSession; + } + + public void setSyncopeSession(final SyncopeSession syncopeSession) { + this.syncopeSession = syncopeSession; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserManager.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserManager.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserManager.java new file mode 100644 index 0000000..456d241 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserManager.java @@ -0,0 +1,170 @@ +/* + * 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.workflow.activiti; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.activiti.engine.identity.Group; +import org.activiti.engine.identity.Picture; +import org.activiti.engine.identity.User; +import org.activiti.engine.identity.UserQuery; +import org.activiti.engine.impl.Page; +import org.activiti.engine.impl.UserQueryImpl; +import org.activiti.engine.impl.persistence.entity.GroupEntity; +import org.activiti.engine.impl.persistence.entity.IdentityInfoEntity; +import org.activiti.engine.impl.persistence.entity.UserEntity; +import org.activiti.engine.impl.persistence.entity.UserIdentityManager; +import org.apache.syncope.core.persistence.api.dao.EntitlementDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.springframework.beans.factory.annotation.Autowired; + +public class SyncopeUserManager implements UserIdentityManager, SyncopeSession { + + @Autowired + private UserDAO userDAO; + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private EntitlementDAO entitlementDAO; + + @Override + public Class<?> getType() { + return UserIdentityManager.class; + } + + @Override + public Boolean checkPassword(final String userKey, final String password) { + throw new UnsupportedOperationException(); + } + + @Override + public User createNewUser(final String userKey) { + throw new UnsupportedOperationException(); + } + + @Override + public UserQuery createNewUserQuery() { + return new SyncopeUserQueryImpl(userDAO, roleDAO, entitlementDAO); + } + + @Override + public void deleteUser(final String userKey) { + throw new UnsupportedOperationException(); + } + + @Override + public List<Group> findGroupsByUser(final String userKey) { + List<Group> result = Collections.emptyList(); + org.apache.syncope.core.persistence.api.entity.user.User user = userDAO.find(userKey); + if (user != null) { + result = new ArrayList<>(); + for (Long roleId : user.getRoleKeys()) { + result.add(new GroupEntity(roleId.toString())); + } + } + + return result; + } + + @Override + public UserEntity findUserById(final String userKey) { + UserEntity result = null; + org.apache.syncope.core.persistence.api.entity.user.User user = userDAO.find(userKey); + if (user != null) { + result = new UserEntity(userKey); + } + + return result; + } + + @Override + public void flush() { + } + + @Override + public void close() { + } + + @Override + public void insertUser(final User user) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isNewUser(final User user) { + throw new UnsupportedOperationException(); + } + + @Override + public void updateUser(final User updatedUser) { + throw new UnsupportedOperationException(); + } + + @Override + public Picture getUserPicture(final String string) { + throw new UnsupportedOperationException(); + } + + @Override + public void setUserPicture(final String string, final Picture pctr) { + throw new UnsupportedOperationException(); + } + + @Override + public List<User> findUserByQueryCriteria(final UserQueryImpl query, final Page page) { + throw new UnsupportedOperationException(); + } + + @Override + public long findUserCountByQueryCriteria(final UserQueryImpl query) { + throw new UnsupportedOperationException(); + } + + @Override + public IdentityInfoEntity findUserInfoByUserIdAndKey(final String userKey, final String key) { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> findUserInfoKeysByUserIdAndType(final String userKey, final String type) { + throw new UnsupportedOperationException(); + } + + @Override + public List<User> findPotentialStarterUsers(final String proceDefId) { + throw new UnsupportedOperationException(); + } + + @Override + public List<User> findUsersByNativeQuery(final Map<String, Object> parameterMap, + final int firstResult, final int maxResults) { + + throw new UnsupportedOperationException(); + } + + @Override + public long findUserCountByNativeQuery(final Map<String, Object> parameterMap) { + throw new UnsupportedOperationException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java new file mode 100644 index 0000000..f51d094 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java @@ -0,0 +1,218 @@ +/* + * 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.workflow.activiti; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.activiti.engine.ActivitiException; +import org.activiti.engine.identity.User; +import org.activiti.engine.identity.UserQuery; +import org.activiti.engine.impl.persistence.entity.UserEntity; +import org.apache.syncope.core.persistence.api.RoleEntitlementUtil; +import org.apache.syncope.core.persistence.api.dao.EntitlementDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.membership.Membership; +import org.apache.syncope.core.persistence.api.entity.role.Role; + +public class SyncopeUserQueryImpl implements UserQuery { + + private UserDAO userDAO; + + private RoleDAO roleDAO; + + private EntitlementDAO entitlementDAO; + + private String username; + + private Long memberOf; + + private List<User> result; + + public SyncopeUserQueryImpl(final UserDAO userDAO, final RoleDAO roleDAO, final EntitlementDAO entitlementDAO) { + this.userDAO = userDAO; + this.roleDAO = roleDAO; + this.entitlementDAO = entitlementDAO; + } + + @Override + public UserQuery userId(final String id) { + this.username = id; + return this; + } + + @Override + public UserQuery userFirstName(final String firstName) { + return this; + } + + @Override + public UserQuery userFirstNameLike(final String firstNameLike) { + return this; + } + + @Override + public UserQuery userLastName(final String lastName) { + return this; + } + + @Override + public UserQuery userLastNameLike(final String lastNameLike) { + return this; + } + + @Override + public UserQuery userFullNameLike(final String fullNameLike) { + return this; + } + + @Override + public UserQuery userEmail(final String email) { + return this; + } + + @Override + public UserQuery userEmailLike(final String emailLike) { + return this; + } + + @Override + public UserQuery memberOfGroup(final String groupId) { + try { + memberOf = Long.valueOf(groupId); + } catch (NumberFormatException e) { + } + return this; + } + + @Override + public UserQuery orderByUserId() { + return this; + } + + @Override + public UserQuery orderByUserFirstName() { + return this; + } + + @Override + public UserQuery orderByUserLastName() { + return this; + } + + @Override + public UserQuery orderByUserEmail() { + return this; + } + + @Override + public UserQuery asc() { + return this; + } + + @Override + public UserQuery desc() { + return this; + } + + private User fromSyncopeUser(final org.apache.syncope.core.persistence.api.entity.user.User user) { + return new UserEntity(user.getUsername()); + } + + private void execute(final int page, final int itemsPerPage) { + if (username != null) { + org.apache.syncope.core.persistence.api.entity.user.User user = userDAO.find(username); + if (user == null) { + result = Collections.<User>emptyList(); + } else { + if (memberOf == null || user.getRoleKeys().contains(memberOf)) { + result = Collections.singletonList(fromSyncopeUser(user)); + } + } + } + if (memberOf != null) { + Role role = roleDAO.find(memberOf); + if (role == null) { + result = Collections.<User>emptyList(); + } else { + result = new ArrayList<>(); + List<Membership> memberships = roleDAO.findMemberships(role); + User user; + for (Membership membership : memberships) { + user = fromSyncopeUser(membership.getUser()); + if (!result.contains(user)) { + result.add(user); + } + } + } + } + // THIS CAN BE *VERY* DANGEROUS + if (result == null) { + result = new ArrayList<>(); + + List<org.apache.syncope.core.persistence.api.entity.user.User> users = + userDAO.findAll(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()), page, itemsPerPage); + for (org.apache.syncope.core.persistence.api.entity.user.User user : users) { + result.add(fromSyncopeUser(user)); + } + } + } + + @Override + public long count() { + if (result == null) { + execute(-1, -1); + } + return result.size(); + } + + @Override + public User singleResult() { + if (result == null) { + execute(-1, -1); + } + if (result.isEmpty()) { + throw new ActivitiException("Empty result"); + } + + return result.get(0); + } + + @Override + public List<User> list() { + if (result == null) { + execute(-1, -1); + } + return result; + } + + @Override + public List<User> listPage(final int firstResult, final int maxResults) { + if (result == null) { + execute((firstResult / maxResults) + 1, maxResults); + } + return result; + } + + @Override + public UserQuery potentialStarter(final String string) { + throw new UnsupportedOperationException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AbstractActivitiServiceTask.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AbstractActivitiServiceTask.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AbstractActivitiServiceTask.java new file mode 100644 index 0000000..85a0ac0 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AbstractActivitiServiceTask.java @@ -0,0 +1,48 @@ +/* + * 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.workflow.activiti.task; + +import org.activiti.engine.RuntimeService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * Abstract base class for Activiti's service tasks in Syncope, with Spring support. + */ +@Component +public abstract class AbstractActivitiServiceTask { + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(AbstractActivitiServiceTask.class); + + @Autowired + protected RuntimeService runtimeService; + + @Transactional(rollbackFor = { Throwable.class }) + public void execute(final String executionId) { + doExecute(executionId); + } + + protected abstract void doExecute(final String executionId); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AutoActivate.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AutoActivate.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AutoActivate.java new file mode 100644 index 0000000..370e6e3 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/AutoActivate.java @@ -0,0 +1,31 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.stereotype.Component; + +@Component +public class AutoActivate extends AbstractActivitiServiceTask { + + @Override + protected void doExecute(final String executionId) { + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.PROPAGATE_ENABLE, Boolean.TRUE); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Create.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Create.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Create.java new file mode 100644 index 0000000..adf36ad --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Create.java @@ -0,0 +1,51 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.syncope.common.lib.to.UserTO; +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.data.UserDataBinder; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class Create extends AbstractActivitiServiceTask { + + @Autowired + private UserDataBinder dataBinder; + + @Autowired + private EntityFactory entityFactory; + + @Override + protected void doExecute(final String executionId) { + UserTO userTO = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER_TO, UserTO.class); + Boolean storePassword = + runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.STORE_PASSWORD, Boolean.class); + // create and set workflow id + User user = entityFactory.newEntity(User.class); + dataBinder.create(user, userTO, storePassword == null ? true : storePassword); + user.setWorkflowId(executionId); + + // report user as result + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER, user); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Delete.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Delete.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Delete.java new file mode 100644 index 0000000..d386388 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Delete.java @@ -0,0 +1,40 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.stereotype.Component; + +@Component +public class Delete extends AbstractActivitiServiceTask { + + @Override + protected void doExecute(final String executionId) { + User user = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER, User.class); + + // Do something with user... + if (user != null) { + user.checkToken(""); + } + + // remove user variable + runtimeService.removeVariable(executionId, ActivitiUserWorkflowAdapter.USER); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/GenerateToken.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/GenerateToken.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/GenerateToken.java new file mode 100644 index 0000000..dcbcfd0 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/GenerateToken.java @@ -0,0 +1,43 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.syncope.core.persistence.api.dao.ConfDAO; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class GenerateToken extends AbstractActivitiServiceTask { + + @Autowired + private ConfDAO confDAO; + + @Override + protected void doExecute(final String executionId) { + User user = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER, User.class); + + user.generateToken( + confDAO.find("token.length", "256").getValues().get(0).getLongValue().intValue(), + confDAO.find("token.expireTime", "60").getValues().get(0).getLongValue().intValue()); + + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER, user); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Notify.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Notify.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Notify.java new file mode 100644 index 0000000..3c886fe --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Notify.java @@ -0,0 +1,62 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AuditElements; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.notification.NotificationManager; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * General-purpose notification task for usage within workflow. + * It requires a pre-existing <tt>Notification</tt> with category <tt>CUSTOM</tt> and result <tt>SUCCESS</tt>. + * An <tt>event</tt> workflow variable needs to be provided as well. + */ +@Component +public class Notify extends AbstractActivitiServiceTask { + + @Autowired + private NotificationManager notificationManager; + + @Override + protected void doExecute(final String executionId) { + User user = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER, User.class); + UserTO userTO = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER_TO, UserTO.class); + String event = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.EVENT, String.class); + + if (StringUtils.isNotBlank(event)) { + notificationManager.createTasks( + AuditElements.EventCategoryType.CUSTOM, + null, + null, + event, + AuditElements.Result.SUCCESS, + userTO, + null, + user.getToken()); + } else { + LOG.debug("Not sending any notification since no event was found"); + } + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/PasswordReset.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/PasswordReset.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/PasswordReset.java new file mode 100644 index 0000000..2fc8097 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/PasswordReset.java @@ -0,0 +1,44 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.workflow.api.WorkflowException; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.stereotype.Component; + +@Component +public class PasswordReset extends AbstractActivitiServiceTask { + + @Override + protected void doExecute(final String executionId) { + User user = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER, User.class); + String token = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.TOKEN, String.class); + String password = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.PASSWORD, String.class); + + if (!user.checkToken(token)) { + throw new WorkflowException(new IllegalArgumentException("Wrong token: " + token + " for " + user)); + } + + user.removeToken(); + user.setPassword(password, user.getCipherAlgorithm()); + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER, user); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Reactivate.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Reactivate.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Reactivate.java new file mode 100644 index 0000000..18102f2 --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Reactivate.java @@ -0,0 +1,29 @@ +/* + * 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.workflow.activiti.task; + +import org.springframework.stereotype.Component; + +@Component +public class Reactivate extends AbstractActivitiServiceTask { + + @Override + protected void doExecute(final String executionId) { + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Suspend.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Suspend.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Suspend.java new file mode 100644 index 0000000..903b2ee --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Suspend.java @@ -0,0 +1,29 @@ +/* + * 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.workflow.activiti.task; + +import org.springframework.stereotype.Component; + +@Component +public class Suspend extends AbstractActivitiServiceTask { + + @Override + protected void doExecute(final String executionId) { + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Update.java ---------------------------------------------------------------------- diff --git a/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Update.java b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Update.java new file mode 100644 index 0000000..7e7596d --- /dev/null +++ b/syncope620/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/task/Update.java @@ -0,0 +1,54 @@ +/* + * 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.workflow.activiti.task; + +import org.apache.commons.lang3.SerializationUtils; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.workflow.activiti.ActivitiUserWorkflowAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class Update extends AbstractActivitiServiceTask { + + @Autowired + private UserDataBinder dataBinder; + + @Override + protected void doExecute(final String executionId) { + User user = runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER, User.class); + UserMod userMod = + runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER_MOD, UserMod.class); + + // update password internally only if required + UserMod actualMod = SerializationUtils.clone(userMod); + if (actualMod.getPwdPropRequest() != null && !actualMod.getPwdPropRequest().isOnSyncope()) { + actualMod.setPassword(null); + } + // update user + PropagationByResource propByRes = dataBinder.update(user, actualMod); + + // report updated user and propagation by resource as result + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER, user); + runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.PROP_BY_RESOURCE, propByRes); + } +}
