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.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.syncope.common.lib.request.UserCR;
31 import org.apache.syncope.common.lib.request.UserUR;
32 import org.apache.syncope.common.lib.to.WorkflowTask;
33 import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
34 import org.apache.syncope.common.lib.types.ResourceOperation;
35 import org.apache.syncope.core.flowable.api.UserRequestHandler;
36 import org.apache.syncope.core.flowable.api.WorkflowTaskManager;
37 import org.apache.syncope.core.flowable.support.DomainProcessEngine;
38 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
39 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
40 import org.apache.syncope.core.persistence.api.dao.UserDAO;
41 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
42 import org.apache.syncope.core.persistence.api.entity.user.User;
43 import org.apache.syncope.core.provisioning.api.PropagationByResource;
44 import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
45 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
46 import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent;
47 import org.apache.syncope.core.provisioning.api.rules.RuleEnforcer;
48 import org.apache.syncope.core.spring.security.AuthContextUtils;
49 import org.apache.syncope.core.spring.security.SecurityProperties;
50 import org.apache.syncope.core.workflow.api.WorkflowException;
51 import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter;
52 import org.flowable.bpmn.model.FlowElement;
53 import org.flowable.bpmn.model.Gateway;
54 import org.flowable.bpmn.model.Process;
55 import org.flowable.bpmn.model.SequenceFlow;
56 import org.flowable.common.engine.api.FlowableException;
57 import org.flowable.engine.runtime.ProcessInstance;
58 import org.flowable.task.api.Task;
59 import org.identityconnectors.framework.common.objects.SyncDeltaType;
60 import org.springframework.beans.BeanUtils;
61 import org.springframework.context.ApplicationEventPublisher;
62
63 public class FlowableUserWorkflowAdapter extends AbstractUserWorkflowAdapter implements WorkflowTaskManager {
64
65 protected final DomainProcessEngine engine;
66
67 protected final UserRequestHandler userRequestHandler;
68
69 public FlowableUserWorkflowAdapter(
70 final UserDataBinder dataBinder,
71 final UserDAO userDAO,
72 final RealmDAO realmDAO,
73 final GroupDAO groupDAO,
74 final EntityFactory entityFactory,
75 final SecurityProperties securityProperties,
76 final RuleEnforcer ruleEnforcer,
77 final DomainProcessEngine engine,
78 final UserRequestHandler userRequestHandler,
79 final ApplicationEventPublisher publisher) {
80
81 super(dataBinder, userDAO, realmDAO, groupDAO, entityFactory, securityProperties, ruleEnforcer, publisher);
82 this.engine = engine;
83 this.userRequestHandler = userRequestHandler;
84 }
85
86 @Override
87 public String getPrefix() {
88 return "ACT_";
89 }
90
91 @Override
92 public <T> T getVariable(final String executionId, final String variableName, final Class<T> variableClass) {
93 return engine.getRuntimeService().getVariable(executionId, variableName, variableClass);
94 }
95
96 @Override
97 public void setVariable(final String executionId, final String variableName, final Object value) {
98 engine.getRuntimeService().setVariable(executionId, variableName, value);
99 }
100
101 protected User lazyLoad(final User user) {
102
103
104 BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
105 return user;
106 }
107
108 @Override
109 protected UserWorkflowResult<Pair<String, Boolean>> doCreate(
110 final UserCR userCR,
111 final boolean disablePwdPolicyCheck,
112 final Boolean enabled,
113 final String creator,
114 final String context) {
115
116 Map<String, Object> variables = new HashMap<>();
117 variables.put(FlowableRuntimeUtils.WF_EXECUTOR, AuthContextUtils.getUsername());
118 variables.put(FlowableRuntimeUtils.USER_CR, userCR);
119 variables.put(FlowableRuntimeUtils.ENABLED, enabled);
120
121 ProcessInstance procInst = null;
122 try {
123 procInst = engine.getRuntimeService().
124 startProcessInstanceByKey(FlowableRuntimeUtils.WF_PROCESS_ID, variables);
125 } catch (FlowableException e) {
126 FlowableRuntimeUtils.throwException(
127 e, "While starting " + FlowableRuntimeUtils.WF_PROCESS_ID + " instance");
128 }
129
130 engine.getRuntimeService().removeVariable(
131 Objects.requireNonNull(procInst).getProcessInstanceId(), FlowableRuntimeUtils.WF_EXECUTOR);
132 engine.getRuntimeService().removeVariable(
133 procInst.getProcessInstanceId(), FlowableRuntimeUtils.USER_CR);
134 engine.getRuntimeService().removeVariable(
135 procInst.getProcessInstanceId(), FlowableRuntimeUtils.USER_TO);
136
137 User user = engine.getRuntimeService().
138 getVariable(procInst.getProcessInstanceId(), FlowableRuntimeUtils.USER, User.class);
139 engine.getRuntimeService().removeVariable(
140 procInst.getProcessInstanceId(), FlowableRuntimeUtils.USER);
141
142 Boolean updatedEnabled = engine.getRuntimeService().
143 getVariable(procInst.getProcessInstanceId(), FlowableRuntimeUtils.ENABLED, Boolean.class);
144 engine.getRuntimeService().removeVariable(
145 procInst.getProcessInstanceId(), FlowableRuntimeUtils.ENABLED);
146 if (updatedEnabled != null) {
147 user.setSuspended(!updatedEnabled);
148 }
149
150 metadata(user, creator, context);
151 FlowableRuntimeUtils.updateStatus(engine, procInst.getProcessInstanceId(), user);
152 User created = userDAO.save(user);
153
154 publisher.publishEvent(
155 new EntityLifecycleEvent<>(this, SyncDeltaType.CREATE, created, AuthContextUtils.getDomain()));
156
157 engine.getRuntimeService().updateBusinessKey(
158 procInst.getProcessInstanceId(), FlowableRuntimeUtils.getWFProcBusinessKey(created.getKey()));
159
160 Boolean propagateEnable = engine.getRuntimeService().getVariable(
161 procInst.getProcessInstanceId(), FlowableRuntimeUtils.PROPAGATE_ENABLE, Boolean.class);
162 engine.getRuntimeService().removeVariable(
163 procInst.getProcessInstanceId(), FlowableRuntimeUtils.PROPAGATE_ENABLE);
164 if (propagateEnable == null) {
165 propagateEnable = enabled;
166 }
167
168 PropagationByResource<String> propByRes = new PropagationByResource<>();
169 propByRes.set(ResourceOperation.CREATE, userDAO.findAllResourceKeys(created.getKey()));
170
171 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
172 user.getLinkedAccounts().forEach(account -> propByLinkedAccount.add(
173 ResourceOperation.CREATE,
174 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
175
176 FlowableRuntimeUtils.saveForFormSubmit(
177 engine,
178 procInst.getProcessInstanceId(),
179 dataBinder.getUserTO(created, true),
180 userCR.getPassword(),
181 enabled,
182 propByRes,
183 propByLinkedAccount);
184
185 Set<String> tasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInst.getProcessInstanceId());
186
187 return new UserWorkflowResult<>(
188 Pair.of(created.getKey(), propagateEnable),
189 propByRes,
190 propByLinkedAccount,
191 tasks);
192 }
193
194 protected Set<String> doExecuteNextTask(
195 final String procInstID,
196 final User user,
197 final Map<String, Object> moreVariables) {
198
199 Set<String> preTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID);
200
201 Map<String, Object> variables = new HashMap<>();
202 variables.put(FlowableRuntimeUtils.WF_EXECUTOR, AuthContextUtils.getUsername());
203 variables.put(FlowableRuntimeUtils.USER, lazyLoad(user));
204
205 if (moreVariables != null && !moreVariables.isEmpty()) {
206 variables.putAll(moreVariables);
207 }
208
209 List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(procInstID).list();
210 String task = null;
211 if (tasks.size() == 1) {
212 try {
213 engine.getTaskService().complete(tasks.get(0).getId(), variables);
214 task = tasks.get(0).getTaskDefinitionKey();
215 } catch (FlowableException e) {
216 FlowableRuntimeUtils.throwException(
217 e, "While completing task '" + tasks.get(0).getName() + "' for " + user);
218 }
219 } else {
220 LOG.warn("Expected a single task, found {}", tasks.size());
221 }
222
223 Set<String> postTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID);
224 postTasks.removeAll(preTasks);
225 if (task != null) {
226 postTasks.add(task);
227 }
228
229 return postTasks;
230 }
231
232 @Override
233 protected UserWorkflowResult<String> doActivate(
234 final User user, final String token, final String updater, final String context) {
235
236 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
237
238 Map<String, Object> variables = new HashMap<>(2);
239 variables.put(FlowableRuntimeUtils.TOKEN, token);
240 variables.put(FlowableRuntimeUtils.TASK, "activate");
241
242 Set<String> tasks = doExecuteNextTask(procInstID, user, variables);
243
244 metadata(user, updater, context);
245 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
246 User updated = userDAO.save(user);
247
248 publisher.publishEvent(
249 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
250
251 variables.keySet().forEach(key -> engine.getRuntimeService().removeVariable(procInstID, key));
252 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
253 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
254
255 return new UserWorkflowResult<>(updated.getKey(), null, null, tasks);
256 }
257
258 @Override
259 protected UserWorkflowResult<Pair<UserUR, Boolean>> doUpdate(
260 final User user, final UserUR userUR, final String updater, final String context) {
261
262 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
263
264
265 UserUR beforeUpdate = engine.getRuntimeService().
266 getVariable(procInstID, FlowableRuntimeUtils.USER_UR, UserUR.class);
267 @SuppressWarnings("unchecked")
268 PropagationByResource<String> propByResBeforeUpdate = engine.getRuntimeService().getVariable(
269 procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
270 @SuppressWarnings("unchecked")
271 PropagationByResource<Pair<String, String>> propByLinkedAccountBeforeUpdate = engine.getRuntimeService().
272 getVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
273
274
275 boolean inFormTask = FlowableRuntimeUtils.getFormTask(engine, procInstID) != null;
276
277 Map<String, Object> variables = new HashMap<>(2);
278 variables.put(FlowableRuntimeUtils.USER_UR, userUR);
279 variables.put(FlowableRuntimeUtils.TASK, "update");
280
281 Set<String> tasks = doExecuteNextTask(procInstID, user, variables);
282
283 metadata(user, updater, context);
284 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
285 User updated = userDAO.save(user);
286
287 publisher.publishEvent(
288 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
289
290 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
291 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
292 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
293
294
295 if (inFormTask) {
296 if (beforeUpdate == null) {
297 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_UR);
298 } else {
299 engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.USER_UR, beforeUpdate);
300 }
301 }
302
303
304 inFormTask = FlowableRuntimeUtils.getFormTask(engine, procInstID) != null;
305 if (!inFormTask) {
306 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_UR);
307 }
308
309 @SuppressWarnings("unchecked")
310 PropagationByResource<String> propByRes = engine.getRuntimeService().getVariable(
311 procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
312 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
313
314 @SuppressWarnings("unchecked")
315 PropagationByResource<Pair<String, String>> propByLinkedAccount = engine.getRuntimeService().getVariable(
316 procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
317 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
318
319 FlowableRuntimeUtils.saveForFormSubmit(
320 engine,
321 procInstID,
322 dataBinder.getUserTO(updated, true),
323 userUR.getPassword() == null ? null : userUR.getPassword().getValue(),
324 null,
325 Optional.ofNullable(propByResBeforeUpdate).orElse(propByRes),
326 Optional.ofNullable(propByLinkedAccountBeforeUpdate).orElse(propByLinkedAccount));
327
328 Boolean propagateEnable = engine.getRuntimeService().getVariable(
329 procInstID, FlowableRuntimeUtils.PROPAGATE_ENABLE, Boolean.class);
330 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROPAGATE_ENABLE);
331
332 return new UserWorkflowResult<>(Pair.of(userUR, propagateEnable), propByRes, propByLinkedAccount, tasks);
333 }
334
335 @Override
336 protected UserWorkflowResult<String> doSuspend(final User user, final String updater, final String context) {
337 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
338
339 Set<String> performedTasks =
340 doExecuteNextTask(procInstID, user, Map.of(FlowableRuntimeUtils.TASK, "suspend"));
341
342 metadata(user, updater, context);
343 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
344 User updated = userDAO.save(user);
345
346 publisher.publishEvent(
347 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
348
349 @SuppressWarnings("unchecked")
350 PropagationByResource<String> propByRes = engine.getRuntimeService().getVariable(
351 procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
352 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
353
354 @SuppressWarnings("unchecked")
355 PropagationByResource<Pair<String, String>> propByLinkedAccount = engine.getRuntimeService().getVariable(
356 procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
357 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
358
359 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
360 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
361 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
362 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
363 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
364
365 return new UserWorkflowResult<>(updated.getKey(), propByRes, propByLinkedAccount, performedTasks);
366 }
367
368 @Override
369 protected UserWorkflowResult<String> doReactivate(final User user, final String updater, final String context) {
370 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
371
372 Set<String> performedTasks =
373 doExecuteNextTask(procInstID, user, Map.of(FlowableRuntimeUtils.TASK, "reactivate"));
374
375 metadata(user, updater, context);
376 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
377 User updated = userDAO.save(user);
378
379 publisher.publishEvent(
380 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
381
382 @SuppressWarnings("unchecked")
383 PropagationByResource<String> propByRes = engine.getRuntimeService().getVariable(
384 procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
385 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
386
387 @SuppressWarnings("unchecked")
388 PropagationByResource<Pair<String, String>> propByLinkedAccount = engine.getRuntimeService().getVariable(
389 procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
390 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
391
392 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
393 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
394 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
395 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
396 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
397
398 return new UserWorkflowResult<>(updated.getKey(), propByRes, propByLinkedAccount, performedTasks);
399 }
400
401 @Override
402 protected void doRequestPasswordReset(final User user, final String updater, final String context) {
403 Map<String, Object> variables = new HashMap<>(3);
404 variables.put(FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
405 variables.put(FlowableRuntimeUtils.TASK, "requestPasswordReset");
406 variables.put(FlowableRuntimeUtils.EVENT, "requestPasswordReset");
407
408 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
409
410 doExecuteNextTask(procInstID, user, variables);
411
412 metadata(user, updater, context);
413 User updated = userDAO.save(user);
414
415 publisher.publishEvent(
416 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
417
418 variables.keySet().forEach(key -> engine.getRuntimeService().removeVariable(procInstID, key));
419 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
420 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
421 }
422
423 @Override
424 protected UserWorkflowResult<Pair<UserUR, Boolean>> doConfirmPasswordReset(
425 final User user, final String token, final String password, final String updater, final String context) {
426
427 Map<String, Object> variables = new HashMap<>(5);
428 variables.put(FlowableRuntimeUtils.TOKEN, token);
429 variables.put(FlowableRuntimeUtils.PASSWORD, password);
430 variables.put(FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
431 variables.put(FlowableRuntimeUtils.TASK, "confirmPasswordReset");
432 variables.put(FlowableRuntimeUtils.EVENT, "confirmPasswordReset");
433
434 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
435
436 Set<String> tasks = doExecuteNextTask(procInstID, user, variables);
437
438 metadata(user, updater, context);
439 User updated = userDAO.save(user);
440
441 publisher.publishEvent(
442 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
443
444 variables.keySet().forEach(key -> engine.getRuntimeService().removeVariable(procInstID, key));
445 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
446 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
447
448 @SuppressWarnings("unchecked")
449 PropagationByResource<String> propByRes = engine.getRuntimeService().getVariable(
450 procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
451 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
452 @SuppressWarnings("unchecked")
453 PropagationByResource<Pair<String, String>> propByLinkedAccount = engine.getRuntimeService().getVariable(
454 procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
455 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT);
456 UserUR updatedReq = engine.getRuntimeService().getVariable(
457 procInstID, FlowableRuntimeUtils.USER_UR, UserUR.class);
458 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_UR);
459 Boolean propagateEnable = engine.getRuntimeService().getVariable(
460 procInstID, FlowableRuntimeUtils.PROPAGATE_ENABLE, Boolean.class);
461 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROPAGATE_ENABLE);
462
463 return new UserWorkflowResult<>(Pair.of(updatedReq, propagateEnable), propByRes, propByLinkedAccount, tasks);
464 }
465
466 @Override
467 protected void doDelete(final User user, final String eraser, final String context) {
468 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
469
470 doExecuteNextTask(procInstID, user, Map.of(FlowableRuntimeUtils.TASK, "delete"));
471
472 PropagationByResource<String> propByRes = new PropagationByResource<>();
473 propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(user.getKey()));
474
475 PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
476 user.getLinkedAccounts().forEach(account -> propByLinkedAccount.add(
477 ResourceOperation.DELETE,
478 Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
479
480 if (engine.getRuntimeService().createProcessInstanceQuery().
481 processInstanceId(procInstID).active().list().isEmpty()) {
482
483 userDAO.delete(user.getKey());
484
485 publisher.publishEvent(
486 new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, user, AuthContextUtils.getDomain()));
487
488 if (!engine.getHistoryService().createHistoricProcessInstanceQuery().
489 processInstanceId(procInstID).list().isEmpty()) {
490
491 engine.getHistoryService().deleteHistoricProcessInstance(procInstID);
492 }
493 } else {
494 FlowableRuntimeUtils.saveForFormSubmit(
495 engine,
496 procInstID,
497 dataBinder.getUserTO(user, true),
498 null,
499 null,
500 propByRes,
501 propByLinkedAccount);
502
503 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
504 metadata(user, eraser, context);
505 User updated = userDAO.save(user);
506
507 publisher.publishEvent(
508 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, updated, AuthContextUtils.getDomain()));
509
510 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
511 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
512 engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.WF_EXECUTOR);
513 }
514 }
515
516 @Override
517 public UserWorkflowResult<String> executeNextTask(final WorkflowTaskExecInput workflowTaskExecInput) {
518 User user = userDAO.authFind(workflowTaskExecInput.getUserKey());
519
520 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey());
521
522 Map<String, Object> variables = new HashMap<>();
523 variables.put(FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
524 variables.putAll(workflowTaskExecInput.getVariables());
525
526 Set<String> performedTasks = doExecuteNextTask(procInstID, user, variables);
527 FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
528 user = userDAO.save(user);
529
530 publisher.publishEvent(
531 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain()));
532
533 engine.getRuntimeService().setVariable(
534 procInstID, FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
535
536 if (engine.getRuntimeService().createProcessInstanceQuery().
537 processInstanceId(procInstID).active().list().isEmpty()) {
538
539 userDAO.delete(user.getKey());
540
541 publisher.publishEvent(
542 new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, user, AuthContextUtils.getDomain()));
543
544 if (!engine.getHistoryService().createHistoricProcessInstanceQuery().
545 processInstanceId(procInstID).list().isEmpty()) {
546
547 engine.getHistoryService().deleteHistoricProcessInstance(procInstID);
548 }
549 } else {
550 @SuppressWarnings("unchecked")
551 PropagationByResource<String> propByRes = engine.getRuntimeService().
552 getVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
553 @SuppressWarnings("unchecked")
554 PropagationByResource<Pair<String, String>> propByLinkedAccount = engine.getRuntimeService().getVariable(
555 procInstID, FlowableRuntimeUtils.PROP_BY_LINKEDACCOUNT, PropagationByResource.class);
556
557 FlowableRuntimeUtils.saveForFormSubmit(
558 engine,
559 procInstID,
560 dataBinder.getUserTO(user, true),
561 null,
562 null,
563 propByRes,
564 propByLinkedAccount);
565 }
566
567 return new UserWorkflowResult<>(user.getKey(), null, null, performedTasks);
568 }
569
570 protected static void navigateAvailableTasks(final FlowElement flow, final List<String> availableTasks) {
571 if (flow instanceof Gateway) {
572 ((Gateway) flow).getOutgoingFlows().forEach(subflow -> navigateAvailableTasks(subflow, availableTasks));
573 } else if (flow instanceof SequenceFlow) {
574 navigateAvailableTasks(((SequenceFlow) flow).getTargetFlowElement(), availableTasks);
575 } else if (flow instanceof org.flowable.bpmn.model.Task) {
576 availableTasks.add(flow.getId());
577 } else {
578 LOG.debug("Unexpected flow found: {}", flow);
579 }
580 }
581
582 @Override
583 public List<WorkflowTask> getAvailableTasks(final String userKey) {
584 String procInstID = FlowableRuntimeUtils.getWFProcInstID(engine, userKey);
585
586 List<String> availableTasks = new ArrayList<>();
587 try {
588 Task currentTask = engine.getTaskService().createTaskQuery().processInstanceId(procInstID).singleResult();
589
590 Process process = engine.getRepositoryService().
591 getBpmnModel(FlowableRuntimeUtils.getLatestProcDefByKey(
592 engine, FlowableRuntimeUtils.WF_PROCESS_ID).getId()).getProcesses().get(0);
593 process.getFlowElements().stream().
594 filter(SequenceFlow.class::isInstance).
595 map(SequenceFlow.class::cast).
596 filter(flow -> flow.getSourceRef().equals(currentTask.getTaskDefinitionKey())).
597 forEach(flow -> navigateAvailableTasks(flow.getTargetFlowElement(), availableTasks));
598 } catch (FlowableException e) {
599 throw new WorkflowException(
600 "While reading available tasks for workflow instance " + procInstID, e);
601 }
602
603 return availableTasks.stream().map(input -> {
604 WorkflowTask workflowTaskTO = new WorkflowTask();
605 workflowTaskTO.setName(input);
606 return workflowTaskTO;
607 }).collect(Collectors.toList());
608 }
609 }