View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.flowable.impl;
20  
21  import java.util.Base64;
22  import java.util.List;
23  import java.util.Optional;
24  import java.util.Set;
25  import java.util.stream.Collectors;
26  import org.apache.commons.lang3.tuple.Pair;
27  import org.apache.syncope.common.lib.SyncopeClientException;
28  import org.apache.syncope.common.lib.to.UserTO;
29  import org.apache.syncope.core.flowable.support.DomainProcessEngine;
30  import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
31  import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
32  import org.apache.syncope.core.persistence.api.entity.user.User;
33  import org.apache.syncope.core.provisioning.api.PropagationByResource;
34  import org.apache.syncope.core.workflow.api.WorkflowException;
35  import org.flowable.common.engine.api.FlowableException;
36  import org.flowable.engine.history.HistoricActivityInstance;
37  import org.flowable.engine.repository.ProcessDefinition;
38  import org.flowable.engine.runtime.Execution;
39  import org.flowable.engine.runtime.ProcessInstance;
40  import org.flowable.task.api.Task;
41  import org.identityconnectors.common.security.EncryptorFactory;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  public final class FlowableRuntimeUtils {
46  
47      private static final Logger LOG = LoggerFactory.getLogger(FlowableRuntimeUtils.class);
48  
49      public static final String WF_PROCESS_ID = "userWorkflow";
50  
51      public static final String USER = "user";
52  
53      public static final String WF_EXECUTOR = "wfExecutor";
54  
55      public static final String FORM_SUBMITTER = "formSubmitter";
56  
57      public static final String USER_CR = "userCR";
58  
59      public static final String USER_TO = "userTO";
60  
61      public static final String ENABLED = "enabled";
62  
63      public static final String USER_UR = "userUR";
64  
65      public static final String TASK = "task";
66  
67      public static final String TOKEN = "token";
68  
69      public static final String PASSWORD = "password";
70  
71      public static final String PROP_BY_RESOURCE = "propByResource";
72  
73      public static final String PROP_BY_LINKEDACCOUNT = "propByLinkedAccount";
74  
75      public static final String PROPAGATE_ENABLE = "propagateEnable";
76  
77      public static final String ENCRYPTED_PWD = "encryptedPwd";
78  
79      public static final String EVENT = "event";
80  
81      public static String encrypt(final String clear) {
82          byte[] encrypted = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(clear.getBytes());
83          return Base64.getEncoder().encodeToString(encrypted);
84      }
85  
86      public static String decrypt(final String crypted) {
87          byte[] decrypted = EncryptorFactory.getInstance().getDefaultEncryptor().
88                  decrypt(Base64.getDecoder().decode(crypted));
89          return new String(decrypted);
90      }
91  
92      public static String getWFProcBusinessKey(final String userKey) {
93          return FlowableRuntimeUtils.getProcBusinessKey(WF_PROCESS_ID, userKey);
94      }
95  
96      public static String getWFProcInstID(final DomainProcessEngine engine, final String userKey) {
97          ProcessInstance procInst = engine.getRuntimeService().createProcessInstanceQuery().
98                  processInstanceBusinessKey(getWFProcBusinessKey(userKey)).singleResult();
99          return Optional.ofNullable(procInst).map(Execution::getId).orElse(null);
100     }
101 
102     public static String getProcBusinessKey(final String procDefId, final String userKey) {
103         return procDefId + ':' + userKey;
104     }
105 
106     public static Pair<String, String> splitProcBusinessKey(final String procBusinessKey) {
107         String[] split = procBusinessKey.split(":");
108         if (split.length != 2) {
109             throw new WorkflowException(new IllegalArgumentException("Unexpected business key: " + procBusinessKey));
110         }
111 
112         return Pair.of(split[0], split[1]);
113     }
114 
115     public static ProcessDefinition getLatestProcDefByKey(final DomainProcessEngine engine, final String key) {
116         try {
117             return engine.getRepositoryService().createProcessDefinitionQuery().
118                     processDefinitionKey(key).latestVersion().singleResult();
119         } catch (FlowableException e) {
120             throw new WorkflowException("While accessing process " + key, e);
121         }
122     }
123 
124     public static Set<String> getPerformedTasks(final DomainProcessEngine engine, final String procInstId) {
125         return engine.getHistoryService().createHistoricActivityInstanceQuery().
126                 executionId(procInstId).
127                 list().stream().
128                 map(HistoricActivityInstance::getActivityId).
129                 collect(Collectors.toSet());
130     }
131 
132     public static void updateStatus(final DomainProcessEngine engine, final String procInstId, final User user) {
133         List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(procInstId).list();
134         if (tasks.isEmpty() || tasks.size() > 1) {
135             LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
136         } else {
137             user.setStatus(tasks.get(0).getTaskDefinitionKey());
138         }
139     }
140 
141     public static String getFormTask(final DomainProcessEngine engine, final String procInstId) {
142         String result = null;
143 
144         List<Task> tasks = engine.getTaskService().createTaskQuery().
145                 taskWithFormKey().processInstanceId(procInstId).list();
146         if (tasks.isEmpty() || tasks.size() > 1) {
147             LOG.debug("While checking if form task: unexpected task number ({})", tasks.size());
148         } else {
149             result = tasks.get(0).getFormKey();
150         }
151 
152         return result;
153     }
154 
155     /**
156      * Saves resources to be propagated and password for later - after form submission - propagation.
157      *
158      * @param engine Flowable engine
159      * @param procInstId process instance id
160      * @param userTO user transfer object
161      * @param password password
162      * @param enabled is user to be enabled or not?
163      * @param propByRes current propagation actions against resources
164      * @param propByLinkedAccount current propagation actions for linked accounts
165      */
166     public static void saveForFormSubmit(
167             final DomainProcessEngine engine,
168             final String procInstId,
169             final UserTO userTO,
170             final String password,
171             final Boolean enabled,
172             final PropagationByResource<String> propByRes,
173             final PropagationByResource<Pair<String, String>> propByLinkedAccount) {
174 
175         String formTaskId = getFormTask(engine, procInstId);
176         if (formTaskId == null) {
177             return;
178         }
179 
180         engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.USER_TO, userTO);
181 
182         if (password == null) {
183             String encryptedPwd = engine.getRuntimeService().
184                     getVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
185             if (encryptedPwd != null) {
186                 userTO.setPassword(decrypt(encryptedPwd));
187             }
188         } else {
189             userTO.setPassword(password);
190             engine.getRuntimeService().
191                     setVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD, encrypt(password));
192         }
193 
194         engine.getRuntimeService().setVariable(
195                 procInstId, FlowableRuntimeUtils.ENABLED, enabled);
196 
197         engine.getRuntimeService().setVariable(
198                 procInstId, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
199         if (propByRes != null) {
200             propByRes.clear();
201         }
202 
203         engine.getRuntimeService().setVariable(
204                 procInstId, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, propByLinkedAccount);
205         if (propByLinkedAccount != null) {
206             propByLinkedAccount.clear();
207         }
208     }
209 
210     public static void throwException(final FlowableException e, final String defaultMessage) {
211         if (e.getCause() == null) {
212             throw new WorkflowException(defaultMessage, e);
213         } else if (e.getCause() instanceof SyncopeClientException) {
214             throw (SyncopeClientException) e.getCause();
215         } else if (e.getCause() instanceof ParsingValidationException) {
216             throw (ParsingValidationException) e.getCause();
217         } else if (e.getCause() instanceof InvalidEntityException) {
218             throw (InvalidEntityException) e.getCause();
219         } else if (e.getCause().getClass().getName().contains("persistence")) {
220             throw (RuntimeException) e.getCause();
221         }
222 
223         throw new WorkflowException(defaultMessage, e);
224     }
225 
226     private FlowableRuntimeUtils() {
227         // private constructor for static utility class
228     }
229 }