1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
157
158
159
160
161
162
163
164
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
228 }
229 }